【问题标题】:Optimizing Windows Service for sending HTTP requests优化 Windows 服务以发送 HTTP 请求
【发布时间】:2012-12-12 21:34:03
【问题描述】:

我正在编写的应用程序发送存储在数据库中的 SMS 消息。对于这一部分,我正在编写一个 Windows 服务,它将通过选择的数据库,然后将等待消息作为 HTTP 请求发送。

因为应用程序正在发送短信,所以速度很重要。现在,我每秒只收到大约 15 个请求。现在,应用程序创建SMSMessages,然后将它们放入同步队列中。我使用多线程从该队列中一次运行 20 个线程。

我注意到,如果我运行了太多线程,那么应用程序实际上会减慢每秒发送的消息数量。

我想弄清楚是否有比我发送请求更快的方法。有没有更好的方法来组织我的线程,或者我应该使用线程池或异步请求来优化应用程序?

主要代码在这里:

            Queue Messages = new Queue();
            DataRow[] Rows = dtSMSCombined.Select(); //Created from a datatable
            foreach (DataRow Row in Rows)
            {
                ... //Get information from the row.

                SMSMessage oSMS = new SMSMessage(Keyword, Number, Message, MessageID);
                Messages.Enqueue(oSMS);
            }

            Queue SyncedMessages = Queue.Synchronized(Messages);
            var tasks = new Task[20];

            for (int i = 0; i < 20; i++)
            {
                tasks[i] = Task.Factory.StartNew(() =>
                    { //each thread will pull out new items from the queue as they finish
                        while (SyncedMessages.Count > 0)
                        {
                            Response = new XDocument();
                            SMSMessage oSMS = (SMSMessage)SyncedMessages.Dequeue();

                            if (oSMS.GetMessage() != null && oSMS.GetMessage() != string.Empty)
                            {
                                Response = oSMS.SendSMS();
                            }
                            string ResponseCode = (string)Response.Descendants("response").First();
                            if (ResponseCode == "ok")
                            {
                                oSMS.sResponseCode = ResponseCode;
                                oSMS.dCompleted = DateTime.Now;
                            }
                            else { }

                            oSMS.DTInsert();
                        }
                    });
            }

            while (tasks.Any(t => !t.IsCompleted)) { }

这是来自SMSMessage 类的SendSMS() 方法:

    public XDocument SendSMS()
    {
        XML = "<message id=\""+ lMessageID +"\"><partnerpassword>" + PartnerPassword + "</partnerpassword><content>" + sMessage + "</content></message>";
        URL = "http://sloocetech.net:****/spi-war/spi/" + PartnerID + "/" + sRecipient + "/" + Keyword + "/messages/mt";
        HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
        Request.Proxy = null;

        RequestBytes = System.Text.Encoding.ASCII.GetBytes(XML);
        Request.Method = "POST";
        Request.ContentType = "text/xml;charset=utf-8";
        Request.ContentLength = RequestBytes.Length;
        RequestStream = Request.GetRequestStream();
        RequestStream.Write(RequestBytes, 0, RequestBytes.Length);
        RequestStream.Close();

        HttpWebResponse Resp = (HttpWebResponse)Request.GetResponse();
        oReader = new StreamReader(Resp.GetResponseStream(), System.Text.Encoding.Default);
        string backstr = oReader.ReadToEnd();

        oReader.Close();
        Resp.Close();

        Doc = XDocument.Parse(backstr);
        return Doc;
    } 

【问题讨论】:

  • 难道API不能让你发送大量消息吗?
  • 有群发方式吗?他们的 API 中没有列出一个。
  • 这种忙碌的等待是不必要的 (while (tasks.Any(t =&gt; !t.IsCompleted)) { })。你可以使用Task.WaitAll

标签: c# multithreading optimization windows-services threadpool


【解决方案1】:

如果你重复使用

HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);

对象?

连接参数在调用之间不会改变。您认为将其设为静态字段是否是个好主意?

[编辑]

例如,您将Request 定义为静态字段:

static HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("http://www.google.com");

或者,甚至是`请求对象的字典:

static Dictionary<string,HttpWebRequest> Requests = new Dictionary<string,HttpWebRequest>();

然后,在您的 SendSMS() 方法中:

public XDocument SendSMS()
{
    XML = "<message id=\"" + lMessageID + "\"><partnerpassword>" + PartnerPassword + "</partnerpassword><content>" + sMessage + "</content></message>";
    URL = "http://sloocetech.net:****/spi-war/spi/" + PartnerID + "/" + sRecipient + "/" + Keyword + "/messages/mt";

    //check if the request object exists
    if (!Requests.Keys.Contains(sRecipient))
        Requests.Add((HttpWebRequest)WebRequest.Create(URL));

    //get the existing request from the dictionary
    Requests = Requests[sRecipient];

    //configure the request
    Request.Proxy = null;
    RequestBytes = System.Text.Encoding.ASCII.GetBytes(XML);
    Request.Method = "POST";
    Request.ContentType = "text/xml;charset=utf-8";
    Request.ContentLength = RequestBytes.Length;
    RequestStream = Request.
    RequestStream.Write(RequestBytes, 0, RequestBytes.Length);
    RequestStream.Close();

    using (System.IO.Stream RequestStream = Request.GetRequestStream())
    {
        using (WebResponse response = Request.GetResponse())
        {
            using (oReader = new StreamReader(Resp.GetResponseStream(), System.Text.Encoding.Default))
            {
                string backstr = oReader.ReadToEnd();
                Doc = XDocument.Parse(backstr);
            }
        }
    }

    return Doc;
}

[编辑]

也许您还应该使用以下静态字段玩一点:

System.Net.ServicePointManager.DefaultConnectionLimit = 20;

【讨论】:

  • 每次创建新连接的部分原因在于,这将在一系列具有潜在不同关键字和不同收件人的数据库中循环。创建连接字典有什么好处?
  • 在这种情况下,System.Net.ServicePointManager.DefaultConnectionLimit = 20 解决了这个问题。我重写了其余部分,但这解决了一个主要瓶颈。
【解决方案2】:

你正在做的那种忙着等待真的是在消耗大量的 CPU。

while (tasks.Any(t => !t.IsCompleted)) { }

实际上,我完全不明白您的代码是如何在没有异常的情况下运行的,因为您检查了计数,然后是 deque。但是多个线程可能会发现计数为 1,并且都尝试出队。除了其中一个之外,其他所有人都会失败。

我认为您现在应该开始学习多线程的基础知识,然后再继续。任何具体的建议都可能无济于事,因为代码中充满了错误(除了我提到的两个之外,还有其他错误)。尝试找到一个关于“fork join parallelism with .net”的好教程,并找出 TPL 提供了哪些功能。

了解多个线程如何安全地协作并且不践踏彼此的数据非常重要。

【讨论】:

  • 谢谢。这就是我所要求的,我不熟悉线程。
【解决方案3】:

而不是使用Queue,例如使用ConcurrentQueue - 有关详细信息,请参阅MSDN。它是一个线程安全的实现,并且大部分是无锁的,因此非常快......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多