SObjectizerシンプルから耇雑ぞ。 パヌトII

最初の蚘事では、SObjectizerに぀いお説明したした。 2番目の蚘事では、゚ヌゞェントがどのように芋えるか、なぜ、どのように、どこで進化するかに぀いお話し始めたした。 今日もこの話を続け、デモ゚ヌゞェントの実装をさらに耇雑にしたす。 同時に、非同期メッセヌゞングの信頌性を確認したす。



前回、電子メヌルでファむルの内容を読み取る操䜜は別のIO゚ヌゞェントに任せる必芁があるずいう事実に決着したした。 それをしお、䜕が起こるか芋おみたしょう。



最初に、IO゚ヌゞェントずemail_analyzerが盞互に亀換する䞀連のメッセヌゞが必芁です。



//     . struct load_email_request { //    . string email_file_; //    . mbox_t reply_to_; }; //    . struct load_email_succeed { //  . string content_; }; //    . struct load_email_failed { //   . string what_; };
      
      





次に、email_analyzer゚ヌゞェントがリク゚ストメッセヌゞload_email_requestを送信する正確な堎所を決定する必芁がありたす。 通垞の方法で行うこずができたすIO゚ヌゞェントを登録するずき、そのdirect_mboxを保存し、このmboxをアナラむザヌ゚ヌゞェントコンストラクタヌにパラメヌタヌずしお枡し、次に各゚ヌゞェントのemail_analyzer゚ヌゞェントコンストラクタヌにパラメヌタヌを枡したす...基本的に、耇数の異なるIOが必芁な堎合゚ヌゞェント、それがどのように行われるべきかです。 しかし、私たちの問題では、1぀のIO゚ヌゞェントで十分です。 これにより、名前付きmboxのデモを行うこずができたす。



名前付きmboxは、so_5 :: environment_t :: create_mboxnameを呌び出すこずによっお䜜成されたす。 同じ名前でcreate_mboxを耇数回呌び出すず、同じ名前のcreate_mboxが最初に呌び出されたずきに䜜成される同じmboxが垞に返されたす。



IO゚ヌゞェントは名前付きmboxを䜜成し、サブスクラむブしたす。 Email_analyzer゚ヌゞェントは、load_email_requestメッセヌゞを送信する必芁があるずきに同じmboxを取埗したす。 したがっお、analyzer_managerを介しおmbox IO゚ヌゞェントを「ドラッグ」する必芁がなくなりたす。



IO゚ヌゞェントずemail_manager間のむンタヌフェヌスを決定したので、 email_analyzer゚ヌゞェントの新しいバヌゞョンを䜜成できたす。



 //  .   IO-  IO-. class email_analyzer : public agent_t { public : email_analyzer( context_t ctx, string email_file, mbox_t reply_to ) : agent_t(ctx), email_file_(move(email_file)), reply_to_(move(reply_to)) {} //  ,      . //         //    . virtual void so_define_agent() override { //       IO-.  //       . so_subscribe_self() .event( &email_analyzer::on_load_succeed ) .event( &email_analyzer::on_load_failed ); } virtual void so_evt_start() override { //       IO-   //  email . send< load_email_request >( // mbox IO-    . so_environment().create_mbox( "io_agent" ), email_file_, //       mbox. so_direct_mbox() ); } private : const string email_file_; const mbox_t reply_to_; void on_load_succeed( const load_email_succeed & msg ) { try { //     . auto parsed_data = parse_email( msg.content_ ); auto status = check_headers( parsed_data->headers() ); if( check_status::safe == status ) status = check_body( parsed_data->body() ); if( check_status::safe == status ) status = check_attachments( parsed_data->attachments() ); send< check_result >( reply_to_, email_file_, status ); } catch( const exception & ) { //   -      //    email-   . send< check_result >( reply_to_, email_file_, check_status::check_failure ); } //    ,   , //   . so_deregister_agent_coop_normally(); } void on_load_failed( const load_email_failed & ) { //    .    //      . send< check_result >( reply_to_, email_file_, check_status::check_failure ); so_deregister_agent_coop_normally(); } };
      
      





Email_analyzer゚ヌゞェントは、これを効率的に行う方法を知っおいる別の゚ヌゞェントにIO操䜜を委任するようになりたした。 したがっお、䜜業スレッドのemail_analyzer゚ヌゞェントは、IO゚ヌゞェントぞの割り圓おを凊理するか、email_analyzer応答を凊理したす。 これにより、䜜成できるemail_analyzer゚ヌゞェントの数ず、必芁なワヌクスレッドの数のビュヌを倉曎するこずができたす。



各email_analyzer゚ヌゞェント自䜓が同期IO操䜜を実行した堎合、䞊列IO操䜜を蚱可するのず同じ数のワヌクスレッドをプヌルに入れる必芁がありたした。 プヌル内の䜜業スレッドの数よりも倚くのemail_analyzer゚ヌゞェントを䜜成しおも意味がありたせんでした。 プヌルに16のスレッドがあり、32の゚ヌゞェントが同時に存圚できるようにするず、これらの゚ヌゞェントの半分は、䜜業スレッドが解攟されるのを単に埅぀こずになりたす。



珟圚、IO操䜜が別の䜜業コンテキストに移動された埌、たず、プヌル内の䜜業スレッドの数を枛らすこずができたす。 むベント内のEmail_analyzer゚ヌゞェントは、䞻にCPUロヌド操䜜を実行したす。 したがっお、利甚可胜な凊理コアよりも倚くのワヌクフロヌを䜜成しおも意味がありたせん。 したがっお、4コアプロセッサがある堎合、プヌル内に16スレッドではなく、4スレッド以䞋である必芁がありたす。



次に、IO操䜜が電子メヌルのコンテンツの凊理よりも時間がかかる堎合、プヌル内のスレッドよりも倚くのemail_analyzer゚ヌゞェントを䜜成する機䌚が埗られたす。 単玔に、これらの゚ヌゞェントのほずんどは、IO操䜜の結果を埅ちたす。 ただし、電子メヌルのダりンロヌド時間がコンテンツの分析時間に匹敵するか、それより短い堎合、このアむテムは関連性を倱い、プヌル内のスレッドの数よりも倚く1-2-3 email_analyzer゚ヌゞェントのみを䜜成できたす。 これらの蚭定はすべお、analyzer_manager゚ヌゞェントで1か所で簡単に行えたす。 コヌド内のいく぀かの定数を文字通り倉曎し、倉曎が゜リュヌションのパフォヌマンスにどのように圱響するかを確認するだけで十分です。 ただし、パフォヌマンスチュヌニングは別の倧きなトピックであり、今すぐ掘り䞋げるのは時期尚早です...



したがっお、以前のバヌゞョンの問題を修正するemail_analyzer゚ヌゞェントの次のバヌゞョンがありたす。 蚱容できるず考えおもいいですか



いや



問題は、結果の実装が信頌できるず芋なされないこずです。



この実装は、送信するメッセヌゞが倱われずに垞に宛先に届くずいう楜芳的なシナリオ向けに蚭蚈されおおり、宛先に届くず垞に凊理されたす。 その埌、垞に必芁な答えが埗られたす。



しかし、人生の厳しい真実は、システムが個々のアクタヌ/゚ヌゞェント間の非同期メッセヌゞ亀換で構築されおいる堎合、この同じ非同期亀換は絶察に信頌できるものずは芋なされないずいうこずです。 メッセヌゞが倱われる可胜性がありたす。 そしお、これは正垞です。



メッセヌゞの損倱はさたざたな理由で発生する可胜性がありたす。 たずえば、受信゚ヌゞェントはただメッセヌゞをサブスクラむブできおいたせん。 たたは、珟時点では受信者゚ヌゞェントがたったくいない。 どちらかですが、過負荷保護メカニズムは機胜しおいたすこれに぀いおは埌続の蚘事で詳しく説明したす。 ゚ヌゞェントがいお、メッセヌゞが届きたしたが、゚ヌゞェントはこのメッセヌゞが凊理されおいない状態です。 ゚ヌゞェントがいお、メッセヌゞが圌に届き、凊理を開始したしたが、凊理䞭に䜕らかのアプリケヌション゚ラヌが発生し、倱敗した゚ヌゞェントは応答を送信したせんでした。



䞀般に、非同期メッセヌゞを介した゚ヌゞェント間の通信は、UDPプロトコルを介したホストの察話に䌌おいたす。 ほずんどの堎合、デヌタグラムは受信者に届きたす。 ただし、凊理䞭たたは凊理䞭に倱われるこずもありたす。



䞊蚘は、load_email_requestがIO゚ヌゞェントに到達しない可胜性があるこずを意味したす。 たたは、応答メッセヌゞload_email_successed / load_email_failedがemail_analyzer゚ヌゞェントに届かない堎合がありたす。 そしお、この堎合はどうなりたすか



システムに存圚するが䜕もしないemail_analyzer゚ヌゞェントを取埗したす。 動䜜したせん。 死ぬ぀もりはない。 たた、他のemail_analyzer゚ヌゞェントを開始したせん。 幞運でなければ、analyzer_managerによっお䜜成されたすべおのemail_analyzer゚ヌゞェントが、䜕もしない半死䜓に倉わる状況に遭遇する可胜性がありたす。 その埌、analyzer_managerは単玔に順番にリク゚ストを蓄積し、タむムアりトが経過するずそれらを砎棄したす。 ただし、有甚な䜜業は実行されたせん。



この状況から抜け出す方法は



たずえば、タむムアりトを制埡するこずにより。 email_analyzer゚ヌゞェントによるIO操䜜の実行時間の制埡を導入できたす぀たり、応答が長すぎる堎合、IO操䜜が倱敗したず想定したす。 たたは、analyzer_manager゚ヌゞェントで電子メヌル分析操䜜党䜓の実行時間を制埡できたす。 たたは䞡方を行いたす。



簡単にするために、email_analyzer゚ヌゞェントで IO操䜜のタむムアりトをカりントするように制限しおいたす



 //  .   -   IO-. class email_analyzer : public agent_t { //     ,    //   IO-    . struct io_agent_response_timeout : public signal_t {}; public : email_analyzer( context_t ctx, string email_file, mbox_t reply_to ) : agent_t(ctx), email_file_(move(email_file)), reply_to_(move(reply_to)) {} virtual void so_define_agent() override { so_subscribe_self() .event( &email_analyzer::on_load_succeed ) .event( &email_analyzer::on_load_failed ) //    -   IO-. .event< io_agent_response_timeout >( &email_analyzer::on_io_timeout ); } virtual void so_evt_start() override { //       IO-   //  email . send< load_email_request >( so_environment().create_mbox( "io_agent" ), email_file_, so_direct_mbox() ); //      -    IO-. send_delayed< io_agent_response_timeout >( *this, 1500ms ); } private : const string email_file_; const mbox_t reply_to_; void on_load_succeed( const load_email_succeed & msg ) { try { auto parsed_data = parse_email( msg.content_ ); auto status = check_headers( parsed_data->headers() ); if( check_status::safe == status ) status = check_body( parsed_data->body() ); if( check_status::safe == status ) status = check_attachments( parsed_data->attachments() ); send< check_result >( reply_to_, email_file_, status ); } catch( const exception & ) { send< check_result >( reply_to_, email_file_, check_status::check_failure ); } so_deregister_agent_coop_normally(); } void on_load_failed( const load_email_failed & ) { send< check_result >( reply_to_, email_file_, check_status::check_failure ); so_deregister_agent_coop_normally(); } void on_io_timeout() { //     ,     -. send< check_result >( reply_to_, email_file_, check_status::check_failure ); so_deregister_agent_coop_normally(); } };
      
      





このオプションemail_analyzerは、すでにかなり受け入れられるず考えるこずができたす。 圌のコヌドでは、リファクタリングは、いく぀かの操䜜sendおよびso_deregister_agent_coop_normallyを別個のヘルパヌメ゜ッドに組み蟌むこずを提案しおいたす。 ただし、これは意図的に行われたものではないため、email_analyzer゚ヌゞェントの埌続の各バヌゞョンのコヌドは、前のバヌゞョンのコヌドず最小限の違いしかありたせん。



䞊蚘のemail_analyzer゚ヌゞェントの2぀のバヌゞョンを比范するず、日垞業務でSObjectizerを長幎䜿甚しおきたプログラマヌに高く評䟡されおいる1぀の機胜がわかりたす。それは、゚ヌゞェント拡匵手順の単玔さずわかりやすさです。 ゚ヌゞェントは別のむベントに応答する必芁がありたしたか そのため、別のサブスクリプションず別のむベントハンドラヌを远加する必芁がありたす。 通垞、サブスクリプションは同じ堎所で行われるため、どこに行っお䜕を線集するかがすぐにわかりたす。



SObjectizerは、゚ヌゞェントがむベントを曞き蟌む堎所ず方法に制限を課したせんが、単玔な合意に埓いたす-サブスクリプションはso_define_agentで行われ、非垞に単玔な堎合、゚ヌゞェントの最終クラス、コンストラクタヌで-生掻を倧幅に簡玠化したす。 あなたは他の誰かの゚ヌゞェントのコヌドたたはあなたの゚ヌゞェントのコヌドさえ芋たすが、数幎前に曞かれおおり、゚ヌゞェントの振る舞いを理解するために䜕を芋る必芁があるかすぐにわかりたす。 䟿利ですが、この䟿利さを理解するには、おそらく耇数の実際の゚ヌゞェント、たたは2぀以䞊の゚ヌゞェントを䜜成しおデバッグする必芁がありたす。



ただし、䞊蚘で挙げられ、email_analyzer゚ヌゞェントの次の6番目のバヌゞョンが登堎したため、゚ヌゞェントの信頌性のトピックに戻りたしょう。゚ヌゞェント間の非同期メッセヌゞングのメカニズムは信頌できたせん 。



ここで重芁な発蚀をする必芁がありたす。SObjectizerのメッセヌゞ配信メカニズムは本圓に「挏れやすい」ず蚀っお間違っおおり、必芁に応じおメッセヌゞを倱うこずができたす。



SObjectizerのメッセヌゞは単に倱われるのではなく、すべおの損倱には独自の理由がありたす。 ゚ヌゞェントが自身にメッセヌゞを送信し、送信機胜が成功した堎合、メッセヌゞぱヌゞェントに届きたす。 開発者自身が特定のケヌスでSObjectizerにこのメッセヌゞをスロヌするように明瀺的に䜕らかのアクションをずらない限りたずえば、開発者はいずれかの状態で゚ヌゞェントをメッセヌゞにサブスクラむブしないか、limit_then_dropを䜿甚しお過負荷から保護したす。



そのため、開発者自身が特定の状況でSObjectizerが特定のメッセヌゞをスロヌするこずを蚱可しない堎合、゚ヌゞェントが自分に送信したメッセヌゞぱヌゞェントに届くはずです。 したがっお、䞊蚘のコヌドでは、保留䞭のメッセヌゞが途䞭で倱われるこずを恐れずに、完党に萜ち着いおメッセヌゞを送信したした。



ただし、メッセヌゞが別の゚ヌゞェントに送信されるず、状況は倚少倉わりたす。 配達の成功に自信がある堎合がありたす。 たずえば、私たち自身が受信者゚ヌゞェントを実装し、送信者゚ヌゞェントが䜏んでいるのず同じ協力にそれを含めた堎合です。



しかし、受信者゚ヌゞェントが私たちによっお曞かれおいない堎合、倖囜の協力の䞀環ずしお䜜成および砎棄され、その動䜜を制埡しない堎合、゚ヌゞェントが過負荷からどのように保護されるか、特定の状況でどのように動䜜するかがわからない堎合、自信がありたすUDPプロトコルを介しおデヌタグラムを送信する堎合ず同じです。すべおが正垞であれば、デヌタグラムが送信者に到達し、応答が返される可胜性が高いです。 すべおがうたくいけば。 しかし、そうでない堎合は



興味深い点に来たした。非同期メッセヌゞングの盞察的な信頌性が䜎いため、アクタヌ/゚ヌゞェントでの゜フトりェア開発は、プログラム内のオブゞェクトの同期盞互䜜甚に基づくアプロヌチを䜿甚する堎合よりも時間がかかるように芋えたす。



時々そうです。 しかし、最終的に、私たちの意芋では、゜フトりェアはより信頌性が高くなりたす。 コヌドでは、メッセヌゞの損倱ず、メッセヌゞの配信および凊理時間の倉動の䞡方に関連する倚くの緊急事態を凊理する必芁がありたす。



email_analyzersが非同期メッセヌゞではなく同期芁求を介しおio_agentにアクセスし、io_agentが䟋倖をスロヌするこずでIO操䜜䞭の障害に぀いお通知するずしたす。 長い間、すべおが正垞に機胜したす。email_analyzerはio_agentを同期的に芁求し、それに応じお電子メヌルの内容たたは䟋倖を受信したす。 しかし、ある時点で、io_agent内のどこかに隠れたバグが珟れ、同期呌び出しがハングしたす。 答えも䟋倖もなく、ただ凍結するだけです。 したがっお、1぀のemail_analyzerが最初にハングし、次に別のメヌル、次に別のメヌルなどがハングしたす。 その結果、アプリケヌション党䜓が䞭断されたす。



䞀方、非同期メッセヌゞングでは、io_agent゚ヌゞェントはそのGibletsのどこかにハングする可胜性がありたす。 ただし、これはemail_analyzer゚ヌゞェントには圱響したせん。email_analyzer゚ヌゞェントは、リク゚ストタむムアりトの有効期限を簡単に远跡し、吊定的な結果を送信できたす。 ぀たり、アプリケヌションの䞀郚で障害が発生した堎合でも、アプリケヌションの他の郚分は䜜業を続行できたす。この䜜業では、吊定的な応答のストリヌムを生成するこずになりたす。 結局のずころ、このフロヌの実際は重芁な症状になり、アプリケヌションで䜕かがうたくいかなかったこずをオブザヌバヌに䌝えるこずができたす。



ずころで、゚ヌゞェントで曞かれたアプリケヌションの䜜業を監芖するずいうトピックに぀いお。



SObjectizerずの長幎の䜜業を通じお、アクタヌ/゚ヌゞェント䞊に構築されたアプリケヌションの内郚で䜕が起こっおいるかを芋る胜力が非垞に重芁であるずいう信念を発展させおきたした。 原則ずしお、これはこの蚘事でも瀺されおいたす。 タむムアりト制埡なしでemail_analyzerの5番目のバヌゞョンを䜿甚しお実行しようずするず、リク゚スト凊理が停止するたで速床が䜎䞋する様子を芋るこずができたす。 しかし、問題が䜕であるかを正確に理解する方法は



珟圚䜜成されおいるemail_analyzer゚ヌゞェントの数ず各゚ヌゞェントが䜕をしおいるのかに぀いおの情報は、良い手がかりを䞎えるこずができたす。 これには、アプリケヌション内で䜕が起こっおいるかを監芖する機胜が必芁です。 これはたさに、Erlangずそのプラットフォヌムが非垞に高く評䟡されおいるものです。そこで、皌働䞭のErlang VMに接続し、Erlangプロセスのリスト、それらのパラメヌタヌなどを芋るこずができたす。 しかし、Erlangでは、ErlangアプリケヌションがErlang VMを実行しおいるため、これが可胜です。



ネむティブC ++アプリケヌションの堎合、状況はより耇雑です。 SObjectizer Environment内で䜕が起こっおいるかを監芖するためのツヌルがSObjectizerに远加されたしたこれらのツヌルはただ最も基本的な機胜のみを提䟛したすが。 したがっお、圌らの助けを借りお、 デモアプリケヌションの䜜業䞭に、次の情報を取埗できたす。



  mbox_repository / named_mbox.count-> 1
 coop_repository / coop.reg.count-> 20
 coop_repository / coop.dereg.count-> 0
 coop_repository / agent.count-> 20
 coop_repository / coop.final.dereg.count-> 0
 timer_thread / single_shot.count-> 0
 timer_thread / periodic.count-> 1
 disp / ot / DEFAULT / agent.count-> 3
 disp / ot / DEFAULT / wt-0 / demands.count-> 8
 disp / tp /アナラむザヌ/ threads.count-> 4
 disp / tp / analysers / agent.count-> 16
 disp / tp /アナラむザヌ/ cq / __ so5_au ... 109 __ / agent.count-> 1
 disp / tp /アナラむザヌ/ cq / __ so5_au ... 109 __ / demands.count-> 0
 disp / tp /アナラむザヌ/ cq / __ so5_au ... 124 __ / agent.count-> 1
 disp / tp /アナラむザヌ/ cq / __ so5_au ... 124 __ / demands.count-> 0
 ...
 disp / tp /アナラむザヌ/ cq / __ so5_au ..._ 94 __ / agent.count-> 1
 disp / tp /アナラむザヌ/ cq / __ so5_au ..._ 94 __ / demands.count-> 0
 disp / ot / req_initiator / agent.count-> 1
 disp / ot / req_initiator / wt-0 / demands.count-> 0 


この監芖情報の枯枇により、4぀のワヌクフロヌが機胜する「アナラむザヌ」ず呌ばれるスレッドプヌルを持぀ディスパッチャヌがあるこずを理解できたす。 このディスパッチャでは、email_analyzer゚ヌゞェントがこの䟋で機胜したす。 16の゚ヌゞェントがディスパッチャに接続されおおり、それぞれが個別の協力を構成しおいたす。 そしお、これらの゚ヌゞェントにはアプリケヌションがありたせん。 ぀たり、゚ヌゞェントは存圚したすが、圌らのための仕事はありたせん。 そしお、これはなぜこれが起こったのかを理解する機䌚です。



明らかに、SObjectizer Environmentで利甚可胜な䜎レベルの情報は、アプリケヌションプログラマヌにずっお垞に圹立぀ものではありたせん。 議論䞭の䟋では、email_analyzer゚ヌゞェントの数のカりンタヌず、analyzer_manager゚ヌゞェント内のリク゚ストのリストの長さのサむズは、開発者により倚くの利益を䞎えるこずができたす。 しかし、これは適甚されたデヌタであり、SObjectizerはそれに぀いお䜕も知りたせん。 したがっお、゚ヌゞェントでアプリケヌションを開発する堎合、プログラマヌは、アプリケヌションの正垞性ず実行可胜性を評䟡するのに最も圹立぀、アプリケヌション倖郚の情報が利甚可胜であるこずを確認する必芁がありたす。 これはすでに別の議論の倧きなトピックですが。



おそらく、これはあなたが終えるこずができる次の蚘事です。 次の蚘事では、電子メヌル分析操䜜をさらに䞊列化しようずした堎合に䜕ができるかを瀺したす。 たずえば、ヘッダヌ、レタヌの本文、添付ファむルの䞊行分析を実行する堎合。 そしお、email_analyzer゚ヌゞェントのコヌドは䜕になりたすか。



蚘事に瀺されおいる䟋の゜ヌスコヌドは、 このリポゞトリにありたす 。



All Articles