皆さんこんにちは、山陽です~~
先週、私は RocketMQ 環境をゼロから構築することに少し時間を費やしました. 非常に簡単だと思ったので、それを共有する記事を書きました.
記事全体は大まかに 3 つの部分に分けることができます. 最初の部分はいくつかのコア概念とワークフローの説明です. 2 番目の部分は一連の環境を純粋に手動で構築することです. 3 番目の部分はテストと統合のための環境に基づいています. SpringBoot は、プロセス全体が比較的詳細であるため、「乳母レベルのチュートリアル」と呼んでいます。
さて、どれだけナンセンスを構成するか、トピックに直接行きます。
序文
RocketMQ は Alibaba のオープン ソース MQ フレームワークで、Double Eleven テストを受け、Java プログラミング言語で実装されており、非常に優れた完全なエコシステムを備えています。RocketMQ は、純粋な Java の分散型キュー モデル オープン ソース メッセージ ミドルウェアとして、トランザクション メッセージ、シーケンシャル メッセージ、バッチ メッセージ、タイミング メッセージ、メッセージ バックトラッキングなどをサポートします。
コアアイデア
- NameServer : トピックのルーティング情報を保存し、Broker を管理するために主に使用されるレジストリとして理解できます。NameServer クラスタでは、NameServer と NameServer の間に通信はありません。
- Broker : 主にトピック情報の保存、プロデューサーによって生成されたメッセージの受け入れ、およびメッセージの永続化に使用されるコア ロール。Broker クラスターでは、同じ BrokerName を Broker グループと呼ぶことができます. Broker グループでは、BrokerId は 0 でマスター ノードとして、その他はスレーブ ノードです。BrokerName と BrokerId は、Broker の開始時に構成ファイルを使用して構成できます。各ブローカー グループには、メッセージの一部のみが保存されます。
- Producer : メッセージを作成する側がプロデューサーです
- プロデューサー グループ: プロデューサー グループには多くのプロデューサーを含めることができます。プロデューサーの作成時にプロデューサー グループを指定するだけで、プロデューサーはそのプロデューサー グループに属します。
- Consumer : プロデューサーのメッセージを消費する当事者
- Consumer group : Like the producer, each consumer has its own consumer group. コンシューマー グループには多数のコンシューマーを含めることができ、異なるコンシューマー グループは互いに影響を与えることなくメッセージを消費します。
- トピック (トピック) : メッセージの集まりの名前として理解できます. プロデューサがメッセージを送信するとき, 送信するトピックを指定する必要があります. コンシューマがメッセージを消費するとき, どのメッセージを知る必要があります.その下でトピックが消費されます。
- タグ(サブトピック) : トピックの1階層下にあり、同じトピックの下で異なる業種のメッセージを区別するために使用でき、メッセージを送信する際にも指定する必要があります。
ここにグループの概念があります。異なるプロデューサー グループまたはコンシューマー グループに異なる構成を持たせるために使用できるため、プロデューサーまたはコンシューマーをより柔軟にすることができます。
作業過程
核となる概念について話した後、核となるワークフローについて話しましょう. ここではまず絵を描きます.
この図から、RocketMQ の一般的なワークフローを明確に知ることができます。
- Broker が起動すると、独自の情報が各 NameServer に登録されます (NameServer は相互に通信しないため、それぞれを登録する必要があります)。これには、独自の IP とポート番号、およびこの Broker にあるトピックが含まれます。
- Producer が起動すると、NameServer との接続を確立し、定期的に NameServer から Broker の情報を取得します. メッセージを送信するとき、メッセージを送信する必要があるトピックに応じて、対応する Broker アドレスを見つけます。 . Broker はリクエストを送信します; リクエストが見つからない場合は、トピックの自動作成を許可するかどうかによって、メッセージを送信するかどうかを決定します。
- Broker は Producer からメッセージを受信した後、メッセージを保存して永続化します. スレーブ ノードがある場合は、スレーブ ノードとの同期も積極的に行い、データのバックアップを実現します.
- コンシューマーが起動すると、ネームサーバーとの接続も確立し、ネームサーバーからブローカと対応するトピックの情報を定期的に取得し、サブスクライブする必要があるトピック情報に従って、対応するブローカのアドレスを見つけます。に接続し、Broker との接続を確立してメッセージを取得し、消費します。
就跟上面的图一样,整体的工作流程还是比较简单的,这里我简化了很多概念,主要是为了好理解。
环境搭建
终于讲完了一些简单的概念,接下来就来搭建一套RocketMQ的环境。
通过上面分析,我们知道,在RocketMQ中有NameServer、Broker、生产者、消费者四种角色。而生产者和消费者实际上就是业务系统,所以这里不需要搭建,真正要搭建的就是NameServer和Broker,但是为了方便RocketMQ数据的可视化,这里我多搭建一套可视化的服务。
搭建过程比较简单,按照步骤一步一步来就可以完成,如果提示一些命令不存在,那么直接通过yum安装这些命令就行。
一、准备
需要准备一个linux服务器,需要先安装好JDK
关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
复制代码
下载并解压RocketMQ
1、创建一个目录,用来存放rocketmq相关的东西
mkdir /usr/rocketmq
cd /usr/rocketmq
复制代码
2、下载并解压rocketmq
下载
wget https://archive.apache.org/dist/rocketmq/4.7.1/rocketmq-all-4.7.1-bin-release.zip
复制代码
解压
unzip rocketmq-all-4.7.1-bin-release.zip
复制代码
看到这一个文件夹就完成了
然后进入rocketmq-all-4.7.1-bin-release文件夹
cd rocketmq-all-4.7.1-bin-release
复制代码
RocketMQ的东西都在这了
二、搭建NameServer
修改jvm参数
在启动NameServer之前,强烈建议修改一下启动时的jvm参数,因为默认的参数都比较大,为了避免内存不够,建议修改小,当然,如果你的内存足够大,可以忽略。
vi bin/runserver.sh
复制代码
修改画圈的这一行
这里你可以直接修改成跟我一样的
-server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=50m
复制代码
启动NameServer
修改完之后,执行如下命令就可以启动NameServer了
nohup sh bin/mqnamesrv &
复制代码
查看NameServer日志
tail -f ~/logs/rocketmqlogs/namesrv.log
复制代码
如果看到如下的日志,就说明启动成功了
NameServer日志
三、搭建Broker
这里启动单机版的Broker
修改jvm参数
跟启动NameServer一样,也建议去修改jvm参数
vi bin/runbroker.sh
复制代码
将画圈的地方设置小点,当然也别太小啊
当然你还是可以跟我设置的一样
-server -Xms1g -Xmx1g -Xmn512m
复制代码
修改Broker配置文件broker.conf
这里需要改一下Broker配置文件,需要指定NameServer的地址,因为需要Broker需要往NameServer注册
vi conf/broker.conf
复制代码
Broker配置文件
Broker配置文件
这里就能看出Broker的配置了,什么Broker集群的名称啊,Broker的名称啊,Broker的id啊,都跟前面说的对上了。
在文件末尾追加地址
namesrvAddr = localhost:9876
复制代码
因为NameServer跟Broker在同一台机器,所以是localhost,NameServer端口默认的是9876。
不过这里我还建议再修改一处信息,因为Broker向NameServer进行注册的时候,带过去的ip如果不指定就会自动获取,但是自动获取的有个坑,就是有可能你的电脑无法访问到这个自动获取的ip,所以我建议手动指定你的电脑可以访问到的服务器ip。
我的虚拟机的ip是192.168.200.143,所以就指定为192.168.200.143,如下
brokerIP1 = 192.168.200.143
brokerIP2 = 192.168.200.143
复制代码
如果以上都配置的话,最终的配置文件应该如下,红圈的为新加的
启动Broker
nohup sh bin/mqbroker -c conf/broker.conf &
复制代码
-c 参数就是指定配置文件
查看日志
tail -f ~/logs/rocketmqlogs/broker.log
复制代码
当看到如下日志就说明启动成功了
四、搭建可视化控制台
其实前面NameServer和Broker搭建完成之后,就可以用来收发消息了,但是为了更加直观,可以搭一套可视化的服务。
可视化服务其实就是一个jar包,启动就行了。
jar包可以从这获取
链接:pan.baidu.com/s/16s1qwY2q…
提取码:s0sd
将jar包上传到服务器,放到/usr/rocketmq的目录底下,当然放哪都无所谓,这里只是为了方便,因为rocketmq的东西都在这里
然后进入/usr/rocketmq下,执行如下命名
nohup java -jar -server -Xms256m -Xmx256m -Drocketmq.config.namesrvAddr=localhost:9876 -Dserver.port=8088 rocketmq-console-ng-1.0.1.jar &
复制代码
rocketmq.config.namesrvAddr就是用来指定NameServer的地址的
查看日志
tail -f ~/logs/consolelogs/rocketmq-console.log
复制代码
当看到如下日志,就说明启动成功了
然后在浏览器中输入http://linux服务器的ip:8088/就可以看到控制台了,如果无法访问,可以看看防火墙有没有关闭
右上角可以把语言切换成中文
Broker集群信息
topic信息
通过控制台可以查看生产者、消费者、Broker集群等信息,非常直观。
功能很多,这里就不一一介绍了。
测试
环境搭好之后,就可以进行测试了。
引入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
复制代码
生产者发送消息
public class Producer {
public static void main(String[] args) throws Exception {
//创建一个生产者,指定生产者组为sanyouProducer
DefaultMQProducer producer = new DefaultMQProducer("sanyouProducer");
// 指定NameServer的地址
producer.setNamesrvAddr("192.168.200.143:9876");
// 第一次发送可能会超时,我设置的比较大
producer.setSendMsgTimeout(60000);
// 启动生产者
producer.start();
// 创建一条消息
// topic为 sanyouTopic
// 消息内容为 三友的java日记
// tags 为 TagA
Message msg = new Message("sanyouTopic", "TagA", "三友的java日记 ".getBytes(RemotingHelper.DEFAULT_CHARSET));
// 发送消息并得到消息的发送结果,然后打印
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
// 关闭生产者
producer.shutdown();
}
}
复制代码
- 构建一个消息生产者DefaultMQProducer实例,然后指定生产者组为sanyouProducer;
- 指定NameServer的地址:服务器的ip:9876,因为需要从NameServer拉取Broker的信息
- producer.start() 启动生产者
- 构建一个内容为三友的java日记的消息,然后指定这个消息往sanyouTopic这个topic发送
- producer.send(msg):发送消息,打印结果
- 关闭生产者
运行结果如下
SendResult [sendStatus=SEND_OK, msgId=C0A81FAF54F818B4AAC2475FD2010000, offsetMsgId=C0A8C88F00002A9F000000000009AE55, messageQueue=MessageQueue [topic=sanyouTopic, brokerName=broker-a, queueId=0], queueOffset=0]
复制代码
sendStatus=SEND_OK 说明发送成功了,此时就能后控制台看到未消费的消息了。
到控制台看到消息那块,然后选定发送的topic,查询的时间范围手动再选一下,不选就查不出来(我怀疑这是个bug),然后查询就能看到了一条消息。
然后点击一下MESSAGE DETAIL就能够看到详情。
这里就能看到发送消息的详细信息。
左下角消息的消费的消费,因为我们还没有消费者订阅这个topic,所以左下角没数据。
消费者消费消息
public class Consumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
// 通过push模式消费消息,指定消费者组
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("sanyouConsumer");
// 指定NameServer的地址
consumer.setNamesrvAddr("192.168.200.143:9876");
// 订阅这个topic下的所有的消息
consumer.subscribe("sanyouTopic", "*");
// 注册一个消费的监听器,当有消息的时候,会回调这个监听器来消费消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.printf("消费消息:%s", new String(msg.getBody()) + "\n");
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 启动消费者
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
复制代码
- 创建一个消费者实例对象,指定消费者组为sanyouConsumer
- 指定NameServer的地址:服务器的ip:9876
- 订阅 sanyouTopic 这个topic的所有信息
- consumer.registerMessageListener ,这个很重要,是注册一个监听器,这个监听器是当有消息的时候就会回调这个监听器,处理消息,所以需要用户实现这个接口,然后处理消息。
- 启动消费者
启动之后,消费者就会消费刚才生产者发送的消息,于是控制台就打印出如下信息
Consumer Started.
消费消息:三友的java日记
复制代码
此时再去看控制台
发现被sanyouConsumer这个消费者组给消费了。
SpringBoot环境下集成RocketMQ
集成
在实际项目中肯定不会像上面测试那样用,都是集成SpringBoot的。
1、引入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
复制代码
2、yml配置
rocketmq:
producer:
group: sanyouProducer
name-server: 192.168.200.143:9876
复制代码
3、创建消费者
SpringBoot底下只需要实现RocketMQListener接口,然后加上@RocketMQMessageListener注解即可
@Component
@RocketMQMessageListener(consumerGroup = "sanyouConsumer", topic = "sanyouTopic")
public class SanYouTopicListener implements RocketMQListener<String> {
@Override
public void onMessage(String msg) {
System.out.println("处理消息:" + msg);
}
}
复制代码
@RocketMQMessageListener需要指定消费者属于哪个消费者组,消费哪个topic,NameServer的地址已经通过yml配置文件配置类
4、测试
@SpringBootTest(classes = RocketMQApplication.class)
@RunWith(SpringRunner.class)
public class RocketMQTest {
@Autowired
private RocketMQTemplate template;
@Test
public void send() throws InterruptedException {
template.convertAndSend("sanyouTopic", "三友的java日记");
Thread.sleep(60000);
}
}
复制代码
直接注入一个RocketMQTemplate,然后通过RocketMQTemplate发送消息。
运行结果如下:
处理消息:三友的java日记
复制代码
的确消费到消息了。
原理
其实原理是一样的,只不过在SpringBoot中给封装了一层,让使用起来更加简单。
1、RocketMQTemplate构造代码
所以从这可以看出,最终在构造RocketMQTemplate的时候,传入了一个DefaultMQProducer,所以可想而知,最终RocketMQTemplate发送消息也是通过DefaultMQProducer发送的。
2、@RocketMQMessageListener 注解处理
从这可以看出,会为每一个加了@RocketMQMessageListener注解的对象创建一个DefaultMQPushConsumer,所以最终也是通过DefaultMQPushConsumer消费消息的。
至于监听器,是在这
遍历每条消息,然后调用handleMessage,最终会调用实现了RocketMQListener的对象处理消息。
最后
通过上面的理论介绍和实际的环境搭建再加上代码的测试,相信应该可以对RocketMQ有个入门,有兴趣的小伙伴可以手动搭起来,整个过程顺利的话可能就十几二十分钟这样子。
最后我再说一句,从文章整体也可以看出本文没有涉及太深入的一些机制和原理的讲解,比如消息是如何存储的,事务和延迟消息是如何实现的,主从是如何同步的等等,甚至压根就没提到队列这个词,主要是因为我打算后面再写一篇文章,来单独剖析这些机制和原理。
最后的最后,本文所有的代码地址:
github.com/sanyou3/roc…
搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习。
往期热门文章推荐