为长时间运行的WCF应用程序选择正确的连接属性

为长时间运行的WCF应用程序选择正确的连接属性,wcf,wcf-binding,wcf-client,Wcf,Wcf Binding,Wcf Client,我正在使用WCF用C#编写一个客户机/服务器应用程序。我的所有测试都进行得很顺利,但在部署服务后,我发现在与服务器通信时出现了随机问题。 我启用了调试,并在服务器中看到如下消息: The communication object, System.ServiceModel.Channels.ServerReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted. 模式如下:

我正在使用WCF用C#编写一个客户机/服务器应用程序。我的所有测试都进行得很顺利,但在部署服务后,我发现在与服务器通信时出现了随机问题。 我启用了调试,并在服务器中看到如下消息:

The communication object, System.ServiceModel.Channels.ServerReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted.
模式如下:

  • 客户端正在发送查询
  • 服务正在处理查询
  • 服务正在发回一些东西
  • 活动边界为“停止”级别-一切正常
  • 将inactivityTimeout of reliable session添加到最后一个联系人的datetime,您就有了服务引发的异常的时间戳
应用程序如下所示:服务实例提供与数据库交互的API方法,类型为“netTcpBinding”。几个客户端(大约40个)连接在一起,并从服务中随机调用方法。客户可以开放几天,甚至不发送或接收任何东西

以下是相关位:

服务

    [ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
    [ExceptionMarshallingBehavior]
...
    <behaviors>
      <serviceBehaviors>
        <behavior name="behaviorConfig">
          <serviceMetadata httpGetEnabled="false" httpGetUrl="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
            maxConcurrentInstances="50" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <bindings>
      <netTcpBinding>
        <binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
          receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
          listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
          maxConnections="1000" maxReceivedMessageSize="671088640"     portSharingEnabled="true">
          <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
            maxBytesPerRead="671088640" />
          <reliableSession inactivityTimeout="23:59:59" enabled="true" />
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
                    receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
                    transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
                    maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
                    maxReceivedMessageSize="671088640">
                    <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
                        maxBytesPerRead="671088640" />
                    <reliableSession ordered="true" inactivityTimeout="23:59:59"
                        enabled="true" />
                    <security mode="None">
                        <message clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>

服务配置

    [ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
    [ExceptionMarshallingBehavior]
...
    <behaviors>
      <serviceBehaviors>
        <behavior name="behaviorConfig">
          <serviceMetadata httpGetEnabled="false" httpGetUrl="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
            maxConcurrentInstances="50" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <bindings>
      <netTcpBinding>
        <binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
          receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
          listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
          maxConnections="1000" maxReceivedMessageSize="671088640"     portSharingEnabled="true">
          <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
            maxBytesPerRead="671088640" />
          <reliableSession inactivityTimeout="23:59:59" enabled="true" />
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
                    receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
                    transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
                    maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
                    maxReceivedMessageSize="671088640">
                    <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
                        maxBytesPerRead="671088640" />
                    <reliableSession ordered="true" inactivityTimeout="23:59:59"
                        enabled="true" />
                    <security mode="None">
                        <message clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>

客户端配置

    [ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
    [ExceptionMarshallingBehavior]
...
    <behaviors>
      <serviceBehaviors>
        <behavior name="behaviorConfig">
          <serviceMetadata httpGetEnabled="false" httpGetUrl="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
            maxConcurrentInstances="50" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <bindings>
      <netTcpBinding>
        <binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
          receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
          listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
          maxConnections="1000" maxReceivedMessageSize="671088640"     portSharingEnabled="true">
          <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
            maxBytesPerRead="671088640" />
          <reliableSession inactivityTimeout="23:59:59" enabled="true" />
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
                    receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
                    transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
                    maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
                    maxReceivedMessageSize="671088640">
                    <readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
                        maxBytesPerRead="671088640" />
                    <reliableSession ordered="true" inactivityTimeout="23:59:59"
                        enabled="true" />
                    <security mode="None">
                        <message clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>

这里有什么问题吗?这类应用程序的最佳配置是什么

更新: 我遇到了一件事:

在一个服务合同中,我更改了一些内容并通知所有连接的客户端。它通常运行良好,至少在我的测试中是这样。但上一次“崩溃”或“冻结”我浏览了日志,发现最新的函数是我使用回调契约通知客户机的函数

我想在那里做的是:我将一些东西保存到数据库中,最后我将更改通知所有连接的客户端。我认为连接的客户端列表不再是最新的,在这一步它会超时

现在的问题是如何避免这些超时

  • 我应该在服务中使用线程吗?我想一旦服务调用结束,线程就会被终止,对吗
  • 我可以实现一个静态队列函数,它执行所有回调通知(这是Marc_建议的)
  • 有没有一种方法可以可靠地检测服务器内部的连接断开

这只是一个疯狂的想法:因为你的客户似乎在很长一段时间(甚至几天)内发送消息,但似乎并不经常发送消息-你是否可以重新构建你的应用程序,使用消息队列而不是回叫合同

队列是解耦两个系统、减少超时可能性等的好方法

在基于队列的方案中,您的客户端会将消息放入队列(例如,随Windows Server的每个副本一起提供的MSMQ),您的服务将在该传入队列上侦听消息。该服务将获取消息并对其进行处理,通常会将某种响应消息放回第二个队列(然后客户机将侦听该队列)

这里的主要好处是:

  • 您不需要非常脆弱和复杂的回拨合同设置
  • 您的系统已解耦,即使它们之间的连接中断几秒钟、几分钟、几小时,也将继续工作
  • 您的服务可以响应传入的消息并发送更多“目标”响应,例如,某些客户端可以在“正常”响应队列上侦听,其他客户端可以在“优先级”响应队列上侦听,等等。系统只会为您提供更大的灵活性
请参阅更多参考资料:

  • -留言
看看像这样的事情

以及其他系统(MassTransit等),它们支持排队通信,以在系统之间提供可扩展的可靠消息传递。

好的,问题解决了

每当一个客户端意外死亡而未注销时,就会出现此问题。 因此,每当客户机从服务调用一个方法,而该服务本身希望向所有连接的客户机广播一条消息时,就会出现超时,从而阻塞客户机。 因此,将所有回调封装到委托函数中有助于解决问题

我是这样做的:

public enum CallbackType
{
    callbackfunc1,
    callbackfunc2,
    callbackfunc3
};
public class CallbackEventArgs : EventArgs
{
    public ISVCCallback callback;
    public CallbackType type;
    public string s1;
    public string s2;
    public string s3;
    public List<string> ls1;
}

// Declare delegate
public delegate void SVCEventHandler(object sender, CallbackEventArgs e);

谢谢

嗨,马克,非常感谢你的回答。不幸的是,这个项目已经完成了98%,我只剩下2周的时间来实现缺少的东西。抛弃当前的体系结构,采用另一种方法在此时听起来并不正确。不幸的是,我现在必须用WCF解决这个问题,然后可能会在版本2.0中重新设计架构。我希望你们能给我这个问题的答案。起初我认为WCF正是为这类东西而设计的……是的,当然——WCF很棒,而且非常灵活。它还支持MSMQ消息传递!回调合同的内容有时有点“脆弱”,不一定是WCF的错,只是很难做到。