3

cloudQueue.BeginAddMessage と EndAddMessage を使用して多くのメッセージを送信しています。まだ返されていない開始の量を 500 に制限しています。それでも、コード 10048 (ソケットの枯渇を意味します) で例外が発生します。

Microsoft.WindowsAzure.Storage.StorageException: Unable to connect to the remote server ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted

レジストリを変更するようにすべてのアドバイスを検索した後に見つけた解決策ですが、これは Azure の worker ロールで計画されているため、それを行うことはできません。

テーブルサービスに挿入する他の機能があります。それらは同じように高速に動作しますが、問題はありません。EndAddMessage 関数が接続などを閉じていないようです (ソケットの理解が限られています)。

私の質問:ここで紺碧の側にバグはありますか? クロールへのメッセージの追加を人為的に遅くする以外に、これを修正するにはどうすればよいですか?

メッセージの送信に使用するテスト関数を次に示します。私の場合、約16500のメッセージが追加され、コールバックが適切に安定して終了した後、速度が低下し、しばらくすると前述の例外がスローされます。

長いコードで申し訳ありませんが、問題を再現するには、これをコピーして貼り付けてください。

から例外がスローされAsyncCallback endAddCallbackます。

    static void Main()
    {
        Console.SetBufferSize(205, Int16.MaxValue - 1);

        // Set the maximum number of concurrent connections (12*6 in my case)
        ServicePointManager.DefaultConnectionLimit = 12 * Environment.ProcessorCount;
        //setting UseNagleAlgorithm to true reduces network traffic by buffering small packets of data and transmitting them as a single packet, but setting to false can significantly reduce latencies for small packets.
        ServicePointManager.UseNagleAlgorithm = false;
        //if true, "Expect: 100-continue" header is sent to ensure a call can be made. This uses an entire roundtrip to the service point (azure), so setting to false sends the call directly.
        ServicePointManager.Expect100Continue = false;

        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(__CONN_STRING);
        CloudQueueClient client = storageAccount.CreateCloudQueueClient();
        CloudQueue queue = client.GetQueueReference(__QUEUE_NAME);
        queue.CreateIfNotExists();
        List<Guid> ids = new List<Guid>();
        for (Int32 i = 0; i < 40000; i++)
            ids.Add(Guid.NewGuid());

        SendMessages(queue, ids.Select(id => new CloudQueueMessage(id.ToString())).ToList().AsReadOnly());
    }

    public static void SendMessages(CloudQueue queue, IReadOnlyCollection<CloudQueueMessage> messages)
    {
        List<CloudQueueMessage> toSend = messages.ToList();
        Object exceptionSync = new Object();
        Exception exception = null;
        CountdownEvent cde = new CountdownEvent(toSend.Count);
        AsyncCallback endAddCallback = asyncResult =>
        {
            Int32 endedItem = (Int32)asyncResult.AsyncState;
            try
            {
                queue.EndAddMessage(asyncResult);
                Console.WriteLine("SendMessages: Ended\t\t{0}\t/{1}", endedItem + 1, toSend.Count);
            }
            catch (Exception e)
            {
                Console.WriteLine("SendMessages: Error adding {0}/{1} to queue: \n{2}", endedItem + 1, toSend.Count, e);
                lock (exceptionSync)
                {
                    if (exception == null)
                        exception = e;
                }
            }
            finally { cde.Signal(); }
        };

        for (Int32 i = 0; i < toSend.Count; i++)
        {
            lock (exceptionSync)
            {
                if (exception != null)
                    throw exception;
            }
            //if number of added but not ended is larger than the MAX, yield and check again.
            while (true)
            {
                Int32 currentOngoing = (i- (cde.InitialCount - cde.CurrentCount));
                if (currentOngoing > 500)
                    Thread.Sleep(5);
                else
                    break;
            }
            Console.WriteLine("SendMessages: Beginning\t{0}\t/{1}", i + 1, toSend.Count);
            queue.BeginAddMessage(toSend[i], endAddCallback, i);
        }

        cde.Wait();
        if (exception != null)
            throw exception;
        Console.WriteLine("SendMessages: Done.");
    }
4

3 に答える 3

2

Cloud[Blob|Table|Queue]Client は状態を維持せず、多くのオブジェクトで使用できます。

この問題は、ServicePointManager が過負荷になることに関連しています。キュー ストレスのシナリオは、多くの小さな要求 (この場合は非常に小さい GUID) を実行するため、この動作を悪化させる傾向があります。この問題を軽減するために行ういくつかの緩和策があります

  • Nagle アルゴリズムは、tcp レイヤーで小さなリクエストをまとめてバッチ処理することでこの場合に役立つように設計されています。これを true に設定すると、場合によってはメッセージごとのレイテンシがわずかに増加する可能性がありますが、ストレス下では、リクエストを待機する必要がないため、ほとんどの場合無視できます。 nagle が探しているウィンドウよりも長い (1400 バイト)
  • ServicePointManager.DefaultConnectionLimit を増やして、ソケットをすばやく開いたり閉じたりできるようにします。
  • コードが実行されている場所、およびこれが実行されている間に接続を使用している他のコードがあるかどうかについて、より多くの情報を提供できますか。既定では、クライアント リクエストは keep alive = true を送信します。これにより、Azure サービスへの永続的な接続が維持され、複数のリクエストで同じソケットを開いたり閉じたり再接続したりする必要がなくなります。

また、テーブル エンティティが同じ動作を示さないというコメントに関しては、テーブル サービスがサポートする現在のワイヤ プロトコルは Atom/Pub であり、かなりおしゃべり (xml など) になる可能性があります。したがって、単純なエンティティの挿入は、単純なキュー GUID メッセージよりもはるかに大きくなります。基本的に、サイズの違いにより、テーブル トラフィックはその下の TCP レイヤーを利用してより適切に機能しているため、これは真のリンゴとリンゴの比較ではありません。

これらの解決策がうまくいかない場合は、バックエンドで確認できるように、アカウントに関するいくつかの情報を入手してください。

ジョー

于 2013-03-01T17:59:39.417 に答える
0

あなたがやっているように、CloudQueueClientがマルチスレッド(非同期)アクセスを意図していないためだと思います。

SendMessagesこのように CloudQueue を再作成してみてください

    CloudQueueClient client = storageAccount.CreateCloudQueueClient();
    CloudQueue queue = client.GetQueueReference(__QUEUE_NAME);

CloudXXClient は一度使用して破棄することを意図しているという多くのフォーラムを読みました。そのプリンシパルがここに適用される可能性があります。

クライアントの ctor はキューにリクエストを送信せず、スレッド化の問題があるため、得られる効率はあまりありません。

于 2013-02-26T14:30:54.503 に答える