[Glue!] メッセージ交換を行なうエージェント


クライアント・エージェントと交換したメッセージの内容によって動作する サーバ・エージェントのプログラムは以下のようになる:
#!/usr/local/bin/perl5

use GlueLogic;
&GlueLogicParseArgs();
&GlueLogicConnect("agent#", undef, "foobar");
&GlueLogicAccess("$GlueLogicAnchor.IN=UNBOUND", "$GlueLogicAnchor.OUT=UNBOUND");
&GlueLogicAccess("!AddInformTo $GlueLogicAnchor.IN $GlueLogicAgent/BINDING");

while (1){
    &GlueLogicWaitForMessage() unless @GlueLogicMessageQueue;
    &GlueLogicEnqueueMessage();
    next unless @GlueLogicMessageQueue;
    last unless defined ( $_ = shift(@GlueLogicMessageQueue) );
    next unless ( $_ eq "Changed $GlueLogicAnchor.IN" );
    ( $_ ) = &GlueLogicAccess( "$GlueLogicAnchor.IN?" );
    ( $vv, $v, $a ) = /^[^=]+=(.(\S+))\s*(.*)$/o;
    if ( $vv eq 'UNBOUND' ) {
	&GlueLogicAccess( "$GlueLogicAnchor.OUT=UNBOUND" ); next;
    }
    $OUT = "Unknown Message Selector";
    if    ( $v eq 'a' )   { $OUT = &ProcessEventA ($a); }
    elsif ( $v eq 'bb' )  { $OUT = &ProcessEventBB($a); }
    elsif ( $v eq 'zzz' ) { last; }
    &GlueLogicAccess( "$GlueLogicAnchor.OUT=\"$OUT" );
}

&GlueLogicAccess("!DelInformTo $GlueLogicAnchor.IN $GlueLogicAgent/BINDING");
&GlueLogicDisconnect();
exit 0;


メッセージ交換のプロトコル

上記の例では、Anchor.IN にリクエストを含むメッセージを受け付け、 そのリクエストに対する回答メッセージを Anchor.OUT に返す、 というプロトコルに基づいている。
また、この時、メッセージや戻り値を書き込む前には、 必ずその値が UNBOUND になっていることを確認することを原則とする。 これによって、排他制御を行なうことができ、 ひとつのサーバ・エージェントが複数のクライアント・エージェントにサービスする場合であっても 適切に競合を避けることができる。
クライアント・エージェントが このプロトコルを守ることを確実にするため、 変更通知の発信モードを BINDING にして、 Anchor.IN の値が UNBOUND とそれ以外との間で変化した場合にのみ、 変更通知メッセージを送るように要求している。 このため、 Anchor.IN の値が UNBOUND を経ずに変化した場合、 サーバ・エージェントはその変化を検出できず、 Anchor.OUT の値も変更しない。

  1. このサーバ・エージェントに対してメッセージを送るエージェントは、 Anchor.INAnchor.OUT との 両方の値が UNBOUND になっていることを確認した上で、 Anchor.IN にメッセージを書き込み、 Anchor.OUT に戻り値が書き込まれて UNBOUND 以外の値になるのを待ち合わせる。
  2. サーバ・エージェントはこれに対して Anchor.IN に書き込まれたメッセージを読み込み、 それに対応する method を起動して処理を行なった後、 Anchor.OUT に戻り値を書き込む。
  3. Anchor.OUTUNBOUND 以外の値になったら、 メッセージを送ったエージェントはその値を取り込んだ後、 Anchor.INUNBOUND を書き込むことによって、 サーバ・エージェントに対して作業結果の確認が完了したことを伝える。
  4. サーバ・エージェントはこれに対して Anchor.OUTUNBOUND を書き込み、 次のメッセージを受け付けられる状態になったことを示す。


対応するクライアント・エージェントの書き方

以下のようにするとうまく行くと思うが、まだ試していない。


&GlueLogicAccess("!AddInformTo $Target.IN $GlueLogicAgent/BINDING");
&GlueLogicAccess("!AddInformTo $Target.OUT $GlueLogicAgent/BINDING");
{
    @ans = &GlueLogicAccess( "$Target.IN:UNBOUND", "$Target.OUT:UNBOUND",
                             "$Target.IN=\"$MessageText" );
    last unless $ans[0] =~ /^!/o;
    &GlueLogicWaitForChanged( "$Target.IN", "$Target.OUT" ) || die( "Disconnected\n" );
    &GlueLogicDequeueChanged( "$Target.IN", "$Target.OUT" );
    redo;
}

&GlueLogicWaitForChanged( "$Target.OUT" ) || die( "Disconnected\n" );
&GlueLogicDequeueChanged( "$Target.IN", "$Target.OUT" );
( $ReturnValue ) = &GlueLogicAccess( "$Target.OUT?" );
&GlueLogicAccess("!DelInformTo $Target.IN $GlueLogicAgent/BINDING");
&GlueLogicAccess("!DelInformTo $Target.OUT $GlueLogicAgent/BINDING");
&GlueLogicAccess( "$Target.IN=UNBOUND" );
$ReturnValue =~ s/^[^=]+="//o;

但し、ごく稀に、原因は不確定ながらおそらくは UNIX と TCP/IP のバッファリングの影響で、 サーバから送られた変更通知メッセージが エージェントまでの通信路のどこかに引っかかってしまうことがある。
通常は、 これに続いて他の名前に関する通知メッセージが同じエージェントに送られると、 滞っていたメッセージも一緒に順番が狂うことなく受信することができる。 しかし、 エージェントの状態によっては、 ただ一つの変更通知メッセージしか発生し得ないケースがあり、 その唯一のメッセージが滞ってしまった場合にはデッド・ロックを起こすことになる。
このような現象は、現在のインプリメンテーションでは避けようがないので、 その場合にはポーリングを併用しなければならない。


 [M.T. HomePage]  [written & copyrighted by Masayuki Takata]