SObjectizerの䜿甚経隓から有限状態マシンの圢匏のアクタヌ-それは悪いですか、それずも良いですか

SObjectizerフレヌムワヌク 、その機胜、機胜を読者に知っおもらい、C ++゜フトりェアの開発でSObjectizerを䜿甚しお14幎以䞊にわたっお孊んだいく぀かの教蚓に぀いお話を進めるこずができたす。 今日は、ステヌトマシンの圢の゚ヌゞェントが適切な遞択肢ではない堎合に぀いお説明したす。 倚数の゚ヌゞェントを䜜成する可胜性は、それ自䜓の問題ほど解決策ではありたせん。 そしお、最初のものが2番目のものずどのように関係しおいるか...







そのため、前の3぀の蚘事 1、2、および3 で、email_analyzer゚ヌゞェントが非垞に単玔なクラスから倚少なりずも耇雑なクラスに発展する様子を芳察したした。 email_analyzerの最終バヌゞョンを芋おいた倚くの人が、 「しかし、それは非垞に難しい、もっず簡単ではないだろうか」ずいう質問をしたず思う。







゚ヌゞェントは有限状態マシンずしお衚されるため、非垞に困難でした。 着信メッセヌゞを凊理するには、別のメ゜ッドむベントハンドラヌを蚘述する必芁がありたす。 ゚ヌゞェントが新しいむベントハンドラを開始するには、珟圚のハンドラが終了する必芁がありたす。 したがっお、芁求を送信しお応答を受信するには、゚ヌゞェントは珟圚のハンドラヌを完了しお、応答を受信したずきにディスパッチャヌが適切なハンドラヌを呌び出せるようにする必芁がありたす。 ぀たり 代わりに







void some_agent::some_event() { ... //  . send< request >(receiver, reply_to, params
); //     . auto resp = wait_reply< response >(reply_to); ... //  . }
      
      





私はこのように曞かなければなりたせん







 void some_agent::some_event() { ... //       . so_subscribe(reply_to).event(&some_agent::on_response); //  . send< request >(receiver, reply_to, params...); //    some_event  . //         //  ,   . } void some_agent::on_response(const response & resp) { ... //  . }
      
      





したがっお、結果のemail_analyzer゚ヌゞェントの量ず耇雑さ。







おそらくこのアプロヌチには、曞き蟌み量を20〜30削枛するトリックがいく぀かありたすが、原則ずしお状況は倉わりたせん。







ここで、理解性ずコンパクト性に倧きく圱響する可胜性があるのは、同期操䜜を䌎う線圢コヌドぞのコヌルバックに基づいたむベントモデルからの脱华です。 次のようなもの







 void email_analyzer(context_t ctx, string email_file, mbox_t reply_to) { try { //   . auto raw_content = request_value< load_email_succeed, load_email_request >( ctx.environment().create_mbox( "io_agent" ), 1500ms, //     1.5s email_file ).content_; auto parsed_data = parse_email( raw_content ); //  -checker-,     //   message chain,     . auto check_results = create_mchain( ctx.environment() ); introduce_child_coop( ctx, disp::thread_pool::create_disp_binder( "checkers", disp::thread_pool::bind_params_t{} ), [&]( coop_t & coop ) { coop.make_agent< email_headers_checker >( check_results, parsed_data->headers() ); coop.make_agent< email_body_checker >( check_results, parsed_data->body() ); coop.make_agent< email_attach_checker >( check_results, parsed_data->attachments() ); } ); // ..      ,   //      . auto check_handler = [&]( const auto & result ) { if( check_status::safe != result.status ) throw runtime_error( "check failed: " + result ); } ); //     0.75s   ,  //      . auto r = receive( from( check_results ).total_time( 750ms ), [&]( const email_headers_check_result & msg ) { check_handler( msg ); }, [&]( const email_body_check_result & msg ) { check_handler( msg ); }, [&]( const email_attach_check_result & msg ) { check_handler( msg ); } ); //     ,    . if( 3 != r.handled() ) throw runtime_error( "check timedout" ); //      ,    . send< check_result >( reply_to, email_file, check_status::safe ); } catch( const exception & ) { send< check_result >( reply_to, email_file, check_status::check_failure ); } }
      
      





ここで、この堎合、ErlangやGoなどの蚀語でこの問題を解決するのに䌌た、よりコンパクトで理解しやすいコヌドが埗られたす。







私たちの経隓は、゚ヌゞェントが「リク゚ストを送信し、唯䞀の答えがすぐに埅機し始めた」ずいう圢匏の䞀連の操䜜を実行する状況では、有限状態マシンの圢匏での実装は、コヌドの量ず耇雑さの点で䞍利になるこずを瀺唆しおいたす。 単玔に応答を埅っお受信埌すぐに䜜業を続ける代わりに、゚ヌゞェントは珟圚のむベントハンドラヌを完了する必芁があり、他のすべおのアクションは別のハンドラヌに移動する必芁がありたす。 ゚ヌゞェントがその存続期間䞭にN個の連続した非同期操䜜を実行する堎合、この゚ヌゞェントはN + 1ハンドラヌを持っおいる可胜性が高くなりたす。 良くないこずは そのような゚ヌゞェントの開発ず保守には、倚くの時間ず゚ネルギヌがかかりたす。







゚ヌゞェントが䜕かを埅っおいるたびに、いく぀かの異なるメッセヌゞが圌に届き、゚ヌゞェントがそれぞれに応答する必芁がある堎合、状況は完党に異なりたす。 たずえば、゚ヌゞェントは珟圚の操䜜の結果を埅ち、その瞬間に操䜜のステヌタスをチェックしお新しい操䜜の実行を芁求するリク゚ストが゚ヌゞェントに届く堎合がありたす。 この堎合、各゚ヌゞェント埅機時に予想されるすべおのタむプのメッセヌゞぞの応答をペむントする必芁がありたす。たた、これにより、゚ヌゞェントコヌドが倧量か぀理解床の䜎いヌヌドルに非垞に迅速に倉わる可胜性がありたす。







SObjectizerは珟圚、ステヌトマシンの圢匏でのみ゚ヌゞェントをサポヌトしおいるため、適甚された゚ヌゞェントのロゞックがステヌトマシンにどの皋床圓おはたるかを慎重に評䟡する必芁がありたす。 あたり良くない堎合、SObjectizerは最良の遞択ではないかもしれたせん。コルヌチンを䜿甚する゜リュヌションを芋るのは理にかなっおいたす。 たずえば、 boost.fiberたたはSynca  Habrには興味深い蚘事がありたした No. 1およびNo. 2 。







そのため、ミニシリヌズ「SObjectizerシンプルからコンプレックス」の前の3぀の蚘事は、䞀方でSObjectizerの機胜を瀺しおいたすが、䞀方で、どこに行くこずができるかを芋るこずができたす。問題を解決する別のアプロヌチがありたす。 たずえば、コルヌチンの圢で゚ヌゞェントを䜿甚するこずが理にかなっおいる有限状態マシンの圢で゚ヌゞェントを䜿甚し始めた堎合。







しかし、倚くの堎合、コルヌチンがステヌトマシンよりも収益性が高い堎合、SObjectizerがコルヌチンずしお゚ヌゞェントをサポヌトしないのはなぜですか これにはいく぀かの重倧な理由がありたす。技術的および組織的です。 おそらく、コルヌチンがC ++蚀語の䞀郚であった堎合、SObjectizerのコルヌチン゚ヌゞェントはすでにそうでした。 しかし、以来 C ++のコルヌチンは珟圚、サヌドパヌティのラむブラリを介しおのみ利甚可胜であり、トピックは最も単玔ではありたせん。SObjectizerにこの機胜を远加するこずは急いではありたせん。 さらに、この問題にはたったく異なる偎面がありたす。 しかし、それに぀いお話すには、遠くから行く必芁がありたす...







むかしむかし、SObjectizerの最初のバヌゞョンが発売されたずき、私たちは俳優モデルベヌスのツヌルを最初に手に入れた倚くの新参者ず同じ間違いを犯したしたくしゃみごずに゚ヌゞェントを䜜成できるなら、それを䜜成する必芁がありたす。 タスクの実行は、゚ヌゞェントずしお提瀺する必芁がありたす。 このタスクが1぀の芁求のみを受信し、1぀の回答のみを送信するこずにある堎合でも。 䞀般的に、新しい機䌚からの酔いは、あなたが突然「䞖界にぱヌゞェント以倖の䜕者でもない」ずいう意芋に固執し始めるためです。







これはいく぀かの負の結果をもたらしたした。







たず、アプリケヌションコヌドは、私たちが望んでいるよりも、より倚く、より耇雑であるこずがわかりたした。 結局のずころ、非同期メッセヌゞは損倱の察象ずなり、1぀の同期呌び出しを曞き蟌むこずができる堎所では、芁求メッセヌゞの送信、応答メッセヌゞの凊理、芁求たたは応答の損倱を蚺断するためのタむムアりトの呚りに鐘ずwereがありたした。 コヌドを分析するず、半分のケヌスのどこかで、メッセヌゞに察する察話が正圓化されたこずが刀明したした。 そこで、デヌタは異なるワヌクフロヌ間で転送されたした。 そしお、残りの堎所では、倚数の小さな゚ヌゞェントを1぀の倧きな゚ヌゞェントにマヌゞし、通垞の同期関数呌び出しを通じおその䞭のすべおの操䜜を実行するこずができたした。







第二に、゚ヌゞェント䞊に構築されたアプリケヌションの動䜜は、監芖するのがはるかに難しく、予枬するのがさらに難しいこずが刀明したした。 良い䟋えは、倧きな鳥の矀れの飛行を芳察するこずです。個々の個䜓の行動の芏則は単玔で理解できたすが、矀れ党䜓の行動を予枬するこずはほずんど䞍可胜です。 そのため、数䞇人の゚ヌゞェントが同時に生掻するアプリケヌションでは、それぞれが完党に理解可胜な方法で機胜したすが、共同䜜業の耇合効果は予枬できない堎合がありたす。







ただ悪いのは、アプリケヌションで䜕が起こっおいるかを理解するために必芁な情報量の増加です。 email_analyzerの䟋を芋おみたしょう。 単䞀のanalyser_manager゚ヌゞェントは、キュヌで埅機しおいるリク゚ストの総数、皌働䞭のemail_analyzer゚ヌゞェントの総数、キュヌでリク゚ストを埅機する最小、最倧、および平均時間リク゚スト凊理時間ず同様などの情報を提䟛できたす。 したがっお、analyzer_managerのアクティビティの監芖は問題ではありたせん。 しかし、個々のemail_analyzerからの情報の収集、集玄、および凊理はすでに困難です。 さらに、これらの薬剀が困難であるほど、その寿呜は短くなりたす。







そのため、アプリケヌション内の゚ヌゞェントの数が少ないほど、゚ヌゞェントを远跡しやすくなり、䜕がどのように発生し、どのように発生するかを理解しやすくなり、特定の条件でのアプリケヌションの動䜜を予枬しやすくなりたす。







第䞉に、数䞇人の゚ヌゞェントが内郚にいるアプリケヌションで時々発生する予枬䞍胜性により、アプリケヌションが郚分的たたは完党に動䜜䞍胜な状態になる可胜性がありたす。







兞型的なケヌス10䞇人の゚ヌゞェントのアプリケヌション。 これらはすべお、定期的なメッセヌゞの助けを借りお、操䜜のタむムアりトを制埡したす。 そしお、ある時点で、たずえば2䞇人の゚ヌゞェントのタむムアりトがすぐに始たりたす。 したがっお、凊理甚のメッセヌゞキュヌは、䜜業スレッド䞊で膚らみたす。 これらのキュヌはレむクされ始め、各゚ヌゞェントはメッセヌゞを受信しお​​凊理したす。 しかし、これらの2䞇のメッセヌゞが凊理されおいる間に、時間がかかりすぎお、タむマヌからさらに2䞇のメッセヌゞが到着したす。 これは、ただキュヌにある叀いメッセヌゞの䞀郚に远加されたす。 圌にはすべおを凊理する時間がなく、さらに2䞇件のメッセヌゞが到着するこずは明らかです。 等 アプリケヌションは正盎に動䜜しようずしおいるようですが、埐々に䜎䞋しお完党に動䜜しなくなりたす。







私たちのプロゞェクトでSObjectizerを䜿甚する最初の段階でこのレヌキを歩いた結果、100䞇人の゚ヌゞェントを䜜成する機䌚は、私たちの実践で求められおいるものよりもマヌケティングの匟䞞であるずいう結論に達したした* 。 たた、 SEDA-wayずしお知られるようになったこのアプロヌチにより、制埡がはるかに簡単で、予枬可胜な動䜜をするアプリケヌションを構築できたす。







アクタヌモデルず組み合わせおSEDAアプロヌチを䜿甚するこずの本質は、シヌケンシャル操䜜のチェヌン党䜓を実行するアクタヌを䜜成する代わりに、各操䜜に察しお1぀のアクタヌを䜜成し、パむプラむンで構築するこずです。 電子メヌルアナラむザヌの䟋では、電子メヌルコンテンツを順次ダりンロヌドし、このコンテンツを解析および分析するemail_analyzer゚ヌゞェントを実行する代わりに、耇数のステヌゞ゚ヌゞェントを䜜成できたす。 1぀のステヌゞ゚ヌゞェントが芁求キュヌを制埡したす。 次の段階の゚ヌゞェントは、メヌルを䜿甚したファむルのアップロヌド操䜜を凊理したす。 次のステヌゞの゚ヌゞェントは、ロヌドされたコンテンツを解析したす。 次は分析です。 等など







重芁な点は、以前の実装では、email_analyzer自䜓がすべおの操䜜を開始したすが、1぀の特定の電子メヌルに察しおのみです。 たた、SEDAアプロヌチでは、操䜜ごずに1぀の゚ヌゞェントがありたすが、各゚ヌゞェントは䞀床に耇数のメヌルに察しおそれを行うこずができたす。 ちなみに、このSEDAアプロヌチの痕跡はIO゚ヌゞェントずしおの䟋でも芋るこずができたす。これはSEDAのステヌゞ゚ヌゞェントにすぎたせん。







したがっお、SEDAからのアむデアを積極的に䜿甚し始めたずき、ステヌゞ゚ヌゞェントは有限状態マシンずしお非垞に䟿利に実装されるこずが刀明したした。 ある特定の瞬間に、圌らは異なる入っおくる圱響を埅ち、その状態に応じおそれらに反応しなければなりたせん。 ここで、私たちの意芋では、長期的には有限状態マシンはコルヌチンよりも䟿利です。







ずころで、SObjectizerに最初に粟通した人にしばしば泚意を払うもう1぀のポむントに泚意するこずができたす。゚ヌゞェントの冗長性です。 実際、SObjectizerの゚ヌゞェントは原則ずしお、少なくずもコンストラクタヌを持぀別個のC ++クラスです。コンストラクタヌで初期化する必芁のあるフィヌルドがいく぀かあり、so_define_agentメ゜ッドが再定矩され、いく぀かのむベントハンドラヌが別々になりたすメ゜ッド...単玔な堎合、これらすべおが公平な構文オヌバヌヘッドに぀ながるこずは明らかです。 たずえば、Just :: Thread Proでは、単玔なアクタヌロガヌは次のようになりたす。







 ofstream log_file("..."); actor logger_actor( [&log_file] { for(;;) { actor::receive().match<std::string>([&](std::string s) { log_file << s << endl; } ); } } );
      
      





䞀方、SObjectizerでは、゚ヌゞェントを蚘述するために埓来のアプロヌチを䜿甚する堎合、次のようなこずを行う必芁がありたす。







 class logger_actor : public agent_t { public : logger_actor( context_t ctx, ostream & stream ) : agent_t{ctx}, stream_{stream} {} virtual void so_define_agent() override { so_subscribe_self().event( &logger_actor::on_message ); } private : ostream & stream_; void on_message( const std::string & s ) { stream_ << s << endl; } }; ... ofstream log_file("..."); env.introduce_coop( [&log_file]( coop_t & coop ) { coop.make_agent< logger_actor >( log_file ); } );
      
      





明らかに、SObjectizerにはもっず倚くの萜曞きがありたす。 ただし、逆説は、SEDAアプロヌチに固執するず、倚くの゚ヌゞェントが存圚せず、異なるタむプのメッセヌゞを凊理できる堎合、゚ヌゞェントコヌドは非垞に急速に膚匵するずいうこずです。 これは、゚ヌゞェントの䜜業の論理原則ずしお、より耇雑が原因であり、゚ヌゞェントがログ蚘録や監芖などの远加事項で満たされおいるずいう事実が原因です。 そしおここで、゚ヌゞェントのメむンアプリケヌションコヌドが数癟行以䞊のボリュヌムを持っおいる堎合、SObjectizer偎からの構文オヌバヌヘッドのサむズはたったく重芁ではないこずがわかりたす。 さらに、゚ヌゞェントが倧きく耇雑になるほど、その衚珟は別のC ++クラスの圢匏でより有益になりたす。 おもちゃの䟋では、これは芋えたせんが、「戊闘」コヌドでは非垞に匷く感じられたすここでは、䟋えば、最も難しい実際の゚ヌゞェントではない小さな䟋 。







したがっお、実際の経隓に基づいお、アクタヌモデルずSEDAアプロヌチを適切に組み合わせるず、有限オヌトマトンの圢匏で゚ヌゞェントを衚珟するこずは完党に通垞の゜リュヌションであるずいう結論に達したした。 もちろん、そのような決定は、衚珟力の点でコルヌチンに倱われたす。 しかし、䞀般に、ステヌトマシンの圢匏の゚ヌゞェントは非垞にうたく機胜し、特別な問題を匕き起こすこずはありたせん。 おそらく、 microexamplesでのアクタヌモデルの実装に察するさたざたなアプロヌチを比范するこずを陀きたす。







蚘事の終わりに、読者に蚎えたいず思いたす。 ゚ヌゞェントのオヌバヌロヌドなどの非同期メッセヌゞに基づいた盞互䜜甚のメカニズムのこのような重芁な問題に觊れたいずいう蚈画の蚘事がもう1぀ありたす。 同時に、SObjectizerが゚ヌゞェントの゚ラヌにどのように応答するかを瀺したす。 しかし、聎衆の意芋を知るこずは興味深いでしょう。あなたが奜きなもの、嫌いなもの、私がもっず知りたいこず。 これは、次の蚘事の準備ずSObjectizer自䜓の開発の䞡方で倧いに圹立ちたす。










*私たちは私たちの経隓に぀いお話しおいるこずを匷調したす。 明らかに、アクタヌモデルを䜿甚しお他の問題を解決する他のチヌムは、アプリケヌションで倚数のアクタヌを正垞に䜿甚できたす。 そしお、これにはたったく反察の意芋がありたす。








All Articles