Akkaを使い始める


Lock Freeフレームワークに関する一連の記事:

1アッカとは?

Akkaは、JAVA仮想マシンプラットフォーム上で同時実行性が高く、分散型でフォールトトレラントなアプリケーションを構築するためのツールキットおよびランタイムです。AkkaはScala言語で書かれており、ScalaとJavaの開発インターフェースも提供します。

Akkaが並行性を処理する方法は、アクターモデルに基づいています。

Actor的概念来自于Erlang,在AKKA中,可以认为一个Actor就是一个容器,用以存储状态、行为、Mailbox以及子Actor与Supervisor策略。Actor之间并不直接通信,而是通过Mail来互通有无。

在Actor模式中,每个Actor都有一个(恰好一个)Mailbox。Mailbox相当于是一个小型的队列,一旦Sender发送消息,就是将该消息入队到Mailbox中。入队的顺序按照消息发送的时间顺序。Mailbox有多种实现,默认为FIFO。但也可以根据优先级考虑出队顺序,实现算法则不相同。

akkaフレームワークは、次の部分で構成されています。

akka-俳優

スレッドプリミティブの苦痛を一切伴わない、並行性と分散のモデルであるakkaのコア

akka-stream

非同期のノンブロッキング背圧フロー処理を実現するための直感的で安全な方法。

akka-http

最新の高速非同期ストリーミングHTTPサーバーおよびクライアント。

akka-cluster

システムを複数のノードに分散することで、回復力と回復力を獲得します。

アッカシャーディング

ユーザーのIDに従って、クラスター内の参加者を割り当てます。

分散データ

結果整合性、読み取りと書き込みの高可用性、および低遅延データ

Akkaの永続性

参加者向けのイベントパッケージを使用すると、再起動後に同じ状態に到達できます。

アッカマネジメント

クラウドシステムでAkkaシステムの拡張機能を実行します(k8s、aws、...)

アルパカ

Akka Streaming Connectorは、他のテクノロジーを統合するために使用されます

2 Akkaの機能:

  • 並行性モデルのより高度な抽象化

  • 非同期、ノンブロッキング、高性能のイベント駆動型プログラミングモデルです

  • 軽量のイベント処理(1GBのメモリで数百万のアクターを保持できます)

  • スレッドよりも粒度が小さいアクターと呼ばれる同時実行モデルを提供し、システムで多数のアクターを有効にすることができます。

  • フォールトトレラントメカニズムのセットを提供し、アクターが異常な場合に回復またはリセット操作を可能にします。

  • Akkaは、単一のマシン上で高度に並行するプログラムを構築することも、ネットワーク上に分散プログラムを構築することもでき、ロケーション透過的なアクターロケーションサービスを提供します。

3アクターモデル

並行プログラムでは、スレッドは並行プログラムの基本的な実行ユニットですが、Akkaでは実行ユニットはアクターです。アクターモデルは1973年に提案された分散並行プログラミングモデルであり、Erlang言語で広くサポートおよび適用されています。

アクターモデルでは、アクターオブジェクトのメソッドを介してアクターに何をするかを指示する代わりに、アクターにメッセージを送信します。アクターがメッセージを受信すると、メッセージの内容に基づいて、自身の状態を変更するなどの特定のアクションを実行する場合があります。この場合、この状態の変更は、外部の介入ではなく、アクター自身によって行われます。

Erlangでは、すべてのコードがプロセス内で実行されます。プロセスは、ErlangのActorの名前ですつまり、その状態は他のプロセスに影響を与えません。システムにはスーパーバイザーが存在しますが、これは実際には別のプロセスです。監視対象のプロセスがハングした場合、スーパーバイザーに通知して処理するため、自己修復機能を備えたシステムを構築することもできます。アクターが異常な状態になってクラッシュした場合、何があってもスーパーバイザーは反応して一貫した状態にしようとすることができます。最も一般的な方法は、初期状態に従ってアクターを再起動することです。


简单来说,Actor通过消息传递的方式与外界通信,而且消息传递是异步的。每个Actor都有一个邮箱,邮箱接收并缓存其他Actor发过来的消息,通过邮箱队列mail queue来处理消息。Actor一次只能同步处理一个消息,处理消息过程中,除了可以接收消息外不能做任何其他操作。

各アクターは完全に独立しており、同時に操作を実行できます。各アクターは、受信したメッセージをマップし、次のアクションを実行するコンピューティングエンティティです。限られた数のメッセージを他のアクターに送信し、限られた数の新しいアクターを作成し、次に受信したメッセージの動作を指定します。これらの3つのアクションには決まった順序はなく、同時に実行できます。アクターは、受信したメッセージに応じて異なる処理を実行します。

3.1アクターモデルの利点


传统并发程序是基于面向对象的方法,通过对象的方法调用进行信息传递,如果对象的方法修改对象本身的状态,在多线程下就可能出现对象状态的不一致,此时就必须对方法调用进行同步,而同步操作会牺牲性能。

たとえば、2つのスレッドが最後のアイテムを同時に購入しようとしたときに、ロックがない場合、複数のスレッドが同時にカウンターの値が購入数以上であると判断し、誤ってデクリメントする可能性があります。カウンター、負の数とスレッドセーフの問題が発生します。

スレッドセーフの問題を防ぐために、ロックが必要です。

Javaの軽量ロックを列として使用すると、競争の激しい段階では、非常に長いスレッドキューがあり、それらはすべてデクリメントカウンターを待機しています。ただし、キューの使用方法に伴う問題は、多くのブロックされたスレッドが発生する可能性があることです。つまり、各スレッドは順番を待ってシリアル化された操作を実行します。

したがって、ロックを不当に使用すると、マルチコアおよびマルチスレッドのアプリケーションがシングルスレッドのアプリケーションに変わったり、ワーカースレッド間で高度な競合が発生したりする可能性があります。

アクターモデルはこの問題をエレガントに解決し、真のマルチスレッドアプリケーションの基本的なサポートを提供します。

3.2アクターの役割の責任:

アクターモデルは、システム内のすべてをアクターロールに抽象化します。システムでは、大規模なタスクをいくつかの小さなタスクに分解できます。これらの小さなタスクは、複数のアクターによって同時に処理できるため、タスクの完了時間が短縮されます。

受信したメッセージに応答して、アクターは、アクターの作成、メッセージの送信、次に受信したメッセージへの応答方法の決定など、独自にいくつかの決定を行うことができます。

アクターの役割の責任:

  • アクターの入力は受信したメッセージです

  • アクターはメッセージを受信し、それを処理する方法を決定します。たとえば、アクターをさらに作成する、メッセージを送信する、次に受信したメッセージに応答する方法を決定するなどです。

  • アクターは、タスクの完了後に他のアクターにメッセージを送信できます

アクターモデルの利点の1つは、共有状態を排除できることです。アクターは一度に1つのメッセージしか処理できないため、ロックメカニズムに関係なく、アクターは内部の状態を安全に処理できます。

アクターモデルでは、主人公はアクターであり、ワーカーに似ています。アクターは、仲介なしで互いに直接メッセージを送信します。メッセージは非同期で送信および処理されます。アクターモデルでは、すべてがアクターであり、すべてのロジックまたはモジュールをアクターと見なすことができます。モジュール間の通信と相互作用は、異なるアクター間でメッセージを渡すことによって実現されます。

アクターは、状態、動作、およびメールボックスで構成されます

  • 状態:状態とは、アクターオブジェクトの変数情報を指します。状態は、並行環境でのロックやメモリの原子性などの問題を回避するために、アクター自体によって管理されます。

  • ビヘイビア(ビヘイビア):ビヘイビアは、アクターの計算ロジックを指定し、アクターが受信したメッセージを通じてアクターの状態を変更します。

  • メールボックス:メールボックスは、アクター間の通信ブリッジです。FIFOメッセージキューは、メールボックス内にメッセージを格納および送信するために使用され、受信者はメールボックスからメッセージを取得します。

3.3メールボックスの役割の責任

1人のアクターだけでは不十分であり、複数のアクターがシステムを形成できます。アクターモデルでは、各アクターは独自のアドレスを持っているため、相互にメッセージを送信できます。注意すべき点の1つは、複数のアクターが同時に実行されていても、1つのアクターはメッセージを順番に処理することしかできないということです。つまり、他のアクターが複数のメッセージをアクターに送信する場合、アクターは一度に1つしか処理できません。複数のメッセージを並行して処理する必要がある場合は、メッセージを複数のアクターに送信する必要があります。

メッセージはアクターに非同期で送信されるため、アクターがメッセージを処理しているときは、新しいメッセージを別の場所、つまりメールボックスメッセージが保存される場所に保存する必要があります。

各アクターには1つだけのメールボックスがあります。メールボックスは小さなキューに相当します。送信者がメッセージを送信すると、メッセージはメールボックスのキューに入れられます。エンキューの順序は、メッセージ送信の時系列に従います。

img

3.3アクターモデルの特徴

アクターモデルは、並行プログラミングを回避するための一連の公理を記述します。

  • すべてのアクターの状態はローカルであり、外部からアクセスすることはできません。

  • アクターはメッセージパッシングを通じて通信する必要があります。

  • アクターは、メッセージに応答したり、新しいアクターを終了したり、内部状態を変更したり、1つ以上のアクターにメッセージを送信したりできます。

  • アクターは自分自身をブロックできますが、アクターは実行中のスレッドをブロックしないでください

最初の行は、システムで作成された最初のアクターであるHelloWorldアクターのパスを出力します。パスは次のとおりです:akka:// hello / user / helloworld。最初のhelloは、ActorSystemのシステム名を表します。これは、構築中に入力される最初のパラメーターです。userはユーザーアクターを表し、すべてのユーザーアクターはユーザーパスの下にマウントされます。最後に、helloworldはこのアクターの名前です。

2行目はGreeterActorのパスを出力し、3行目と4行目はGreeterで出力される情報です。

5行目は、HelloWorldが停止し、Greeterから送信されたメッセージが正常に配信されなかったために、システムでメッセージ配信の失敗が発生したことを示しています。

並行開発にActorを使用する場合、フォーカスはスレッドに集中しなくなります。スレッドのスケジューリングはAkkaフレームワークによってカプセル化されているため、Actorオブジェクトにのみフォーカスする必要があります。情報は、表示されたメッセージ送信を通じてActorオブジェクト間で渡されます。

システムに複数のアクターがある場合、Akkaはスレッドプール内のスレッドを自動的に選択してアクターを実行します。異なるアクターが同じスレッドで実行される場合と、アクターが異なるスレッドで実行される場合があります。

注:アクターで時間のかかるコードを実行しないでください。他のアクターのスケジューリングの問題が発生する可能性があります。

4 JavaアーキテクトがAkkaを学ぶ必要があるのはなぜですか?

AkkaはScala言語で書かれており、Java開発インターフェースを提供しますが、Akkaに基づく開発はほとんどありません。ただし、多くの分散フレームワークはakkaで作成されています。たとえば、flinkの分散通信[3]はAkkaに依存しています。

ただし、アプリケーションレベルのチームの場合、パフォーマンスは常にニーズを満たすことができ、パフォーマンスの制限を追求する必要はありません。ミドルウェアの信頼性が高いほど、開発は速くなります。もちろん、アプリケーション開発チームは、Hadoop、spark、hive、flink、Kinesis、Kafka、Storm、その他のコンポーネントなどの分散ミドルウェアを使用して問題を解決することを好みます。

したがって、Akkaはアプリケーション開発のために学ぶ必要はありません。しかし、建築家にとって、それは学ばなければなりません。

少なくとも、Akkaの原則は学ぶ価値があります。

5アッカ入門例

次のプログラムは、akkaの簡単な例を示しています。コマンドを処理し、メッセージパッシングを介して対話するアクターを作成します。

依存関係を導入します。

    <dependency >
    <groupId> com.typesafe.akka</groupId >
    <artifactId>akka-actor_2.10</artifactId>
    <version>2.3.10</version>
    </dependency>
    <dependency >
        <groupId> com.typesafe.akka</groupId >
        <artifactId>akka-persistence-experimental_2.10</artifactId>
        <version>2.3.10</version>
    </dependency>

コマンドオブジェクトを作成する

  //创建命令对象
    @Data
    @AllArgsConstructor
    static class Command implements Serializable
    {
        private static final long serialVersionUID = 1L;
        private String data;
    }

アクターオブジェクトを作成する

    //创建Actor对象
    static class SimpleActor extends UntypedActor
    {

        LoggingAdapter log = Logging.getLogger(getContext().system(), this);

        public SimpleActor()
        {
            log.info("SimpleActor constructor");
        }

        @Override
        public void onReceive(Object msg) throws Exception
        {

            log.info("Received Command: " + msg);
            if (msg instanceof Command)
            {
                final String data = ((Command) msg).getData();
                // emmit an event somewhere...

            } else if (msg.equals("echo"))
            {
                log.info("ECHO!");
            }
        }
    }

ActorSystemを起動します

   public static void main(String[] args) throws InterruptedException
    {

        final ActorSystem actorSystem = ActorSystem.create("actor-system");

        Thread.sleep(5000);

        final ActorRef actorRef = actorSystem.actorOf(Props.create(SimpleActor.class), "simple-actor");

        actorRef.tell(new Command("CMD 1"), null);
        actorRef.tell(new Command("CMD 2"), null);
        actorRef.tell(new Command("CMD 3"), null);
        actorRef.tell(new Command("CMD 4"), null);
        actorRef.tell(new Command("CMD 5"), null);

        Thread.sleep(5000);

        log.debug("Actor System Shutdown Starting...");

        actorSystem.shutdown();

    }

mainを実行した結果は次のとおりです。

[INFO] [11/01/2020 18:12:15.303] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] SimpleActor constructor
[INFO] [11/01/2020 18:12:15.306] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] Received Command: AkkaDemo.Command(data=CMD 1)
[INFO] [11/01/2020 18:12:15.306] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] Received Command: AkkaDemo.Command(data=CMD 2)
[INFO] [11/01/2020 18:12:15.307] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] Received Command: AkkaDemo.Command(data=CMD 3)
[INFO] [11/01/2020 18:12:15.307] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] Received Command: AkkaDemo.Command(data=CMD 4)
[INFO] [11/01/2020 18:12:15.308] [actor-system-akka.actor.default-dispatcher-3] [akka://actor-system/user/simple-actor] Received Command: AkkaDemo.Command(data=CMD 5)

6Akkaのベストプラクティス

素数の計算

要件:複数のスレッドを使用して、1,000,000以内の素数の数を見つけます

共有メモリ方式の処理フローは次のとおりです。

img

従来の方法では、ロック/同期を使用して同時実行を実現します。同期ごとに現在の値を取得し、スレッドが値が素数であるかどうかを判断できるようにします。素数の場合、カウンターは同期して1ずつインクリメントされます。

アクターモデルの処理フローは次のとおりです。

img

アクターモデルメソッドを使用すると、このプロセスが複数のモジュールに分割されます。つまり、複数のアクターに分割されます。各アクターはさまざまな部分を担当し、メッセージパッシングを通じて複数のアクターが連携できるようにします。

銀行振込

img

既存の問題:ユーザーAアクターがお金を差し引いている場合、ユーザーBアクターは制限されていません。現時点では、ユーザーBアクターを操作することは合法です。この場合、純粋なアクターモデルは比較的弱く、追加する必要があります。一貫性。

注:上記のケースはモデルのリファレンスとしてのみ使用されており、リファレンス実装は提供されていません。特定の実装コードについては、クレイジーメーカーサークルコミュニティにアクセスしてコミュニケーションをとることができます。

7Akkaのメッセージ配信戦略

Akkaアプリケーションはメッセージによって駆動され、メッセージはアクター以外の最も重要なコアコンポーネントです。アクター間で渡されるメッセージは、不変性、つまり不変モードを満たす必要があり、可変メッセージを並行環境で効率的に使用することはできません。Akkaでは不変オブジェクトを使用することをお勧めします。最終的なフィールド宣言はコードで使用できます。メッセージが作成された後は、変更できません。

Akkaのメッセージ配信戦略:

  • 最大1回の配信:この戦略の各メッセージは最大1回配信され、配信が失敗することがあり、メッセージが失われる可能性があります。この戦略は高性能です。

  • 少なくとも1回の配信:この戦略の各メッセージは、成功するまで少なくとも1回配信されます。場合によっては、受信者が重複したメッセージを受信することがありますが、メッセージは失われません。この戦略では、メッセージ配信のステータスを保存して、再試行を続ける必要があります。

  • 正確な配信:すべてのメッセージは正確に配信され、一度正常に受信されることが保証されており、失われたり繰り返されたりすることはありません。この戦略は最も費用がかかり、実装が困難です。

メッセージの信頼性について:Akka層でメッセージの信頼性を保証する必要はありません。費用がかかりすぎて不要です。メッセージの信頼性は、アプリケーションのビジネスレイヤーで保証する必要があり、一部のメッセージの損失がアプリケーションの要件に沿っている場合があります。

メッセージ配信の順序:Akkaは、配信の順序をある程度保証できます。アクターA1が3つのメッセージM1、M2、およびM3をA2に順番に送信し、アクターA3が3つのメッセージM4、M5、およびM6をA2に順番に送信する場合、システムは以下を保証できます。

  • M1が失われていない場合は、M2とM3の前にA2が受信する必要があります。

  • M2が失われない場合は、M3の前にA2が受信する必要があります。

  • M4が失われていない場合は、M5およびM6の前にA2が受信する必要があります。

  • M5が失われていない場合は、M6の前にA2が受信する必要があります。

  • A2の場合、A1からA3へのメッセージはシーケンシャルであることが保証されていません。

img

さらに、次の図に示すように、このメッセージ配信ルールは転送できません。

img

CがM1とM2を受け取る順序は保証されません


クレイジーメーカーサークルに戻る

Crazy Maker Circle-Javaの同時実行性の高い研究コミュニティ、すべての人に大きな工場への扉を開く

おすすめ

転載: blog.csdn.net/crazymakercircle/article/details/109432688