自己ホスト型WCFサービスから多くのクライアントにメッセージを送信すると(約10秒程度)、メッセージが予想よりもかなり遅延することがあります。ローカルネットワーク)。なぜ誰が考えているのでしょうか、それを修正する方法はありますか?ロード中にWCFメッセージを送信する
一部の背景:アプリケーションは株式ティッカースタイルのサービスです。サードパーティのサーバーからメッセージを受信し、サービスに接続するクライアントにメッセージを再パブリッシュします。メッセージができるだけ早く公開されることは非常に重要です。メッセージを受信してからすべてのクライアントに公開するまでの時間は、50ms未満です(これはDateTime.Nowの解決に近づくほど速いです)。
過去数週間、私たちはメッセージが2,3秒遅れている場合を監視してきました。数日前、大きなスパイクがあり、メッセージは40〜60秒遅れていました。メッセージは私が知る限りでは落とされていません(接続全体が落とされない限り)。遅延は、どのクライアントにも固有ではないようです。ローカルネットワーク上のクライアントを含むすべてのクライアントに影響します。
私はThreadPoolをスパムしてクライアントにメッセージを送信します。メッセージが到着するとすぐに、クライアントごとにメッセージごとにBeginInvoke()を1回呼び出します。その理論は、あるクライアントがメッセージを受信するのが遅い場合(ダイアルアップやアップデートのダウンロードなど)、他のクライアントに影響を与えないということです。それは私が観察していることではありません。ローカルネットワーク上のクライアントを含むすべてのクライアントに同様の期間の遅延が発生しているように見えます。
私が扱っているメッセージの量は、毎秒100-400です。メッセージには、文字列、GUID、日付、メッセージタイプに応じて10-30の整数が含まれます。私はWiresharkをそれぞれ1kB以下のものとして使用しています。私たちはいつでも10-20人のクライアントを接続しています。
WCFサーバーは、Windows 2003 Web Edition Server上のWindowsサービスでホストされています。 SSL/TLS暗号化を有効にしたNetTCPバインディングと、カスタムユーザー名/パスワード認証を使用しています。それは4Mビットのインターネット接続、デュアルコアCPUと1GBのラムを持っており、このアプリケーション専用です。サービスはConcurrencyMode.Multipleに設定されます。サービスプロセスは、負荷が高い場合でも、CPU使用率が20%を超えることはめったにありません。
これまでのところ、私のようなさまざまなWCFの設定オプション微調整しました:(現在は102)
- serviceBehaviors/serviceThrottling/maxConcurrentSessions(現在は64)
- serviceBehaviors/serviceThrottling/maxConcurrentCalls
- バインディングを/ netTcpBinding/binding/maxConnections(現在100)
- bindings/netTcpBinding/binding/listenBacklog(現在100)
- バインディング/ netTcpBinding/bin ding/sendTimeout(現在は45分ですが、私は3分ほど試しましたが)
しきい値に達するとメッセージがWCF内にキューイングされているようですスロットリング制限)。しかし、すべてのクライアントに影響を与えるには、1つまたは2つの低速クライアントを使用してすべての発信接続を最大限にする必要があります。これが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" />
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
0:
</behavior>
コードは、メッセージを送信するために使用しました
メッセージタイプの1のサンプル:マイクロソフトのサポートとの長い支援要請後
<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