发送许多消息时出现SocketException(代码10048)

时间:2013-02-25 15:50:04

标签: c# wcf sockets azure tcp

我正在使用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.");
    }

3 个答案:

答案 0 :(得分:2)

Cloud [Blob | Table | Queue]客户端不维护状态,可以在多个对象中使用。

此问题与ServicePointManager变得过载有关。队列压力场景往往会加剧这种行为,因为它们执行许多小请求(在你的情况下是一个非常小的guid)。您可以采取一些缓解此问题的缓解措施

  • Nagle算法旨在帮助在这种情况下通过在tcp层将小请求批处理,在某些情况下将其设置为true可能会略微增加每个消息延迟,但在压力下,这很可能会因为请求不会忽略不计需要等待很长时间才能大于nagle正在寻找的窗口(1400字节)
  • 增加ServicePointManager.DefaultConnectionLimit以考虑快速打开和关闭套接字。
  • 您是否可以提供有关代码运行位置的更多信息,以及在运行时是否有任何其他代码使用连接。默认情况下,客户端请求发送keep alive = true,这应保持与Azure服务的持久连接,并允许多个请求使用相同的套接字,而无需打开/关闭/重新连接。

另外,关于表示实体没有显示相同行为的注释,表服务支持的当前有线协议是Atom / Pub,它可能非常繁琐(xml等)。因此,简单的实体插入比简单的队列guid消息大得多。基本上由于大小差异,表流量利用其下面的TCP层做得更好,所以这不是真正的苹果对比。

如果这些解决方案对您不起作用,那么获取有关您帐户的更多信息会很有帮助,以便我们在后端查看。

答案 1 :(得分:0)

我怀疑是因为CloudQueueClient并不适合您进行多线程(异步)访问。

尝试在此{/ 1}}中重新创建CloudQueue

SendMessages

我在很多论坛上都看到CloudXXClient只能使用一次并处理掉。该校长可能适用于此。

没有太多的效率,因为客户端的ctor没有向队列发送请求并且存在线程问题。

答案 2 :(得分:0)

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

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