WCF - 最快的进程间通信

9
我有一个通过basicHttpBinding方式可以访问的Web服务,我还希望以尽可能高的性能从同一台机器上的其他.NET服务中访问该服务。我知道netNamedPipeBinding非常适合这个目的,但是想知道在仅与其他.NET进程通信时,最佳的配置是什么。
例如,我不必使用像SOAP这样的编码,因为它可能太笨重了,而且我不需要与除.NET客户端之外的任何其他客户端兼容。我也认为我不需要任何安全性。
对于这个目的,最好的绑定配置是什么(或者说有哪些其他配置)?
3个回答

9

正如您所注意到的,NetNamedPipeBinding 绑定是针对同一台计算机通信进行优化的:

提供了一个安全可靠的绑定, 针对同一计算机通信进行了优化。

参考文献: System-Provided Bindings

Juval Lowy 的书籍 "Programming WCF Services" 第一章中提供了一个有用的决策活动图,以选择正确的绑定:

首先,您应该问自己的问题是,您的服务是否需要与非WCF客户端交互。如果答案是肯定的,并且客户端是遗留的MSMQ客户端,则选择MsmqIntegrationBinding,使您的服务能够通过MSMQ与此类客户端进行交互。如果您需要与非WCF客户端进行交互,并且该客户端期望基本的Web服务协议(ASMX Web服务),则选择BasicHttpBinding,它将您的WCF服务暴露给外部世界,就像它是ASMX Web服务一样(即WSI-basic配置文件)。缺点是您无法利用大多数现代WS-*协议。但是,如果非WCF客户端可以理解这些标准,则选择其中一个WS绑定,例如WSHttpBinding、WSFederationHttpBinding或WSDualHttpBinding。如果您可以假设客户端是WCF客户端,但它需要离线或断开连接的交互,请选择使用MSMQ来传输消息的NetMsmqBinding。如果客户端需要连接通信,但可能跨机器边界调用,请选择使用TCP进行通信的NetTcpBinding。如果客户端与服务位于同一台计算机上,请选择使用命名管道来最大化性能的NetNamedPipeBinding。您可以根据其他条件调整绑定选择,例如需要回调(WSDualHttpBinding)或联合安全性(WSFederationHttpBinding)。

谢谢您的回复,但我正在寻求“微调”绑定以提高效率。例如,我认为默认情况下启用了安全性 - 我可以禁用它并节省一些开销。我假设使用的是SOAP编码,但也许二进制会加速事情。甚至可能使用更快的自定义编码?任何关于优化绑定的建议将不胜感激。 - Barguast
1
你似乎在说,在你确定需要的情况下,“微调”绑定。你能解释一下为什么你需要这种性能,以及你已经做了什么来确定这个要求吗? - Mitch Wheat
2
我已经测试了性能,发现使用HTTP、命名管道进行1,000次操作,并在同一应用程序上执行所需的时间:服务(HTTP):5192毫秒,服务(命名管道):4646毫秒,应用程序:2971毫秒。我可以接受它会更慢,但我期望NamedPipe绑定速度与应用程序速度(本地运行相同操作)更接近,而不是HTTP速度。 - Barguast
3
为什么还需要序列化?如果想要进行调整,请使用PROTOBUF作为序列化协议,比起微软的XML / Binary更快。可以通过谷歌下载可用的源代码。 - TomTom

4

当然,命名管道传输是最好的选择。

使用EncryptAndSign的传输安全在标准的NetNamedPipeBinding上默认启用。您肯定希望将其删除,因为这样做将加快速度,而不会对安全造成任何实际影响,原因如我在这里讨论

我还怀疑,但尚未确认,更改消息编码绑定元素可能会有所帮助。这是因为默认值是WCF专有的“带内字典的二进制编码”,它是XML信息集的编码,旨在减少冗余字节,例如在打开和关闭元素标记中:当涉及网络IO时,这是一个值得追求的目标,但当消息传输完全在内存中时(前提是消息不太大),这可能是浪费CPU资源。因此,更改为纯文本编码也可能提供速度改进。


1
链接已失效。您能否更新或发布相关信息?您的回答非常有帮助,我很想看到剩下的内容。 - VoteCoffee

1

我知道这是一个相当老的问题,但它仍然值得回答。如已经提到的那样,命名管道是最快的,您需要禁用安全性,但如果摆脱数据契约序列化并切换到基于流的传输模式,您将获得最显着的效果。

使用类似以下内容的绑定配置:

                new NetNamedPipeBinding
                {
                    MaxReceivedMessageSize     = 524288000,
                    ReceiveTimeout             = TimeSpan.MaxValue, // never timeout
                    SendTimeout                = TimeSpan.MaxValue, // never timeout
                    ReaderQuotas               =
                    {
                        MaxStringContentLength = 655360000
                    },
                    TransferMode               = TransferMode.Streamed,
                    Security = new NetNamedPipeSecurity
                    {
                        Mode = NetNamedPipeSecurityMode.None,
                        Transport = new NamedPipeTransportSecurity
                        {
                            ProtectionLevel = ProtectionLevel.None
                        }
                    }
                }

定义您的服务消息如下:

像这样:

[MessageContract]
public class CallRequestMessage
{
    [MessageHeader]
    public string Arg1;
    [MessageHeader]
    public int ParametersLen;
    [MessageBodyMember]
    public Stream Parameters;
}

[MessageContract]
public class CallResponceMessage
{
    [MessageHeader]
    public int ResultCode;
    [MessageHeader]
    public int ResultsLen;
    [MessageBodyMember]
    public Stream Results;
}

[ServiceContract]
public interface ILocalServiceAPI
{
    [OperationContract]
    CallResponceMessage Call(CallRequestMessage message);
}

这种方法的缺点是现在您必须自己序列化数据。我更喜欢直接使用protobuf序列化到MemoryStream中。将此流放置到CallRequestMessage.Parameters中。
不要忘记在消息头中传输ParametersLen / ResultsLen,因为Stream是无限的(在读取时,您可能会收到0字节,但与普通流不同,您应该继续读取)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接