記事ディレクトリ
MQの基本構造
- メッセージ: メッセージ データ オブジェクト
- 製品: コードをプログラムし、メッセージを生成し、キューにメッセージを送信します。
- コンシューマ: プログラム コード、キューの監視 (バインド)、メッセージの取得、消費コードの実行
- queue: Rocketmq Rabbitmq kafka これらのメッセージ キュー ミドルウェア ソフトウェア。
頼る
<dependency>
<!--2.2.2底层rocketmq客户端4.9.1-->
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
場合:
製品
public class MyProducer {
/**
* 向rocketmq发送第一条消息
*/
@Test
public void sendTest01() throws Exception {
//1.准备一个生产者对象,开启长链接
DefaultMQProducer producer=new DefaultMQProducer();
//对当前producer设置分组
producer.setProducerGroup("first-producer-group");
//连接nameserver localhost:9876
producer.setNamesrvAddr("localhost:9876");
//开启长链接
producer.start();
//2.封装一个消息对象,我们想要发送的内容,只是消息的一部分
//创建一个消息对象
Message message=new Message();
//消息携带的内容 body
String msg="当前发送的第一条消息";
message.setBody(msg.getBytes(StandardCharsets.UTF_8));
//设置消息主题,分类,按业务分类
message.setTopic("first-topic-a");
//主题标签 和key标识
//3.调用api方法将消息发送,接收返回结果,查看发送的信息比如状态
//分为异步发送,同步发送,异步发送性能速度更高,但是无法保证成功.
//同步发送,性能速度没有异步快,但是可以接收反馈结果
SendResult send = producer.send(message);
//result解析获取发送相关的信息
System.out.println("发送状态:"+send.getSendStatus());
System.out.println("消息到达主题,队列,broker信息:"+send.getMessageQueue());
}
}
消費者
public class MyConsumer1 {
@Test
public void consumerTest01() throws Exception {
//1.构建一个消费者对象,连接nameserver创建长链接
// push pull的区别 push消费端,消费的消息是队列推送给他的
// pull 消费端代码执行一次pull 拉取过来一条消息
// 收邮件 推的, 抢红包 拉取的
DefaultMQPushConsumer consumer=new DefaultMQPushConsumer();
//设置nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//消费者分组
consumer.setConsumerGroup("first-consumer-group-a");
//定义监听的主题,消费端代码会根据定义的主题寻找nameserver路由信息,找到主题的队列进行绑定
//topic 主题名称,subExpression 定义过滤逻辑 *表示匹配所有
consumer.subscribe("first-topic-a","*");
//2.执行api开始监听主题,实现队列的消费
//提供给consumer一个监听器
consumer.setMessageListener(new MessageListenerConcurrently() {
/**
* 推送过来的消息,都会调用consumerMessage执行消费逻辑
* @param list 消息数据 list表示可以批量处理的消息,不是批量消息,list元素只有1个
* @param consumeConcurrentlyContext
* @return
*/
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> list,
ConsumeConcurrentlyContext consumeConcurrentlyContext) {
//获取消息 由于不是批量发送只有list一个元素
MessageExt messageExt = list.get(0);
messageExt.getMsgId();//唯一的一个标识,每次消息组装的对象都会在发送时,生成一个msgId
byte[] body = messageExt.getBody();
//将消息转化
String message=new String(body, StandardCharsets.UTF_8);
System.out.println("消费端获取到消息:"+message);
//context 控制返回确认信息 ackIndex顺序
//返回消费状态 success 队列会将消息对应当前消费组,移动偏移量,记录消费完成
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//开启长连接
consumer.start();
while (true);
}
}
核となるアイデア
1.ネームサーバー
NameServer は、トピックとブローカーの動的な登録と検出をサポートする単純なトピック ルーティング レジストリです。
主に次の 2 つの機能が含まれています。
- Broker管理、NameServerはBrokerクラスタの登録情報を受け取り、ルーティング情報の基礎データとして保存します。次に、ブローカーがまだ生きているかどうかを確認するハートビート検出メカニズムを提供します。
- ルーティング情報管理。各ネームサーバーは、ブローカー クラスタに関するルーティング情報全体とクライアント クエリのキュー情報を保存します。プロデューサーとコンシューマーは、メッセージを配信および消費するために、ネームサーバーを通じてブローカー クラスター全体のルーティング情報を知ることができます。
通常、NameServer には複数のインスタンスがデプロイされており、インスタンスは相互に通信しません。ブローカーは独自のルーティング情報を各 NameServer に登録するため、各 NameServer インスタンスは完全なルーティング情報を保存します。何らかの理由でネームサーバーがオフラインになっても、クライアントは他のネームサーバーからルーティング情報を取得できます。
- つまり、ネームサーバーはコーディネーターとして機能し、情報を使用する人はネームサーバーに登録する必要があり、登録情報を使用したい人はネームサーバーにアクセスして同期的に取得する必要があり、ブローカーはプロデューサ コードとコンシューマ コードによって rocketmq コンテナとして使用される必要があります
。
2.ブローカー
ブローカーは主に、メッセージの保存、配信、クエリ、およびサービスの高可用性の保証を担当します。
- NameServer には状態ノードがほとんどないため、ノード間で情報を同期せずにクラスターにデプロイできます。ブローカーの展開は比較的複雑です。
- マスター/スレーブ アーキテクチャでは、ブローカーはマスターとスレーブに分割されます。マスターは複数のスレーブに対応できますが、スレーブは 1 つのマスターにのみ対応できます。マスターとスレーブの対応関係は、同じ BrokerName と異なる BrokerId を指定することによって定義されます。BrokerId は、マスターの場合は 0、スレーブの場合は 0 以外です。マスターは複数展開することもできます。
3. トピックキュー
-
簡単に理解すると、トピックはメッセージのクラスのコレクションであり、メッセージを送信するたびに、特定のトピックにバインドするメッセージを指定する必要があります。
-
プロデューサによって送信される特定のメッセージは 1 つのトピックのみを指すことができ、複数のメッセージは同じトピックを指すことができます。メッセージを保存するために同じトピック内に複数のメッセージ キューがあり、コンシューマはサブスクライブされたトピックに従って異なるトピックのメッセージを消費できます。これにより、ビジネスの分離を実現できます。
-
たとえば、電子商取引のテーマは注文、カート、製品関連などです。
-
1 つのタイプのメッセージは、データの形式から本文の形式まで完全に一貫しています。「最初のメッセージ」ということはありません。本文は通常の文字列で、2 番目のメッセージはオブジェクト Json です。最初のメッセージがメッセージを遅らせること (支払い注文のカウントダウンのキャンセル) は不可能であり、2 番目のメッセージは通常の同期メッセージです。
4.キューキュー
メッセージを保存するための物理的なエンティティ (最小単位)。トピックには複数のキュー(分散表現の鍵) を含めることができ、各キューにはトピックのメッセージが保存されます。トピック キューは、トピック内のメッセージのパーティション** (パーティション**) とも呼ばれます。
注: トピックのキュー内のメッセージは、コンシューマ グループ内の 1 つのコンシューマによってのみ消費できます (消費ポイント ロジック)。キュー内のメッセージは、同じコンシューマ グループ内の複数のコンシューマが同時に消費することを許可しません。
5.プロデューサー
質問: 上記の概念を理解すると、プロデューサー、ネームサーバー、ブローカーはどのように対話するのかがわかります。
- ネームサーバーを起動してブローカーのルーティング情報を保存します
- トピックを作成したら、ブローカーに保存すると同時にキューを生成し、これらのデータはルーティング情報としてネームサーバーに保存されます。
- プロデューサーはネームサーバーから現在のクラスターの登録情報 (ルーティング) を取得します。
- メッセージを送信するときは、特定のブローカーに接続して、特定のトピックの特定のキューを見つけてメッセージを送信します。使用された特定の情報は、返される SendResult に反映されます。
6. 消費者グループと生産者グループ
メッセージプロデューサーはメッセージの作成を担当します。これは本質的にプログラム内のコードの一部です。プロデューサーは、メッセージをブローカー エージェントに配信します。トピックを見つけて、負荷分散し、キューに保存します。RocketMQ のメッセージ プロデューサーはすべて、プロデューサー グループ (プロデューサー グループ) の形式で表示されます
。プロデューサー グループは、同じタイプのメッセージを生成する同じタイプのプロデューサーのコレクションであり、そのようなプロデューサーは同じトピック タイプのメッセージを送信します。プロデューサー グループは、同時に複数のトピックにメッセージを送信できます。
RocketMQ のメッセージ コンシューマはすべて、コンシューマ グループ (コンシューマ グループ) の形式で表示されます。コンシューマ グループは、同じタイプのコンシューマのコレクションです。これらのコンシューマは、同じトピック タイプのメッセージを消費し、同じタイプのメッセージ データに対応します。コンシューマ グループにより、ロード バランシング (トピック内の異なるキューを同じコンシューマ グループ内の異なるコンシューマに均等に分散) とフォールト トレランス (コンシューマがハングアップした場合でも、コンシューマ グループ内の他のコンシューマが消費ロジックを実行し続けることができます) が有効になります。
トピックには複数のキューがあるため、コンシューマーのグループは最大でもキューと同じ数のコンシューマー メンバーを持つことができ、それ以上はキューをバインドしてメッセージを消費することはできません。
7. 消費ポイント
オフセットに関連するすべてのデータは、次のようなキューに記録されます。
- 最小オフセット: すべて 0
- 最大オフセット: 現在のメッセージの数
オフセットはコンシューマにも記録されます
- 現在のグループに対応するトピック キューの消費最小オフセットとキューの最大オフセット (これら 2 つの値により、現在のコンシューマがどのメッセージを消費し、どのくらいが未消費のまま残っているかを知ることができます)