2017-10-18 15 views
4

サービスファブリックのステートレスサービスにメッセージをブロードキャストするクラスがあります。このステートレスサービスは単一のパーティションを持ちますが、多くのレプリカがあります。 メッセージは、システム内のすべてのレプリカに送信する必要があります。したがって、FabricClientに単一パーティションと、そのパーティションのすべてのレプリカを照会します。 標準のHTTP通信(ステートレスなサービスには、WebListener/HttpSysを使用してSelfhosted OWINリスナを持つCommunication Listenerがあります)を共有HttpClientインスタンスと共に使用します。 負荷テスト中、メッセージの送信中に多くのエラーが発生します。同じアプリケーション内に、通信(WebListener/HttpSys、ServiceProxy、およびActorProxy)する他のサービスがあることに注意してください。負荷テスト中のFabricTransientException "指定されたService Fabricゲートウェイエンドポイントにpingできませんでした。

我々は例外を参照してくださいコードは(スタックトレースは、コードサンプルの下にある)である:

private async Task SendMessageToReplicas(string actionName, string message) 
{ 
    var fabricClient = new FabricClient(); 
    var eventNotificationHandlerServiceUri = new Uri(ServiceFabricSettings.EventNotificationHandlerServiceName); 

    var promises = new List<Task>(); 
    // There is only one partition of this service, but there are many replica's 
    Partition partition = (await fabricClient.QueryManager.GetPartitionListAsync(eventNotificationHandlerServiceUri).ConfigureAwait(false)).First(); 

    string continuationToken = null; 
    do 
    { 
    var replicas = await fabricClient.QueryManager.GetReplicaListAsync(partition.PartitionInformation.Id, continuationToken).ConfigureAwait(false); 
    foreach(Replica replica in replicas) 
    { 
     promises.Add(SendMessageToReplica(replica, actionName, message)); 
    } 

    continuationToken = replicas.ContinuationToken; 
    } while(continuationToken != null); 

    await Task.WhenAll(promises).ConfigureAwait(false); 
} 


private async Task SendMessageToReplica(Replica replica, string actionName, string message) 
{ 
    if(replica.TryGetEndpoint(out Uri replicaUrl)) 
    { 
    Uri requestUri = UriUtility.Combine(replicaUrl, actionName); 
    using(var response = await _httpClient.PostAsync(requestUri, message == null ? null : new JsonContent(message)).ConfigureAwait(false)) 
    { 
     string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 
     if(!response.IsSuccessStatusCode) 
     { 
     throw new Exception(); 
     } 
    } 
    } 
    else 
    { 
    throw new Exception(); 
    } 
} 

次の例外がスローされます。

System.Fabric.FabricTransientException: Could not ping any of the provided Service Fabric gateway endpoints. ---> System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80071C49 
at System.Fabric.Interop.NativeClient.IFabricQueryClient9.EndGetPartitionList2(IFabricAsyncOperationContext context) 
at System.Fabric.FabricClient.QueryClient.GetPartitionListAsyncEndWrapper(IFabricAsyncOperationContext context) 
at System.Fabric.Interop.AsyncCallOutAdapter2`1.Finish(IFabricAsyncOperationContext context, Boolean expectedCompletedSynchronously) 
--- End of inner exception stack trace --- 
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Company.ServiceFabric.ServiceFabricEventNotifier.<SendMessageToReplicas>d__7.MoveNext() in c:\work\ServiceFabricEventNotifier.cs:line 138 

同じ期間に、我々はまた、この例外ビーイングを参照してくださいスロー:

System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.) ---> System.ComponentModel.Win32Exception (0x80004005): An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full 
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) 
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) 
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) 
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) 
at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry) 
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry) 
at System.Data.SqlClient.SqlConnection.OpenAsync(CancellationToken cancellationToken) 

クラスタ内のマシンのイベントログには、 E警告:

Event 4096 
Source Microsoft-Service-Fabric 
Level: Error 
client-02VM4.company.nl:19000 failed to bind to local port for connecting: 0x80072747 

ことができます誰:

Event ID: 4231 
Source: Tcpip 
Level: Warning 
A request to allocate an ephemeral port number from the global TCP port space has failed due to all such ports being in use. 

Event ID: 4227 
Source: Tcpip 
Level: Warning 
TCP/IP failed to establish an outgoing connection because the selected local endpoint was recently used to connect to the same remote endpoint. This error typically occurs when outgoing connections are opened and closed at a high rate, causing all available local ports to be used and forcing TCP/IP to reuse a local port for an outgoing connection. To minimize the risk of data corruption, the TCP/IP standard requires a minimum time period to elapse between successive connections from a given local endpoint to a given remote endpoint. 

そして最後に、マイクロソフト・サービスファブリックの管理ログがしばらく

Event 4121 
Source Microsoft-Service-Fabric 
Level: Warning 
client-02VM4.company.nl:19000/192.168.10.36:19000: error = 2147942452, failureCount=160522. Filter by (type~Transport.St && ~"(?i)02VM4.company.nl:19000") to get listener lifecycle. Connect failure is expected if listener was never started, or listener/its process was stopped before/during connecting. 

Event 4097 
Source Microsoft-Service-Fabric 
Level: Warning 
client-02VM4.company.nl:19000 : connect failed, having tried all addresses 

に似た警告の数百を示し、警告がエラーになりますなぜこれが起こったのか、これを解決するために私たちができることを教えてください。私たちは何か間違っているのですか?

答えて

1

のために私たちは(私はOPで動作)、これをテストしてきたとエスベンバッハによって示唆されているように、それはFabricClientであることが判明参照してください。

FabricClient上のドキュメントも述べている:

非常には、可能な限りFabricClientsを共有することをお勧めします。これは、FabricClientには、キャッシングやバッチ処理などの複数の最適化があり、それ以外の場合は完全に利用できないためです。

FabricClientは、インスタンスを共有する必要があるHttpClientクラスのように動作しているようですが、同じ問題が発生した場合にポート枯渇が発生する可能性があります。

FabricClient documentationでの作業の一般的な例外は、しかしまたFabricObjectClosedExceptionは、あなたがすべき発生​​したときと述べて:あなたが使用しているFabricClientオブジェクトの

廃棄して、新しいFabricClientオブジェクトをインスタンス化します。

FabricClientを共有すると、ポートの枯渇問題が解決されます。

1

ポートの枯渇の問題があるようです。その場合には 接続を再利用する方法を理解する必要があります。また、使用可能なすべてのポートを使い切らないように、ある種のスロットルメカニズムを実装する必要があります。

ファブリッククライアントがどのように動作するのかわからない場合は、それが疲労の原因であるか、おそらくコードを見ることができないSQL Serverの部分です(ただし、ログに投稿したので、あなたのpingテストとは無関係です)。

httpwebresponse(https://github.com/Microsoft/referencesource/blob/master/System/net/System/Net/HttpWebResponse.cs)の参考文献を見ると、レスポンス(つまり、postasyncのusingステートメント)を配置するとHttpClients接続が閉じられることもあります。つまり、接続を再利用するのではなく、新しい接続を常に開いているということです。

あなたのhttpwebresponseを処理しないバリアントをテストするのは簡単なことです。

+0

実際、ポート枯渇問題だと思います。 HttpWebResponsesを処理しないことは奇妙に思えます。サービスファブリッククライアントまたはHttpClientの使用に問題があるかどうかを調べるために、いくつかのバリエーションを試してみましょう。 –

+0

それは奇妙だと思うが、参照元を見ると、終了時に接続グループを閉じるようだ。 ConnectStream connectStream = m_ConnectStream as ConnectStream; if(connectStream!= null && connectStream.Connection!= null) { connectStream.Connection.ServicePoint.CloseConnectionGroup(ConnectionGroupName); } だからおそらく –

1

既存のサービスインスタンスをそれぞれ呼び出す理由は何ですか?

通常、SFランタイムによって提供されるサービスインスタンスを1つだけ呼び出す必要があります(同じノード/プロセスから、またはこのノードがあまりにも負荷がかかる場合は別のノードから1つを選択しようとします)。

すべてのサービスインスタンスで何らかの状態変更/イベントを通知する必要がある場合は、サービス実装内でこれを実行して、この状態の変更(ステートフルサービスからのものかどうか)この情報が必要になるたびにサブイベントキューに格納されます(たとえばhttps://github.com/loekd/ServiceFabric.PubSubActorsを参照)。

もう1つのアイデアは、バルクデータをサポートする別のアクションで、多数のメッセージを一度にサービスインスタンスに送信することです。

単一の送信元からの個々のメッセージを高い頻度で送信する必要がある場合は、前の回答のように接続を開いたままにすることをお勧めします。

また、呼び出し側は接続の回復力を行う必要があり、たとえばhttps://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication#communicating-with-a-service

+0

私たちはこのコミュニケーションのためにActorイベントを実際に使用しています。しかし、それでも最高の解決策ではないかもしれませんが、技術的にはうまくいくはずです。 –

関連する問題