4

对于实验,我在 VS2012 上使用 .NET 4.5 创建了一个简单的“Hello World”WCF 服务和客户端。服务器托管在控制台应用程序上并使用 net.tcp 绑定。我编写了客户端代码来向服务器发送大量异步请求(总共 700 个请求)。一切都很顺利,直到我打开服务的可靠会话功能。开启后,服务突然跑得很慢,在我的机器上花了将近一分钟才完成 700 个请求。我尝试微调 Concurrency 和 Throttling 参数(见下文),但没有帮助。

有谁知道为什么会这样?有没有办法避免这种情况?

如果我关闭了 Reliable session 功能,或者我使服务调用同步,则不会发生缓慢。所以我认为这可能与 WCF 在 WS-ReliableMessaging 模式下处理挂起请求的方式有关。

编辑:当我将 netTcpBinding 更改为 wsHttpBinding 时,这也没有发生。这很奇怪,因为在这种情况下 wsHttpBinding 比 netTcpBinding 快得多。

编辑:在服务器端运行 Perfmon.exe 表明在上述情况下,“线程数”逐渐从 8 增加到 100 以上。

编辑:我的电脑(本地网络)上的一些测量吞吐量。看到案例 1 的性能非常缓慢并且几乎没有用处。

  1. 异步 + NetTcpBinding/可靠吞吐量 -> 大约。14 次呼叫/秒(70 毫秒/次呼叫)
  2. Async + WsHttp/Reliable 吞吐量 -> 7957 call/s (0.12 ms/call)
  3. Sync + NetTcpBinding/Reliablethroughtput -> 3986 call/s (0.25 ms/call)

下面是我在实验中使用的服务器和客户端的代码和配置。

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ServiceModel;
using System.ServiceModel.Description;

[ServiceContract]
public interface IHelloService
{
    [OperationContract(IsOneWay=false)]
    string SayHello(string name);
}

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, InstanceContextMode=InstanceContextMode.PerSession)]
public class HelloService : IHelloService
{
    public string SayHello(string name) {
        String s = string.Format("Hello {0}", name); 
        return s; 
    }
}

namespace WcfServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri("net.tcp://localhost:8080/hello");
            using (ServiceHost host = new ServiceHost(typeof(HelloService), baseAddress)){
                // Open and listen
                host.Open();
                Console.WriteLine("The service is ready at {0}", baseAddress);
                Console.WriteLine("Press <Enter> to stop the service.");
                Console.ReadLine();
                // Close the ServiceHost.
                host.Close();
            }
        }
    }
}

客户:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ServiceModel;
using WcfClient.WcfServer;

namespace WcfClient
{
    class Program
    {
        static async Task PrintNameAsync(HelloServiceClient client, int cnt) {
            string s = await client.SayHelloAsync(string.Format("-- {0} --", cnt));
            Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            HelloServiceClient client = new HelloServiceClient("HelloService", "net.tcp://10.20.61.13:8080/hello");
            List<Task> tasks = new List<Task>();
            for(int i=0; i < 700; i++){
                Task t = PrintNameAsync(client, i);
                tasks.Add(t);
            }
            Task.WhenAll(tasks).Wait();
            client.Close();
        }
    }
}

服务器的 App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="HelloServiceBinding">
                    <reliableSession ordered="true" enabled="true" />
                    <security mode="None" />
                </binding>
            </netTcpBinding>
        </bindings>
        <behaviors>
            <serviceBehaviors>
                <behavior name="HelloServiceBehavior">
                    <serviceMetadata policyVersion="Policy15" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000"
                        maxConcurrentInstances="1000" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="HelloServiceBehavior" name="HelloService">
                <endpoint address="net.tcp://localhost:8080/hello" binding="netTcpBinding"
                    bindingConfiguration="HelloServiceBinding" name="HelloService" contract="IHelloService" />
                <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
            </service>
        </services>
    </system.serviceModel>
</configuration>

客户端的 App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="HelloServiceBinding" sendTimeout="00:01:00">
                    <reliableSession enabled="true" />
                    <security mode="None" />
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:8080/hello" binding="netTcpBinding"
                bindingConfiguration="HelloServiceBinding" contract="WcfServer.IHelloService"
                name="HelloService">
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>
4

3 回答 3

2

从以下链接中找到了该问题的部分解决方法:

使用解决方法(使用 WorkerThreadPoolBehavior),测得的吞吐量如下:

  1. Async + NetTcpBinding/Reliable 吞吐量 -> 474 call/s (2.1 ms/call) ... 改进但不令人满意
  2. Async + WsHttp/Reliable 吞吐量 -> 7856 call/s (0.13 ms/call) ... 没有变化
  3. Sync + NetTcpBinding/Reliablethroughtput -> 2110 call/s 0.47 ms/call) ... 降级

请注意,上述案例 1 比 70 毫秒/调用显着提高。但是,它仍然落后于案例 2。对于案例 3,引入 WorkerThreadPool 行为会导致性能从 0.25 毫秒/调用下降到 0.47 毫秒/调用。

于 2012-10-30T03:03:59.417 回答
2

看这个...

"设置 MaxTransferWindowSize

Windows Communication Foundation (WCF) 中的可靠会话使用传输窗口来保存客户端和服务上的消息。可配置属性MaxTransferWindowSize指示传输窗口可以容纳多少条消息。

在发送方,这表明传输窗口在等待确认时可以保存多少条消息;在接收器上,它指示要为服务缓冲多少消息……”

来源“MSDN:可靠会话的最佳实践”:http: //msdn.microsoft.com/en-us/library/ms733795.aspx

于 2013-07-19T15:23:46.830 回答
1

您正在异步发送消息,但您已在 ReliableSessionBindingElement 上订购了="true"。这没有意义。将ordered 设置为false,因为它对您的场景更有意义。ReliableMessaging 会导致性能下降,因为它会在每个响应消息中添加一个 SequenceAcknowledgement。它还增加了 CreateSequence/CreateSequenceResponse 和 CloseSequence/CloseSequenceResponse 的开销,然后在会话开始和结束时进行 TerminateSequence/TerminateSequenceResponse 消息交换。

于 2012-10-23T16:45:36.997 回答