5

ZeroMQ N-to-N pub/sub モデルを活用した POC を構築しています。アプリ サーバーから http リクエストが処理されたときに、スレッドがデータベースからデータをプルすると、ローカルの memcache インスタンスがそのデータで更新されます。アプリ サーバー クラスター内の他の memcache インスタンスを同期するために、リクエスト スレッドはZMQ パブリッシャーを使用してデータを含むメッセージを送信します...したがって、問題は次のとおりです。メッセージを送信するためにソケットに依存する多くのスレッドがありますか? ソケットのプールを共有しますか? スレッドごとにソケットを作成/破棄しますか?

戦略 1 - スレッド管理のパブリッシャ ソケット
このアプローチでは、各スレッドT1、、、T2およびT3が、ソケット オブジェクト (パブリッシャ) の作成、接続の確立、メッセージの送信、および最後にソケットのクローズによって、ソケット オブジェクトのライフサイクルを管理します。thisに基づいて、これは確かに最も安全なアプローチですが、ソケットの作成、接続、および破棄を繰り返す場合のオーバーヘッドに関して懸念があります。オーバーヘッドがパフォーマンスに悪影響を及ぼす場合は、回避したいと考えています。

ここに画像の説明を入力

戦略 2 - パブリッシャー ソケット オブジェクト プール
このアプローチでは、親プロセス (アプリ サーバー) が起動時に ZMQ パブリッシャーのプールを初期化します。スレッドがパブリッシャーを必要とする場合、オブジェクト プールからパブリッシャーを取得し、メッセージを送信してから、パブリッシャーをプールに返します。パブリッシャーを使用するスレッドに関しては、ソケットの作成、接続、および破棄のプロセスは排除されますが、プールへのアクセスは同期され、同じパブリッシャー オブジェクトを同時に使用する 2 つのスレッドを回避します。発生する可能性があります。

最初に SO テストでリトマス試験を行いたかったため、どちらのアプローチもプロファイルしていません。ボリュームに関しては、アプリケーションは「重い」パブリッシュではありませんが、メッセージをパブリッシュする必要があると同時に、(アプリ サーバーごとに) 100 ~ 150 のスレッドが存在する可能性があります。

ZMQ パブリッシャー オブジェクト プール

繰り返しになりますが、メッセージの送信をパブリッシャに依存するスレッドがアプリケーションに多数ある場合、パフォーマンスを重視しながらオーバーヘッドを最小限に抑えるには、どの戦略が最も効果的でしょうか?

4

2 に答える 2

4

推定スループットの実際の数値を提供せずに、パフォーマンスについて実際に質問することはできません。毎秒 10 リクエスト、100、1,000、10K について話しているのでしょうか?

HTTP サーバーが実際に要求ごとにスレッドを作成および破棄している場合、0MQ ソケットを繰り返し作成すると OS に負荷がかかり、要求の量とプロセスの制限に応じて、機能するか、ハンドルが不足します。これを簡単にテストできます。これが最初のステップです。

次に、ソケットのプール (「ZMQ パブリッシャー」とは何を意味するか) を共有するのは厄介です。人々はこれを行いますが、ソケットはスレッドセーフではないため、ソケットを別のスレッドに切り替えるときは非常に注意が必要です。

スレッドを永続的に保つ方法があれば、必要に応じて各スレッドが PUB ソケットを作成し、存在する限りそれを保持できます。そうでない場合、私の最初の設計では、とにかくソケットを作成/破棄しますが、inproc:// を使用してメッセージを単一の永続的なフォワーダー スレッド (SUB-PUB プロキシ) に送信します。これをテストして、壊れたら、もっとエキゾチックなデザインに行きます.

一般に、設計プロセスを考えすぎるよりも、最も単純な設計を作成してそれを壊す方がよいでしょう (特に最初の段階では)。

于 2013-05-21T06:29:58.730 に答える
1

It sounds like premature optimization to me too, and if at all possible, you should stick with the first strategy and save yourself the headaches.

But as an alternative to your second option, you could perhaps maintain an Executor thread pool inside your application to do the actual zmq sending. This way each executor thread can keep its own socket. You can listen to application/servlet life cycle events to know when to shutdown the pool and cleanup the sockets.

EDIT:

The simplest way to do this is to create the Executor using Executors.newFixedThreadPool() and feed it Runnable jobs that use a ThreadLocal socket. (See Java Executors and per-thread (not per-work unit) objects? ) The threads will be created only once and reused from then on until the Executor is shutdown.

This gets a little tricky when an exception is thrown in the job's run() method. I suspect you'll find you need a little bit more control over the executor threads' lifecycle. If so, you can copy the source for newFixedThreadPool:

return new ThreadPoolExecutor(nThreads, nThreads,
                              0L, TimeUnit.MILLISECONDS,
                              new LinkedBlockingQueue<Runnable>());

and subclass the ThreadPoolExecutor that gets instantiated to customize it. This way you could for example override afterExecute to detect and clean up broken sockets.

The send jobs get transferred to the worker threads through a blocking queue. I realise that this is not the ZeroMQ way to hand off the messages to the worker threads, which would be inproc messaging. This moves ZeroMQ away from the HTTP worker threads whose lifecycle is out of your control and therefore hard to maintain sockets in, more towards the edge of the application. You'd have to simply test which of the both is more efficient and have to make a judgement call on how rigorously you want your application to adopt the ZeroMQ messaging paradigm for inter-thread communication.

于 2013-05-21T06:58:36.130 に答える