web-dev-qa-db-ja.com

Azure Function AppでのAzure Storage Queue処理の調整

各キューアイテムがURLであるキューを処理するAzure Storage QueueトリガーでAzure Functionアプリを作成しました。関数はURLのコンテンツをダウンロードするだけです。サイトのXMLサイトマップを読み込んで解析し、すべてのページのURLをキューに追加する別の関数があります。私が抱えている問題は、Functionsアプリの実行が速すぎて、Webサイトをハンマーしてサーバーエラーを返し始めることです。関数アプリの実行速度を制限/スロットルする方法はありますか?

もちろん、それらをシリアルに処理する(または非同期で、同時要求の数を制限する)単純なWebジョブを作成することもできますが、Azure Functionsのシンプルさが本当に好きで、「サーバーレス」コンピューティングを試してみたかったのです。

24
Alex

検討できるオプションがいくつかあります。

最初に、キューの処理を制御するHost.jsonで構成できるいくつかのノブがあります(文書化された here )。 queues.batchSizeノブは、一度にフェッチされるキューメッセージの数です。 1に設定すると、ランタイムは一度に1つのメッセージをフェッチし、そのメッセージの処理が完了したときにのみ次のメッセージをフェッチします。これにより、単一インスタンスである程度のシリアル化が可能になります。

別のオプションとして、エンキューするメッセージにNextVisibleTimeを設定して、間隔を空ける方法があります。デフォルトでは、エンキューされるメッセージは表示され、すぐに処理する準備ができています。

最後のオプションは、一度に1つではなく、サイトのすべてのURLのコレクションでメッセージをエンキューすることです。そのため、メッセージが処理されるときに、関数でURLをシリアルに処理し、並列処理を制限できます。そのように。

32
mathewc

NextVisibleTimeいくつかの並列関数がキューに追加されている場合、混乱する可能性があります。この問題を抱えている人のためのもう1つの簡単なオプション:別のキュー「throttled-items」を作成し、元の関数がキュートリガーにそれに従うようにします。次に、元のキューから毎分メッセージを移動する単純なタイマー関数を追加し、それに応じてNextVisibleTimeの間隔を空けます。

    [FunctionName("ThrottleQueueItems")]
    public static async Task Run([TimerTrigger("0 * * * * *")] TimerInfo timer, ILogger logger)
    {
        var originalQueue = // get original queue here;
        var throttledQueue = // get throttled queue here;
        var itemsPerMinute = 60; // get from app settings
        var individualDelay = 60.0 / itemsPerMinute;
        var totalRetrieved = 0;
        var maxItemsInBatch = 32; // change if you modify the default queue config
        do
        {
            var pending = (await originalQueue.GetMessagesAsync(Math.Min(maxItemsInBatch, itemsPerMinute - totalRetrieved))).ToArray();
            if (!pending.Any())
                break;
            foreach (var message in pending)
            {
                await throttledQueue.AddMessageAsync(new CloudQueueMessage(message.AsString), null,
                                                                                        TimeSpan.FromSeconds(individualDelay * ++totalRetrieved), null, null);
                await originalQueue.DeleteMessageAsync(message);
            }
        } while (itemsPerMinute > totalRetrieved);
    }
6
vladhorby

同様の問題を解決しようとしたときにこの投稿を見つけました。これは、ここに到着したすべての人に役立つ場合があります。 WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUTアプリ設定を使用して、関数の同時インスタンス数を制限できるようになりました。これを1に設定してバッチ制限1と組み合わせると、キューのシリアル処理を実行できます。

WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT

関数アプリがスケールアウトできるインスタンスの最大数。デフォルトは無制限です。

https://docs.Microsoft.com/en-gb/Azure/azure-functions/functions-app-settings#website_max_dynamic_application_scale_out

0
jondow