【问题标题】:why is my WCF server so slow [closed]为什么我的 WCF 服务器这么慢 [关闭]
【发布时间】:2013-03-02 14:40:24
【问题描述】:

我创建了一个 WCF 服务器和一个 WCF 客户端来使用该服务。 服务器的目的是将传入的 2 个数字相加并等待 X 毫秒,然后返回总和。

客户端创建 Y 个任务并启动它们。每个任务都是服务器添加数字并等待 X 毫秒的请求。

当 x = 0 和 y = 1000 时,完成所有任务平均需要 6.2 秒。 当 X = 0 和 Y = 10000 时,完成所有任务平均需要 61 秒。

为什么这么慢或者这很正常?

谢谢 达摩

客户端 C# 方法

private void radButtonTaskWithStatus_Click(object sender, EventArgs e)
        {
            try
            {
                var dateTime1 = DateTime.UtcNow;
                radProgressBarStatus.Maximum = int.Parse(radTextBoxFloodRequests.Text);
                radProgressBarStatus.Value1 = 0;

                Random rnd = new Random();


                Task<int>[] tasks = new Task<int>[int.Parse(radTextBoxFloodRequests.Text)];

                for (int i = 0; i < int.Parse(radTextBoxFloodRequests.Text); i++)
                {
                    int x = i;
                    tasks[i] = new Task<int>(() =>
                    {    

                        int FirstRandomNumber = rnd.Next(1, 20);
                        int SecondRandomNumber = rnd.Next(1, 20);


                        int result = TaskRequestWithResult(FirstRandomNumber, SecondRandomNumber, int.Parse(radTextBoxFloodDelay.Text), x);    

                        return result;
                    });
                }

                var continuation = Task.Factory.ContinueWhenAll(
                            tasks,
                            (antecedents) =>
                            {
                                var dateTime2 = DateTime.UtcNow;
                                var diffInSeconds = (dateTime2 - dateTime1).TotalSeconds;
                                this.radListView1.BeginInvoke((MethodInvoker)(() => this.radListView1.Items.Add((dateTime2 - dateTime1).TotalSeconds)));
                                //MessageBox.Show(diffInSeconds.ToString());


                                int total = 0;
                                for (int i = 0; i < int.Parse(radTextBoxFloodRequests.Text); i++)
                                    total = total + tasks[i].Result;
                                Debug.Print("Finished - Sum of all results is: " + total);
                                //RadMessageBox.Show("Finished - Sum of all results is: " + total);                     
                            });


                for (int i = 0; i < int.Parse(radTextBoxFloodRequests.Text); i++)
                {
                    tasks[i].Start();
                  //  System.Threading.Thread.Sleep(10); // Wait
                }

                TaskProgress(tasks, count => Invoke(new MethodInvoker(() => radProgressBarStatus.Value1 = count)));

                // Use next line if you want to block the main thread until all the tasks are complete
                //continuation.Wait();


            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.Message.ToString());

            }
        }

        public static void TaskProgress(IEnumerable<Task> tasks, Action<int> callback)
        {
            int count = 0;
            foreach (var task in tasks)
                task.ContinueWith(t => callback(Interlocked.Increment(ref count)));
        }

        private int TaskRequestWithResult(int number1, int number2, int delay, int count)
        {

            try
            {

                ServiceReference1.WCFJobsLibraryClient Client = new ServiceReference1.WCFJobsLibraryClient();
                Client.Endpoint.Address = new System.ServiceModel.EndpointAddress(radTextBoxbaseAddress.Text);
                WCFClient.ServiceReference1.ReturnClass AddNumbers_Result;

                AddNumbers_Result = Client.AddNumbers(number1, number2, delay);


                if (AddNumbers_Result.ErrorCode < 0)
                {
                    // If exception happens, it will be returned here
                    MessageBox.Show(AddNumbers_Result.ErrorCode.ToString() + " " + AddNumbers_Result.ErrorMessage + " " + AddNumbers_Result.ExMessage);
                }

                else
                {
                    Debug.Print("Task Executing now: " + count.ToString());                   

                }

                return AddNumbers_Result.Result;

            }

            catch (Exception ex)
            {                
                MessageBox.Show(ex.Message.ToString());
                return -1;

            }
        }


    }

Client App.config

<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false"
                    bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8732/Design_Time_Addresses/WCFService/WCFJobsLibrary/"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpEndpoint"
                contract="ServiceReference1.IWCFJobsLibrary" name="BasicHttpEndpoint" />
        </client>
    </system.serviceModel>
</configuration>

服务器 C# 方法

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WCFJobsLibrary : IWCFJobsLibrary
{

    public ReturnClass AddNumbers(int FirstNumber, int SecondNumber, int Delay) //Add two numbers and wait a predefined interval
    {
        Logging.Write_To_Log_File("Entry", MethodBase.GetCurrentMethod().Name, "", "", "", 1);
        ReturnClass myReturnClass = new ReturnClass(-1, null, null, 0);
        try
        {             

            myReturnClass.ErrorCode = 1;

            myReturnClass.Result = FirstNumber + SecondNumber;

            System.Threading.Thread.Sleep(Delay); // Wait
            Logging.Write_To_Log_File("Exit", MethodBase.GetCurrentMethod().Name, "", "", "", 1);
            return myReturnClass;

        }
        catch (Exception ex)
        {
            Logging.Write_To_Log_File("Error", MethodBase.GetCurrentMethod().Name, "", "", ex.ToString(), 2);
            myReturnClass.ErrorCode = -1;
            myReturnClass.ErrorMessage = ex.ToString();
            return myReturnClass;
        }

    }

//Add two numbers and wait defined interval
[OperationContract]
ReturnClass AddNumbers(int FirstNumber, int SecondNumber, int Delay);

服务器 App.config

<configuration>
  <system.serviceModel>
  <services>
    <service name="WCFService.WCFJobsLibrary" behaviorConfiguration="Throttled" >
      <endpoint address="" binding="basicHttpBinding" bindingConfiguration=""
        name="BasicHttpEndpoint" contract="WCFService.IWCFJobsLibrary">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8732/Design_Time_Addresses/WCFService/WCFJobsLibrary/" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="Throttled">
        <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
        <serviceMetadata httpGetEnabled="true" />
        <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
        <serviceDebug includeExceptionDetailInFaults="true" />
        <serviceThrottling maxConcurrentCalls="100000" maxConcurrentInstances="100000" maxConcurrentSessions="100000" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  </system.serviceModel>
</configuration>

【问题讨论】:

  • 你基于时间的表现很好,它是线性的......
  • 我不禁注意到,您的 2 个 Y 值相差 10 倍,您的响应时间相差 10 倍。您确定 Y 不是用于您的等待计算?
  • 它是 WCF ......它的性能很慢,所以你不得不聘请顾问来为你修复它:D
  • 你在服务器端睡觉!为什么你期望它很慢?
  • 我的睡眠值为 x 和 x = 0

标签: c# performance wcf task wcf-binding


【解决方案1】:

拖慢你速度的不是 WCF,而是你的架构。

例如,您正在创建 1k 个任务,这些任务将尝试从 ThreadPool 中获取线程,每个任务仅在从您的 WCF 服务获得响应时完成(它在每个任务的基础上同步调用您的 WCF 服务)。

现在,您可能认为您将同时启动 1k 或 10k 个作业,但实际上,.NET 的线程池将从少量线程开始,fe。 4 并在需要时随着时间的推移增加它们的数量,这可能需要一些时间。

您可以通过增加线程池应分配的最小线程数来快速检查这是否是瓶颈:

int min, io;
ThreadPool.GetMinThreads(out min, out io);
ThreadPool.SetMinThreads(1000, io);

这只是如何规避您的问题,而不是解决它,您的架构应该不同,将您的工作分散在 2-4 个任务之间,而不是 10k!

此外,您似乎正在为每个请求创建一个新客户端,基本上,您正在做很多管道工作,分配大量资源(#task!= #threads,但这些仍然相关) ,对于一个非常简单的任务,因此您实际上衡量的不是 WCF 工作,而是您的工作。

【讨论】:

  • 我怎样才能“将你的工作分散在 2-4 个任务之间,而不是 10k 之间” - 如果我这样做了,它会执行得更快吗?
  • 您可以创建 N 个任务,并且在每个任务中,在正常的 for 循环中,您将生成 x / N 个请求,因此您总共可以有 x / N * N = x 个请求,但是您将只创建 N 个客户端并为此任务分配不超过 N 个线程。但是,如果您的服务器长时间阻塞,这将不起作用,因为您一次只能安排 N 个任务。解决此问题的最佳方法是使用异步 WCF 服务,因此您可以只安排来自 1 个线程的 x 个请求,而无需等待它们完成并仅使用线程池线程来接收结果。
  • +1 :) 对于伟大的回答
最近更新 更多