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 中的工作角色中计划的,所以我不能这样做。

我还有其他插入表服务的功能,它们运行速度一样快,但没有任何问题。似乎 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 回答
0

现在已在 Storage Client Library 2.0.5.1 中解决了这个问题。

或者,还有一种解决方法:卸载 KB2750149。

于 2013-04-10T11:16:18.437 回答