2009-09-03 10 views
4

自己ホスト型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 

答えて

2

、我々は問題を識別するために管理しました。

Begin/End Invokeデリゲートパターンを使用したWCFチャネルメソッドの呼び出しは、実際には非同期ではなく同期呼び出しになります。

非同期的にWCFメソッドを呼び出す正しい方法は、スレッドプール、生スレッドまたはWCF非同期コールバックを含む可能性がある非同期の代理人を除くすべての方法です。

私はWCF async callbacksを使用しました(コールバックインターフェイスに適用できますが、具体的な例は見つかりませんでした)。

以下のリンクを使用すると、より明示的になります。 http://blogs.msdn.com/drnick/archive/2007/06/12/begininvoke-bugs.aspx

関連する問題