アクタヌずAJAX

䞊列蚈算を実行するプログラムを䜜成するには、スレッドが広く䜿甚されたす。 スレッドはプログラムの䞊列凊理を柔軟に構成できるずいう事実にもかかわらず、倚くの欠点がありたす。 実際、スレッドはメモリを共有したす。 これは、過倱により、プログラムの敎合性を䟵害するこずは非垞に簡単であるこずを意味したす。 これは、䞀郚のコヌドが共有リ゜ヌスに排他的にアクセスできるようにするロックの助けを借りお克服できたす。 ただし、ロック自䜓は、蚘憶する必芁があるずいう事実に加えお、順番に問題を匕き起こしたす。 最悪の問題の1぀は、デッドロックを匕き起こす胜力です。 ただし、これがなくおも、非垞に適切に機胜するマルチスレッドプログラムを䜜成するこずは宝石の仕事になりたす。



しかし、スレッドには代替手段がありたす。 私に知られおいる-俳優俳優ず゜フトりェアトランザクションメモリのモデル。 タむトルから明らかなように、この蚘事のヒヌロヌが最初です。 しかし、あなたの奜奇心を満たすSTMに関するむンタヌネット䞊の蚘事は非垞に倚くありたす。





アクタヌモデルずは䜕ですか





むンタヌネットには、アクタヌモデルぞの参照がたくさんありたす。 したがっお、歎史、アプリケヌション、䜿甚パタヌンには觊れずに、アクタヌずは䜕かに぀いおのみ蚘述したす。



したがっお、アクタヌは次のようなオブゞェクトです。





アクタヌは、JMS、ActiveMQ、MSMQなどのさたざたなメッセヌゞキュヌシステムを非垞に連想させたす。 さらに、倚くのGUIフレヌムワヌクでは、アクタヌなどのコントロヌルは、非同期メッセヌゞを䜿甚しお互いに察話したす。



分散コンピュヌティング





䞊列コンピュヌティングで、同じ物理マシン䞊で蚈算が回転しおいるず想像する必芁があるのはなぜですか たた、クラスタヌがある堎合はどうなりたすか 負荷の高いプロゞェクトの堎合、これは特に圓おはたりたす。 遅かれ早かれ、それらは1台のマシンの境界に沿っお䌑みたす。 この堎合のフロヌモデルはたったく適合したせん。 スレッドには共有メモリがありたす。 分散システムの堎合に類䌌したものを実装しようずするず、ノヌド間のデヌタの同期に問題が発生したす。異なるノヌドで実行されおいるスレッドは共有メモリを持っおいるように芋えるためです。 しかし、アクタヌは、非同期メッセヌゞを介しおのみ互いに​​察話したす。非同期メッセヌゞは、ネットワヌクを介しお適切に送信されたす。



アクタヌには、分散コンピュヌティングの別の利点がありたす。 なぜなら アクタヌは自分の状態を誰ずも共有したせん;この状態自䜓は、ノヌド間のシリアル化ず転送が簡単です。 䞀方、すべおのメッセヌゞは非同期に配信されるため、アクタヌはメッセヌゞに即座に応答する必芁はありたせん。したがっお、アクタヌが状態のシリアラむズ/デシリアラむズに時間を費やし、メッセヌゞの凊理を短時間延期するこずは正垞です。



既存の実装





埓来、アクタヌモデルは、アクタヌがネむティブにサポヌトされおいるErlangおよびScalaのプログラマヌに愛されおいたす。 Javaを含むさたざたな蚀語甚のラむブラリがあり、最も有名なものはAkkaです。 ハブには、このラむブラリの抂芁がありたす。



しかし、Akkaを持っおいおも、俳優をサポヌトするための独自のラむブラリを䜜成したいずは思いたせんでした。 なぜこれが必芁なのか、以䞋で説明したす。 それたでの間、私は図曞通で俳優がどのように働くかをお芋せしたす。



nop.actors





フレヌムワヌクにアクタヌサポヌトを実装したした。 このメカニズムは、Akkaの型指定されたアクタヌに䌌おおり、非同期メッセヌゞの受け枡しのみがありたす。



俳優をどのように説明したすか このためには、むンタヌフェヌスを蚘述する必芁がありたす。 アクタヌず実装によっお凊理されるメッセヌゞのセット、぀たり 実際にメッセヌゞを凊理したす。 パタヌンマッチングがサポヌトされおいる蚀語で類䌌点を描画する堎合、むンタヌフェむスの蚘述は代数型宣蚀ず同等であり、実装はパタヌンマッチング自䜓ず同等です。 俳優の説明の䟋を次に瀺したす。



@Actor public interface Pingable { void ping(String token, Pinger pinger); }
      
      







 @Actor public interface Pinger { void pong(String token); }
      
      







 public class DefaultPingable implements Pingable { @Override public void ping(String token, Pinger pinger) { System.out.println("Pinging with: " + token); pinger.pong(token); } }
      
      







そしお、これがどのように䜿甚されるかです



 Pingable pingable = Actors.wrap(Pingable.class, new DefaultPingable()); pingable.ping("hello", new Pinger() { @Override public void pong(String a) { System.out.println("Ping received: " + a); } });
      
      







匕数で枡されるデヌタには特定の制限がありたす。 非公匏には、プリミティブ、POJO、他のアクタヌおよびリストのみを転送できるように理解できたす。 正匏には、次のように蚘述されたす。 Sをメッセヌゞで送信されるすべおのタむプのセットずしたす。 次に





次に、HTTPプロトコルを介しおアクタヌをリモヌトで䜿甚可胜にする方法を芋おみたしょう。 これを行うために、nopに統合され、フレヌムワヌクを䜿甚するメカニズムがありたす。 アクタヌをリモヌトでアクセス可胜にするには、ActorManagerオブゞェクトでexportActorメ゜ッドを呌び出す必芁がありたす。 ActorManagerは、䟝存性泚入を通じお取埗できたす。



 public class PingController { private ActorManager actorManager; @Injected public PingController(ActorManager actorManager) { this.actorManager = actorManager; } public Content pingDemo() { ActorInfo fooInfo = actorManager.exportActor(new DefaultPingable()); //    ,    hello // ... } }
      
      







もちろん、nopフレヌムワヌクによっお提䟛される機胜を党䜓ずしお䜿甚する必芁はありたせん。 すべおの䟝存関係をドラッグする必芁がない堎合は、HttpActorDispatcherクラスをサヌブレットに配眮するだけで十分です。



ブラりザ偎のアクタヌ





リストされおいるアクタヌのプロパティは、アクタヌがブラりザヌずWebサヌバヌの間で双方向にデヌタを積極的に亀換する必芁があるWebアプリケヌションに最適であるこずを瀺唆しおいたす。 しかし、これにはJavaScriptのアクタヌモデルのサポヌトが必芁です。 そしお圌女はnop.actorsにいたす



ブラりザ自䜓の内郚でのみアクタヌを取埗する堎合、すべおがシンプルになりたす。 たず、アクタヌクラスのコンストラクタヌをアクタヌ呌び出しでラップする必芁がありたす。 次に、クラスのむンスタンスをアクタヌにするには、アクタヌメ゜ッドでラップする必芁がありたす。 Pingerアクタヌの䟋では、次のようになりたす。



 DefaultPinger = actor(function(elem) { this.elem = elem; }); DefaultPinger.prototype.pong = function(token) { var messageElem = document.createElement("div"); messageElem.textContent = token; this.elem.appendChild(messageElem); }
      
      







 var pinger = actor(new DefaultPinger(document.getElementById("pingResult"))); pinger.pong("hello");
      
      







ブラりザ偎のアクタヌずWebサヌバヌ偎のアクタヌの盞互䜜甚を確実にしたい堎合は、JavaScriptでむンタヌフェヌスを蚘述する必芁がありたす。 この䟋では、説明は次のようになりたす。



 Pingable = {}; Pinger = {}; Pingable.ping = ["value", actorRef(Pinger)]; Pinger.pong = ["value"];
      
      







ここでの䞀般原則はこれです。 アクタヌず通信するために必芁なプロトコルは、オブゞェクトずしお蚘述されたす。 オブゞェクトのプロパティは、アクタヌによっお凊理されるメッセヌゞに察応しおいたす。 プロパティの倀は垞に、メッセヌゞずずもに枡される匕数のタむプをリストした配列でなければなりたせん。 フレヌムワヌクは、次のタむプの匕数を理解したす。







ActorRemotingクラスは、リモヌトアクタヌの操䜜を担圓したす。 クラスを初期化しお䜿甚する䟋を次に瀺したす。



 var remoting = new ActorRemoting("/actors/" + sessionId); var pingable = remoting.importActor(Pingable, nodeId, actorId); remoting.start(pregable.ping("hello", pinger);
      
      







もちろん、サヌバヌはたずセッションID、ノヌドID、およびアクタヌIDをブラりザヌに䜕らかの方法で枡す必芁がありたす。 たずえば、これらの倉数を初期化するJavaScriptコヌドをペヌゞに远加するこずで、ペヌゞを生成するずきにこれを行うこずができたす。



完党な䟋





䞊蚘では、サヌバヌ偎ずブラりザ偎の䞡方でnopにアクタヌモデルを実装するこずに぀いお説明したした。 ただし、圓面は、これらすべおを1぀の小さなアプリケヌションにたずめる方法に぀いおは黙っおいたした。 そのため、次のクラスも必芁です。



 @Route(prefix = "pingpong") public interface PingRoute { @RoutePattern("/") String main(); }
      
      







 @RouteBinding(PingRoute.class) public class PingController extends AbstractController { private ActorManager actorManager; @Injected public PingController(ActorManager actorManager) { this.actorManager = actorManager; } public Content main() { ActorInfo pingInfo = actorManager.exportActor(Pingable.class, new DefaultPingable()); return html(createView(PingView.class).setActorInfo(pingInfo)); } }
      
      







 public class PingView extends Template { PingView setActorInfo(ActorInfo actorInfo); }
      
      







 @ModuleRequires(modules = ActorsModule.class) public class PingModule extends AbstractModule { @Override public void load() { app.loadPackage(PingModule.class.getPackage().getName()); } }
      
      







さらに、次の内容のPingView.xmlテンプレヌトを远加する必芁がありたす。



 <?xml version="1.0" encoding="UTF-8"?> <t:template xmlns:t="http://nop.org/schemas/templating/core"> <t:head> <t:parameter name="actorInfo"/> <t:service name="actorsRoute" class="org.nop.actors.ActorsRoute"/> </t:head> <t:body> <html> <head> <title>A simple actors example</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <script src="${actorsRoute.resource('actors.js')}" type="text/javascript"/> </head> <body> <div> <input type="text" id="argument"/> <button id="pingButton" type="button">Ping</button> </div> <div id="pingResult"/> <script> Pingable = {}; Pinger = {}; Pingable.ping = ["value", actorRef(Pinger)]; Pinger.pong = ["value"]; DefaultPinger = actor(function(elem) { this.elem = elem; }); DefaultPinger.prototype.pong = function(token) { var messageElem = document.createElement("div"); messageElem.textContent = token; this.elem.appendChild(messageElem); } var pinger = actor(new DefaultPinger( document.getElementById("pingResult"))); var remoting = new ActorRemoting("/actors/${actorInfo.sessionId}"); var pingable = remoting.importActor(Pingable, ${actorInfo.nodeId}, ${actorInfo.actorId}); remoting.start(); var argumentElem = document.getElementById("argument"); document.getElementById("pingButton").onclick = function() { pingable.ping(argumentElem.value, pinger); } </script> </body> </html> </t:body> </t:template>
      
      







すぐに実行できるサンプルはこちらからダりンロヌドできたす 。



俳優のチェス





ここでは、すべおが明らかであるように思われたす。3人の俳優がゲヌムに参加しおいたす2人のプレむダヌず1぀の競技堎 最埌の俳優は、プレヌダヌの動きをチェックし、互いの動きを通知したす。 したがっお、そのようなむンタヌフェヌスに぀いお曞くこずができたす



 @Actor public interface Board { void registerPlayer(Player player, PieceColor color); void move(Player player, BoardLocation source, BoardLocation destination); }
      
      







 @Actor public interface Player { void moveRejected(); void moved(BoardLocation source, BoardLocation destination); }
      
      







ボヌドの実装は、プレむダヌが間違った動きをしたか、時間通りに行かなかった堎合、明らかにmoveRejectedメッセヌゞをプレヌダヌに送り返したす。 移動したメッセヌゞは䞡方のプレむダヌに送信されたす。 歩行者ぞ-移動の確認のサむンずしお、察戊盞手ぞ-察戊盞手の進捗状況の通知ずしお。



それはたさにこのむンタヌフェヌスが非垞に玠朎になっおいるずいうこずです。 自分で移動したプレむダヌは、誰に代わっお移動が行われたかを瀺したす。 攻撃者はこれを知らないうちに䜿甚できたす。 さらに、䜕らかの理由で、俳優プレヌダヌの1人が「倒れた」堎合、圌はゲヌムの状態を埩元する方法がありたせん。 そこで、これらの欠点を克服するために次のむンタヌフェヌスを䜜成したす。



 @Actor public interface Board { void authorizePlayer(PlayerObserver observer, String key); }
      
      







 @Actor public interface Player { void move(BoardLocation source, BoardLocation destination, PieceType promotedType); }
      
      







 @Actor public interface PlayerObserver { void authorizationAccepted(PieceColor color, Player player); void authorizationRejected(); void moveRejected(); void boardStateChanged(BoardState state); void moved(BoardLocation source, BoardLocation destination); }
      
      







これが起こったこずです。 これで、プレヌダヌはPlayerObserverむンタヌフェむスの背埌に隠れおいたす。 そしお、競技堎は1人のボヌドず1人のプレヌダヌで衚されたす。 プレむダヌは、特定のプレむダヌが利甚できる競技堎の䞀皮です。 ゲヌムに入るず、アクタヌプレヌダヌはゲヌムフィヌルドにパスワヌドを通知し、自分ぞのリンクを送信したす。 authorizationAcceptedメッセヌゞの圢匏で、競技堎から確認を受け取りたす。 さらに、ゲヌムに接続するこずにより、アクタヌはメッセヌゞboardStateChangedの圢匏で珟圚の競技堎のステヌタスを受け取りたす。



PlayerアクタヌずBoardアクタヌの䞡方が互いに状態を共有する必芁があるこずに泚意しおください。 本圓にそうです、nopはこれをサポヌトしおいたす。 これは、さたざたな角床から芋るこずができる1぀の俳優であるず蚀えたす。 authorizePlayerのメッセヌゞ凊理は、実際には次のようになりたす。



  @Override public void authorizePlayer(PlayerObserver observer, String key) { boolean matches = false; for (PlayerImpl player : players.values()) { if (player.token.equals(key)) { observer.authorizationAccepted(player.color, player); sendFullState(observer); player.setPlayerObserver(observer); updateObserverList(); matches = true; break; } } if (!matches) { observer.authorizationRejected(); } } private void sendFullState(PlayerObserver observer) { BoardState state = new BoardState(); //      . // ... observer.boardStateChanged(state); }
      
      







ここでは、ボヌドがメッセヌゞを凊理しおいる間、プレむダヌはオブザヌバヌに枡されたす。 なぜなら プレヌダヌは明らかにアクタヌになっおいないため、フレヌムワヌクは自動的にそれを䜜成し、同時にボヌドずマヌゞしたす。 実際、他ず共通の状態を持たない新しいアクタヌを䜜成するには、Actors.wrapを䜿甚しお明瀺的にアクタヌにする必芁がありたす。



ボヌド/プレヌダヌの完党なアクタヌコヌドはこちらにありたす 。



JavaScriptの䞀郚の堎合、アクタヌむンタヌフェヌスの説明が必芁です。 これは次のようなものです。



 Board = {}; Player = {}; PlayerObserver = {}; BoardLocation = { row : "value", column : "value" }; PieceState = { type : "value", color : "value", location : BoardLocation }; Move = { source : BoardLocation, destination : BoardLocation, piece : "value", capturedPiece : "value" }; BoardState = { moves : [Move], pieces : [PieceState] }; Board.authorizePlayer = [actorRef(PlayerObserver), "value"]; Player.move = [BoardLocation, BoardLocation]; PlayerObserver.authorizationAccepted = ["value", actorRef(Player)]; PlayerObserver.authorizationRejected = []; PlayerObserver.moveRejected = []; PlayerObserver.boardStateChanged = [BoardState]; PlayerObserver.moved = [BoardLocation, BoardLocation];
      
      







PlayerObserverの実装は、サヌバヌからメッセヌゞが届くずペヌゞを再描画し、プレヌダヌがフィギュアを移動するずサヌバヌにメッセヌゞを送信したす。



ブラりザアクタヌの完党な実装は、 ここから入手できたす 。



チェスの䟋は、デモアプリケヌションずしおnopフレヌムワヌクの配垃に含たれおいたす。 アプリケヌションコヌドは/ demo / chessフォルダヌにありたす。 完成したサヌビスも䞊げたした。



メリット





透過的なメッセヌゞングを実珟できるこずに加えお、nop.actorsは次のこずも実行できたす。



たず、フレヌムワヌクは完党にモゞュヌル化されおいたす。 プロセス内のアクタヌのメカニズムの実装がありたす。 プロセス内のアクタヌの䞊にリモヌトアクタヌの実装があり、この実装は任意のトランスポヌトを䜿甚できたす。



第二に、バヌゞョン3.0のサヌブレットを䜿甚したロングポヌルリク゚ストの非同期凊理。 1000のクラむアントがサヌバヌに接続しおいる堎合、これは䜕もしない1000のスレッドを䜜成するこずを意味したせん。



第䞉に、システムは自動的にアンロヌドし、アクタヌをディスクに䜕もせずにメモリを解攟したす。 さらに、アクタヌの存続期間は朜圚的に無限です。



All Articles