2009-09-03 2 views
4

자체 호스팅 WCF 서비스에서 많은 클라이언트 (약 10 명 정도)에게 메시지를 보내면 때로는 메시지가 예상보다 오래 지연되는 경우가 있습니다. 지역 네트워크). 누구나 이것이 왜 생겨나 고 그것을 고쳐야하는지에 대한 생각을 갖고 있습니까?로드 중 지연되는 WCF 메시지 보내기


일부 배경 : 응용 프로그램은 주식 시세 표시 스타일 서비스입니다. 타사 서버에서 메시지를 수신하고 서비스에 연결하는 클라이언트에 메시지를 다시 게시합니다. 가능한 한 빨리 메시지를 게시하는 것이 매우 중요합니다. 대부분의 경우 메시지를 수신하여 모든 클라이언트에 게시하는 시간이 50ms 미만입니다 (이 방법이 DateTime.Now의 해상도에 빨리 접근합니다).

지난 몇 주 동안 Google은 메시지가 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 RAM이 있으며이 응용 프로그램 전용입니다. 서비스는 ConcurrencyMode.Multiple로 설정됩니다. 서비스 프로세스는 심지어 고부하 하에서도 거의 20 %의 CPU 사용량을 초과합니다.

지금까지 내가 같은 다양한 WCF 구성 옵션 쥐게했습니다 (현재 102)

  • serviceBehaviors/serviceThrottling/maxConcurrentSessions (현재 64)
  • serviceBehaviors/serviceThrottling/maxConcurrentCalls
  • 바인딩을/(현재 100)
  • 바인딩/netTcpBinding/바인딩/ding/sendTimeout (현재 45 초, 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" /> 

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> 

코드 메시지를 보내는 데 사용

메시지 유형 중 하나의 샘플 : Microsoft 지원과 긴 지원 요청 후

<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