Android アプリケーション統合 RabbitMQ メッセージ処理ガイド

Android アプリケーション統合 ​​RabbitMQ メッセージ処理ガイド

ラビットMQ

RabbitMQ 公式ウェブサイト直通電車 —> ✈✈✈✈✈✈

1 はじめに

       最近仕事が忙しくて、長い間ブログを更新していませんでした。

       今日の飽和したインターネットでは、如何做到不同系统之间传递信息与通信?実際のプロジェクトでは、ios、android、pc、小さなプログラムなどの複数の端末を使用して RabbitMQ からリアルタイムのパケット メッセージを取得し、このリアルタイム パケット メッセージは応答処理に使用されます。

       インターネット テクノロジーの発展に伴い、システム間の結合度はますます高くなっています。 システム間の分離を実現するために、メッセージ ミドルウェアが歴史的な瞬間に登場しました。中でも優れたものとして、RabbitMQはその使いやすさ、信頼性の高さ、マルチプロトコル対応などの特徴から非同期処理やタスクキューなどの分野で広く使われており、実際のプロジェクトでも第一候補となっています。 开源消息中间件

       しかし、多くの人にとって、RabbitMQ はまだあまり馴染みがありません。多くの概念があり、どのように始めればよいかわからないため、多くの人にとって RabbitMQ を学習して使用する際の障害となっています。次にRabbitMQについて紹介します。

2. RabbitMQ の概要

2.1. RabbitMQ とは何ですか?

       MQ はメッセージ キューの略です。メッセージ キュー (MQ) はアプリケーション間の通信方法です。アプリケーションは、キューとの間でメッセージ (アプリケーション固有のデータ) を読み書きすることによって通信します。キューをリンクするための専用接続は必要ありません。メッセージ パッシングとは、プログラムが相互に直接呼び出しを行うのではなく、メッセージでデータを送信することによって相互に通信することを指します。これは通常、リモート プロシージャ コールなどの手法で使用されます。キューイングとは、キューを介して通信するアプリケーションを指します。キューを使用すると、受信アプリケーションと送信アプリケーションを同時に実行する必要がなくなります。より成熟した MQ 製品には、IBM WEBSPHERE MQ などがあります。

RabbitMQ は采用Erlang语言实现的开源消息队列系统、現在最も主流のメッセージ ミドルウェアの 1 つです。

平たく言えば、RabbitMQ は郵便局のようなもので、手紙の受信と転送を担当します。

  • プロデューサーはメール クライアントのようなもので、RabbitMQ 郵便局に手紙 (メッセージ) を配達します。
  • 消費者は受取人のようなもので、RabbitMQ 郵便局から手紙を受け取り、処理します。
  • RabbitMQ は、各レター (メッセージ) が安全かつ確実に受信者 (消費者) に届き、レターの紛失や重複がないことを保証します。
  • RabbitMQ は、同じレターのコピーを異なる受信者 (消費者) に配布できるため、複数の受信者がレターを並行して処理できます。
  • RabbitMQ 郵便局があまりにも多くの手紙を蓄積し、それらを処理する時間がない場合、手紙をキューに入れて先入れ先出しで順番に送信します。
  • RabbitMQ は、重要な文字が最初に処理されるように、特殊な文字に優先順位を設定することもできます。
  • RabbitMQ は、受信者の処理能力に応じてレターを配布することができ、1 人の受信者に大量のレターを処理させることはありません。

簡単に言えば、RabbitMQ は、すべてのメッセージが安全かつ確実に送信および処理されることを保証するインテリジェントなポスト オフィス システムです。異なるシステム間のメッセージングや通信に非常に適しています。

2.2. RabbitMQの特徴

  • STOMP、MQTT などの複数のメッセージ プロトコルをサポートします。
  • メッセージキューをサポートし、メッセージをキャッシュできます。
  • メッセージの損失を防ぐためにメッセージの永続化をサポートします。
  • 複雑なルーティング ルールをサポートし、パブリッシュ/サブスクライブ、負荷分散、その他の機能を実装できます。
  • 高信頼性、クラスターモードをサポート。
  • 管理インターフェイスはフレンドリーで使いやすいです。

2.3. RabbitMQ の動作原理

  • プロデューサ (パブリッシャー) はメッセージを生成し、RabbitMQ サーバーにパブリッシュします。
  • Consumer (コンシューマ) は、RabbitMQ サーバーからメッセージをプルして処理します。
  • RabbitMQ はメッセージ ブローカーとして機能し、メッセージの受信、保存、転送を担当します。

2.4. RabbitMQ のいくつかの重要な概念

  • プロデューサー: メッセージを送信するアプリケーションはプロデューサーです。プロデューサーはメッセージを RabbitMQ にパブリッシュします。
  • コンシューマ: メッセージを受信するアプリケーションはコンシューマです。コンシューマは処理のために RabbitMQ からメッセージを取得します。
  • ConnectionFactory (ファクトリ クラス): これは、RabbitMQ Java クライアントが接続を作成するために使用するファクトリ クラスです。接続の作成に使用されるすべてのパラメータ設定がカプセル化されます。
  • Exchange (スイッチ): Exchange は、プロデューサーによって送信されたメッセージを受信し、ルーティング キーに従って指定されたキューにメッセージをルーティングするために使用されます。
  • キュー (メッセージ キュー): メッセージ キューは、メッセージを保存するための RabbitMQ 内のバッファです。プロデューサによって送信されたメッセージは、まずキューに格納され、コンシューマは処理のためにキューからメッセージを取得します。
  • チャネル: メッセージの読み取りと書き込みのためのチャネル。Connection 上で確立される仮想接続です。同時実行性を実現し、ビジネスと例外の分離を容易にするためのベスト プラクティスは、接続に基づいて直接操作するのではなく、単一の接続に基づいて複数のチャネルを確立することです。
  • ルーティング キー: プロデューサが Exchange にメッセージを発行するときに、ルーティング キーを指定します。 Exchange は、このルーティング キーに基づいてメッセージをルーティングするキューを決定します。
  • バインディング: バインディングは、Exchange と Queue の間の関連付けです。ルーティング キーをバインドに含めることができます。
  • 仮想ホスト (仮想ホスト): RabbitMQ は、権限管理と論理的分離のために複数の仮想ホストを作成できます。
  • メッセージ確認応答 (メッセージ確認応答): コンシューマはメッセージ確認応答メカニズムを有効にし、メッセージを受信して​​処理した後、確認の受信を RabbitMQ に送信できます。その後、RabbitMQ はメッセージをキューに入れて削除します。

3. Android Studio に RabbitMQ を統合する

3.1. マニフェストに権限を追加します。

<uses-permission android:name="android.permission.INTERNET" />

3.2. build.gradle(:app) に依存関係を追加します。

implementation 'com.rabbitmq:amqp-client:5.19.0'

同期が完了するまで辛抱強く待った後、RabbitMQ の関連 API を使用できるようになります。

4. 接続を確立する

4.1. ConnectionFactory オブジェクトの作成

このオブジェクトには、RabbitMQ ホスト アドレス、ポート、仮想ホスト、ユーザー名、パスワードなど、接続の作成に必要な設定が含まれています。

ConnectionFactory factory = new ConnectionFactory();
// 连接配置
// factory.setHost(Config.MQ_ADDRESS); // 服务器ip
// factory.setPort(Integer.parseInt(Config.MQ_PORT)); // 端口
// factory.setUsername(Config.MQ_USERNAME); // 用户名
// factory.setPassword(Config.MQ_PASSWORD); // 密码
// factory.setVirtualHost(Config.MQ_VIRTUALHOST);
factory.setUri(url);

4.2. ConnectionFactory を使用して接続を作成する

ConnectionFactorycreateConnection() メソッドを呼び出して、接続オブジェクトを作成します。

Connection connection = factory.newConnection();

4.3. チャンネルの作成

メッセージの公開または消費のために接続上にチャネルを作成します。

Channel channel = connection.createChannel();

4.4. キュー、スイッチなどを宣言します。

チャネルを使用してキュー、スイッチなどを宣言します。

4.5. ニュースを作成または消費する

チャネルを通じてメッセージを送受信します。

4.6. 接続を閉じる

使用後は接続とチャネルを閉じます。

channel.close();
connection.close();

5. メッセージを送信する

5.1. 接続とチャネルの作成

ConnectionFactory を使用して接続を作成し、その接続上にチャネルを作成します。

5.2. キューの宣言

キューが存在しない場合は、事前にキューを宣言する必要があります。

5.3. メッセージコンテンツの準備

送信するメッセージのコンテンツ本文を定義します。バイト配列や文字列などを指定できます。

5.4. リリースニュース

BasicPublish メソッドは、チャネル オブジェクトを使用して呼び出されます。これには、交換名、ルーティング キー、メッセージ コンテンツが必要です。

5.5. リソースを閉じる

送信が完了したら、チャネルと接続を閉じることができます。

5.6. ケース

// 构建消息内容
String message = "Hello World!";

// 发布消息到队列
channel.basicPublish(EXCHANGE_NAME, "routingkey", null, message.getBytes());

// 关闭资源
channel.close();
connection.close();

6. メッセージを受信する

6.1. 接続とチャネルの作成

ConnectionFactory を使用して接続を作成し、その接続上にチャネルを作成します。

6.2. キューの宣言

キューが存在しない場合は、最初にキューを宣言する必要があります。

6.3. 消費者の定義

Consumer インターフェイスを実装し、メッセージ処理ロジックを定義します。

6.4. リスニングキュー

チャネル オブジェクトを使用して、basicConsume メソッドを呼び出し、指定されたキューをリッスンします。

6.5. メッセージの受信

RabbitMQ はメッセージを Consumer に配信し、メッセージは handleDeliver メソッドで取得して処理できます。

6.6. メッセージの確認

処理が完了したら、チャネルの BasicAck メソッドを呼び出してメッセージを手動で確認します。

6.7. リソースを閉じる

最後に、チャネルと接続を閉じる必要があります。

6.8. ケース

channel.basicConsume(QUEUE_NAME, true, consumer); 

public class MyConsumer implements Consumer {
    
    
  public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
    
    
    // 获取消息并处理
    String message = new String(body, "UTF-8");
    System.out.println("Received message: " + message);
    
    // 确认消息
    channel.basicAck(envelope.getDeliveryTag(), false);
  }
}

7. 確認の仕組み

RabbitMQ の確認メカニズムは主に 2 種類に分けられます。

7.1. 発行者による確認

これは一方向の確認メカニズムで、プロデューサーはメッセージが RabbitMQ によって受信されたかどうかを知ることができます。

チャネルで確認モードを有効にすると、すべてのメッセージに一意の ID (1 から始まる) が割り当てられます。メッセージが一致するすべてのキューに配信されると、RabbitMQ はプロデューサーに確認を送信します (メッセージの一意の ID が含まれます)。これにより、メッセージが処理されたことがプロデューサーに通知されます。

メッセージとキューが永続的である場合、メッセージの永続化には確認応答メカニズム自体が必要です。


Java クライアントでは、チャネルのconfirmSelectメソッドを使用してチャネルを確認モードに設定できます。

channel.confirmSelect();

次に、確認済みメッセージと未確認メッセージをリッスンするリスナーを追加できます。

channel.addConfirmListener(new ConfirmListener(){
    
    
  // ...
});
7.2. 消費者の確認

これは双方向の確認メカニズムであり、メッセージが配信されたことをプロデューサーに伝えるだけでなく、RabbitMQ にも次のことを伝えます。メッセージはコンシューマによって受信され、受信および処理されました。

確認モードがオンになった後、コンシューマがメッセージを受信すると、そのメッセージは RabbitMQ メッセージ バッファから削除されます。メッセージの処理中にコンシューマが失敗するか異常終了した場合、メッセージが失われないように、未処理のメッセージは RabbitMQ によって他のコンシューマに再配布されます。

確認メカニズムを正しく使用すると、RabbitMQ のパフォーマンスとメッセージ処理機能が向上するだけでなく、ビジネス プロセスの整合性も確保できます。したがって、実際の使用においては、確認の仕組みが非常に重要になります。


Java クライアントでは、basicConsume 中に autoAck=false を設定し、手動で basicAck を呼び出して確認を行うことができます。

channel.basicConsume(queue, false, consumer); 

consumer.handleDelivery(envelope, properties, body) {
    
    
  //...
  channel.basicAck(envelope.getDeliveryTag(), false); 
}

8. テーマの確認

RabbitMQ にはいくつかの一般的なトピック タイプがあり、どれを使用するかを選択するのは主に特定のアプリケーション シナリオによって決まります。

8.1. 直接交換(デフォルト)

ダイレクト スイッチ。ルーティング キーを完全に照合してメッセージをキューに配信します。このシナリオでは、メッセージを受信するには指定されたキューを受信する必要があります。

8.2、ファンアウト交換

ファンアウト スイッチは、すべてのバインドされたキューにメッセージを配布します。シナリオでは、メッセージをブロードキャストする必要があります。

8.3、話題交換

トピック スイッチは、ルーティング キーのルールに従ってメッセージを照合し、キューに配信します。このシナリオでは、ルールに従ってメッセージをさまざまなキューに分散する必要があります。

8.4、ヘッダー交換

ヘッダー スイッチは、送信されたメッセージのヘッダー属性に従ってメッセージを照合し、配布します。シナリオでは、メッセージ ヘッダーに基づいてルーティング配布を実行する必要があります。

どのテーマ タイプを使用するかの選択は、主に実際のビジネス ニーズに基づいて行われます。

  • 指定したキューにメッセージを直接送信する必要がある場合は、direct スイッチを使用します。
  • すべてのキューにメッセージをブロードキャストする必要がある場合は、ファンアウト スイッチを使用します。
  • ルールの一致に基づいてメッセージを配信する必要がある場合は、トピック スイッチを使用します。
  • メッセージ ヘッダー属性に基づいた配信が必要な場合は、headers スイッチを使用します。

コードでは、次のようにスイッチを宣言するときにタイプを指定するだけです。

channel.exchangeDeclare("myExchange","topic");

9. 使用例

Android、IOS、小規模プログラムなどのマルチターミナル アプリケーションにおける RabbitMQ の一般的なアプリケーション シナリオと機能:

9.1. 非同期処理

各エンドは、RabbitMQ を介してタスクの非同期処理を実装でき、ユーザーの待ち時間を回避し、ユーザー エクスペリエンスを向上させます。たとえば、ミニ プログラムで注文した後、注文情報は RabbitMQ を通じて非同期でサーバーに送信されます。

9.2. プッシュ通知

RabbitMQ を使用して、注文配達通知などのモバイル プッシュ通知を実装できます。

9.3. データ送信

モバイル端末とサーバー側は、直接結合を回避し、送信の柔軟性を向上させるために、RabbitMQ を介してデータを送信できます。

9.4. 負荷分散

RabbitMQ は、複数の端末とサーバーの間で負荷分散を実行し、過度のサーバー負荷を防ぐことができます。

9.5. トラフィックピーククリッピング

RabbitMQ のメッセージ キューを使用してリクエストのピークを処理し、サーバーが瞬時に過負荷になるのを防ぎます。

9.6. サービスの分離

異なるエンドは通信のために RabbitMQ のみに依存し、サービスの分離を実現するために相手側の技術的な実装の詳細に注意を払う必要はありません。

9.7. 柔軟な拡張

RabbitMQは各端末やサーバーの柔軟な拡張を容易にします。

9.8. オフライン操作のサポート

モバイル端末は、RabbitMQ を介して特定のオフライン操作を実装し、ネットワークが復元された後にサーバーと同期できます。

実用的なアプリケーション:

       私の実際のモノのインターネット プロジェクトでは、ユーザーがワイヤレス デバイスを介して身体指標を測定した後、デバイスはネットワークを介してデータをバックエンドに送信し、バックエンドがデータを解析した後、MQ を介して各エンドにデータを送信します。受信した情報パケットを通じて終了し、適切な処理を実行します。

接続を作成するために設定が必要な場合、私はConnectionFactoryを使用して配置Url経由で接続を確立します。 startConsumer によって指定されたキューの使用とリッスンを開始し、情報パケットを受信するためのコールバック インターフェイス DeliverCallback を定義します。最後に、データ パケットはEventBusを通じて配信されます。

結果を示す

ここに画像の説明を挿入します
ここに画像の説明を挿入します

実装

ここに画像の説明を挿入します
![ここに画像の説明を挿入](https://img-blog.csdnimg.cn/473682e32d2a48fabd1efa6bba256e84.png

/**
 * @author 拉莫帅
 * @date 2023/10/24
 * @address
 * @Desc EventBus
 */
public class MessageEvent {
    
    
    private String message;

    public String getMessage() {
    
    
        return message;
    }

    public void setMessage(String message) {
    
    
        this.message = message;
    }
}
/**
 * @author 拉莫帅
 * @date 2023/10/24
 * @address
 * @Desc rabbitMQ
 */
public class RabbitMQUtil {
    
    

    private Connection connection;
    private Channel channel;
    private ConnectionFactory factory;
    private String queueName;

    public RabbitMQUtil(String httpType, String userName, String password, String host, String port, String virtualHost, String exChangeName, String bindingKey) {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (connection == null) {
    
    
                    // 创建一个连接
                    factory = new ConnectionFactory();
                    try {
    
    
                        StringBuilder builder = new StringBuilder();
                        StringBuilder stringBuilder = builder.append(httpType).append("://").append(userName).append(":").append(password)
                                .append("@").append(host).append(":").append(port).append("/").append(virtualHost);
                        String url = stringBuilder.toString();
                        Log.e("RabbitMQ", "Url " + url);

                        // 连接配置
                        // factory.setHost(Config.MQ_ADDRESS); // 服务器ip
                        // factory.setPort(Integer.parseInt(Config.MQ_PORT)); // 端口
                        // factory.setUsername(Config.MQ_USERNAME); // 用户名
                        // factory.setPassword(Config.MQ_PASSWORD); // 密码
                        // factory.setVirtualHost(Config.MQ_VIRTUALHOST);
                        factory.setUri(url);

                        // 创建一个新的代理连接
                        connection = factory.newConnection();
                        // 使用内部分配的通道号创建一个新通道
                        channel = connection.createChannel();
                        channel.exchangeDeclare(exChangeName, "topic", true); // 声明一个转发器

                        queueName = channel.queueDeclare().getQueue();
                        channel.queueBind(queueName, exChangeName, bindingKey); // 绑定一个到转发器
                        Log.e("Waiting for logs.", "");

                        startConsumer();
                    } catch (URISyntaxException e) {
    
    
                        e.printStackTrace();
                    } catch (NoSuchAlgorithmException e) {
    
    
                        e.printStackTrace();
                    } catch (KeyManagementException e) {
    
    
                        e.printStackTrace();
                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    /**
     * 开始消费
     */
    public void startConsumer() throws Exception {
    
    
        Log.e("startConsumer", "");

        // 定义回调接口DeliverCallback
        DeliverCallback callback = (consumerTag, message) -> {
    
    
            String result = new String(message.getBody(), "UTF-8");
            Log.e("DeliverCallback >>>", result);

            // 创建一个事件
            MessageEvent event = new MessageEvent();
            event.setMessage(result);

            // 通过EventBus发送事件
            EventBus.getDefault().post(event);
        };

        // 启动基本消费,并传入回调接口
        channel.basicConsume(queueName, true, callback, consumerTag -> {
    
    
        });
    }

    /**
     * 关闭连接
     */
    public void close() throws Exception {
    
    
        channel.close();
        connection.close();
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    private static final String bindingKey = "topic.chain=2.region=3.area=4.pharmacy=5.";

    private RabbitMQUtil rabbitMQUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initMQ();
    }

    private void initMQ() {
    
    
        if (rabbitMQUtil == null) {
    
    
            rabbitMQUtil = new RabbitMQUtil(Config.MQ_HTTP_TYPE, Config.MQ_USERNAME, Config.MQ_PASSWORD,
                    Config.MQ_ADDRESS, Config.MQ_PORT, Config.MQ_VIRTUALHOST, Config.MQ_EXCHANGE, bindingKey);
        }
    }

    @Override
    public void onStart() {
    
    
        super.onStart();

        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
    
    
        super.onStop();

        EventBus.getDefault().unregister(this);
    }

    // 接收事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessage(MessageEvent event) {
    
    
        String message = event.getMessage();
        Log.e("接收MQ +++++++++++++", message);

        // 更新UI
        // ...
    }
    
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();

        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    rabbitMQUtil.close();
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

10. まとめ

RabbitMQ の優れたパフォーマンスと柔軟性により、単純なリクエストとレスポンスのやり取りから複雑な非同期処理シナリオまで、あらゆるものを処理できます

これは、システム間の非同期分離だけでなく、アプリケーション内のさまざまなコンポーネントの分離にも使用できます。これは分散システム間のデータ交換と統合に非常に適しており、エンタープライズレベルの分散アーキテクチャの重要なコンポーネントの 1 つとなっています。

一般に、RabbitMQ は使いやすく、安定した強力なメッセージング ミドルウェアとして可用性が高く、スケーラブルで遅延の少ないメッセージング サービスを提供できます。 a>、実際のプロジェクトで広く使用されています。そして、それは現在のテクノロジースタックの中で非常に重要な位置を占めており、習得しなければならないスキルの 1 つです。

おすすめ

転載: blog.csdn.net/chen_md/article/details/134133260
おすすめ