【问题标题】:Sending WCF messages being delayed under load发送 WCF 消息在负载下被延迟
【发布时间】:2009-09-03 02:54:54
【问题描述】:

从自托管 WCF 服务向许多客户端(大约 10 个左右)发送消息时,有时消息的延迟时间比我预期的要长得多(发送到本地网络上的客户端需要几秒钟)。有谁知道为什么会这样以及如何解决它?


一些背景:该应用程序是股票行情式服务。它从第 3 方服务器接收消息并将它们重新发布到连接到该服务的客户端。尽快发布消息非常重要,在大多数情况下,从接收消息到将其发布给所有客户端的时间小于 50 毫秒(速度快到接近 DateTime.Now 的分辨率)。

在过去的几周里,我们一直在监控一些消息延迟 2 或 3 秒的情况。几天前,我们遇到了一个高峰,消息被延迟了 40-60 秒。据我所知,消息没有被丢弃(除非整个连接被丢弃)。延迟似乎并非特定于任何一个客户;它会影响所有客户端(包括本地网络上的客户端)。

我通过向 ThreadPool 发送垃圾邮件向客户端发送消息。消息到达后,我会为每个客户端的每条消息调用一次 BeginInvoke()。理论是,如果任何一个客户端接收消息的速度很慢(因为它正在拨号和下载更新或其他东西),它不会影响其他客户端。不过,这不是我观察到的。似乎所有客户端(包括本地网络上的客户端)都会受到类似持续时间的延迟影响。

我正在处理的消息量是每秒 100-400 条。消息包含一个字符串、一个 guid、一个日期以及 10-30 个整数,具体取决于消息类型。我使用 Wireshark 观察到它们每个小于 1kB。我们随时有 10-20 个客户端连接。

WCF 服务器托管在 Windows 2003 Web Edition Server 上的 Windows 服务中。我正在使用启用了 SSL/TLS 加密和自定义用户名/密码身份验证的 NetTCP 绑定。它具有 4Mbit 互联网连接、双核 CPU 和 1GB 内存,专用于该应用程序。该服务设置为 ConcurrencyMode.Multiple。服务进程即使在高负载下也很少超过 20% 的 CPU 使用率。

到目前为止,我已经调整了各种 WCF 配置选项,例如:

  • serviceBehaviors/serviceThrottling/maxConcurrentSessions(目前为 102)
  • serviceBehaviors/serviceThrottling/maxConcurrentCalls(目前为 64 个)
  • bindings/netTcpBinding/binding/maxConnections(当前为 100)
  • bindings/netTcpBinding/binding/listenBacklog(当前为 100)
  • bindings/netTcpBinding/binding/sendTimeout(目前是45s,虽然我试过高达3分钟)

在我看来,一旦达到某个阈值,消息就会在 WCF 中排队(因此我要增加限制限制)。但是要影响所有客户端,它需要最大化与一两个慢客户端的所有传出连接。有谁知道这是否适用于 WCF 内部?

我还可以通过在将传入消息发送到客户端时合并它们来提高效率。但是,我怀疑存在一些潜在的问题,从长远来看,合并不会解决问题。

WCF 配置(公司名称已更改):

<system.serviceModel>

<host>
 <baseAddresses>
  <add baseAddress="net.tcp://localhost:8100/Publisher"/>
 </baseAddresses>
</host>

<endpoint address="ThePublisher"
                              binding="netTcpBinding"
                              bindingConfiguration="Tcp"
                                      contract="Company.Product.Server.Publisher.IPublisher" />

</behavior>

用于发送消息的代码:

    Private Sub HandleDataBackground(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
            If Me._FeedDataQueue.Count > 0 Then
                ' Dequeue any items received in last 50ms.
                While True
                    Dim dataAndReceivedTime As DataWithReceivedTimeArg
                    SyncLock Me._FeedDataQueue
                        If Me._FeedDataQueue.Count = 0 Then Exit While
                        dataAndReceivedTime = Me._FeedDataQueue.Dequeue()
                    End SyncLock

                    ' Publish data to all clients.
                    Me.SendDataToClients(dataAndReceivedTime)
                End While
            End If
    End Sub

    Private Sub SendDataToClients(ByVal data As DataWithReceivedTimeArg)
            Dim clientsToReceive As IEnumerable(Of ClientInformation)
            SyncLock Me._ClientInformation
                clientsToReceive = Me._ClientInformation.Values.Where(Function(c) Contract.CollectionContains(c.ContractSubscriptions, data.Data.Contract) AndAlso c.IsUsable).ToList()
            End SyncLock

            For Each clientInfo In clientsToReceive
                Dim futureChangeMethod As New InvokeClientCallbackDelegate(Of DataItem)(AddressOf Me.InvokeClientCallback)
                futureChangeMethod.BeginInvoke(clientInfo, data.Data, AddressOf Me.SendDataToClient)
            Next

    End Sub
    Private Sub SendDataToClient(ByVal callback As IFusionIndicatorClientCallback, ByVal data As DataItem)
        ' Send 
        callback.ReceiveData(data)
    End Sub

    Private Sub InvokeClientCallback(Of DataT)(ByVal client As ClientInformation, ByVal data As DataT, ByVal method As InvokeClientCallbackMethodDelegate(Of DataT))
        Try
            ' Send 
            If client.IsUsable Then
                method(client.CallbackObject, data)
                client.LastContact = DateTime.Now
            Else
                ' Make sure the callback channel has been removed.
                SyncLock Me._ClientInformation
                    Me._ClientInformation.Remove(client.SessionId)
                End SyncLock
            End If
        Catch ex As CommunicationException
            ....
        Catch ex As ObjectDisposedException
            ....
        Catch ex As TimeoutException
            ....
        Catch ex As Exception
            ....
        End Try
    End Sub

其中一种消息类型的示例:

 <DataContract(), KnownType(GetType(DateTimeOffset)), KnownType(GetType(DataItemDepth)), KnownType(GetType(DataItemDepthDetail)), KnownType(GetType(DataItemHistory))> _
 Public MustInherit Class DataItem
  Implements ICloneable

  Protected _Contract As String
  Protected _MessageId As Guid
  Protected _TradeDate As DateTime

  <DataMember()> _
  Public Property Contract() As String
   ...
  End Property

  <DataMember()> _
  Public Property MessageId() As Guid
   ...
  End Property

  <DataMember()> _
  Public Property TradeDate() As DateTime
   ...
  End Property

  Public MustOverride Function Clone() As Object Implements System.ICloneable.Clone
 End Class

 <DataContract()> _
 Public Class DataItemDepth
  Inherits DataItem

  Protected _VolumnPriceDetail As IList(Of DataItemDepthItem)

  <DataMember()> _
  Public Property VolumnPriceDetail() As IList(Of DataItemDepthItem)
   ...
  End Property

  Public Overrides Function Clone() As Object
   ...
  End Function
 End Class


 <DataContract()> _
 Public Class DataItemDepthItem
  Protected _Volume As Int32
  Protected _Price As Int32
  Protected _BidOrAsk As BidOrAsk ' BidOrAsk is an Int32 enum
  Protected _Level As Int32

  <DataMember()> _
  Public Property Volume() As Int32
   ...
  End Property

  <DataMember()> _
  Public Property Price() As Int32
   ...
  End Property

  <DataMember()> _
  Public Property BidOrAsk() As BidOrAsk  ' BidOrAsk is an Int32 enum
   ...
  End Property

  <DataMember()> _
  Public Property Level() As Int32
   ...
  End Property
 End Class

【问题讨论】:

    标签: networking performance


    【解决方案1】:

    在向 Microsoft 支持提出长期支持请求后,我们设法确定了问题。

    使用 Begin/End Invoke 委托模式调用 WCF 通道方法实际上变成了同步调用,而不是异步。

    异步调用 WCF 方法的正确方式是异步委托以外的任何方式,异步委托可能包括线程池、原始线程或 WCF 异步回调。

    最后我使用了WCF async callbacks(可以应用于回调接口,虽然我找不到具体的例子)。

    以下链接使这一点更加明确: https://docs.microsoft.com/en-us/archive/blogs/drnick/begininvoke-bugs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-15
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 2021-11-08
      • 2014-05-14
      • 1970-01-01
      相关资源
      最近更新 更多