gpt4 book ai didi

networking - 发送 WCF 消息在负载下被延迟

转载 作者:行者123 更新时间:2023-12-03 09:46:12 26 4
gpt4 key购买 nike

当从自托管的 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 绑定(bind)。它具有 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

    最佳答案

    在向 Microsoft 支持提出长期支持请求后,我们设法确定了问题。
    使用 Begin/End Invoke 委托(delegate)模式调用 WCF channel 方法实际上会变成同步调用,而不是异步调用。
    异步调用 WCF 方法的正确方法是通过异步委托(delegate)以外的任何方式,其中可能包括线程池、原始线程或 WCF 异步回调。
    最后我用了WCF async callbacks (可以应用于回调接口(interface),虽然我找不到具体的例子)。
    以下链接使这一点更加明确:
    https://docs.microsoft.com/en-us/archive/blogs/drnick/begininvoke-bugs

    关于networking - 发送 WCF 消息在负载下被延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1371218/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com