如上图所示, RocketMQ的部署结构有以下特点:
如上图所示,RocketMQ的逻辑部署结构有Producer和Consumer两个特点。
Producer Group作用如下:
# 安装 jdk1.8
> yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
配置JDK环境
> vim /etc/profile
# set jdk path
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH
export JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin
export PATH=$PATH:${JAVA_PATH}
> source /etc/profile
否则执行 bin/mqadmin 等RocketMQ命令会报错
> sh bin/mqadmin topicList -n 192.168.160.2:9876
RocketMQLog:WARN No appenders could be found for logger (io.netty.util.internal.PlatformDependent0).
RocketMQLog:WARN Please initialize the logger system properly.
org.apache.rocketmq.tools.command.SubCommandException: TopicListSubCommand command failed
at org.apache.rocketmq.tools.command.topic.TopicListSubCommand.execute(TopicListSubCommand.java:113)
at org.apache.rocketmq.tools.command.MQAdminStartup.main0(MQAdminStartup.java:139)
at org.apache.rocketmq.tools.command.MQAdminStartup.main(MQAdminStartup.java:90)
# https://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq 查看版本 目前使用 4.6.1
> wget https://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.6.1/rocketmq-all-4.6.1-bin-release.zip
> uzip rocketmq-all-4.6.1-bin-release.zip
> cd rocketmq-all-4.6.1-bin-release
使用默认提供的 conf/broker.conf 配置文件
> vim conf/broker.conf
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
指定 broker 自己监听的 IP 地址,以及 namesrv 的地址和端口,不然 rocketmq-console、mqadmin 客户端等会因为跨域而连接不上,只需要在conf/broker.conf
配置文件中添加 brokerIP1 和 namesrvAddr 配置即可:
# 支持跨域
brokerIP1 = 192.168.160.2
namesrvAddr = 192.168.160.2:9876
关闭selinux
# 首先查看 SELinux功能是否开启
> getenforce
# 如果显示Permissive 或者 Disabled 该步骤直接跳过,如果是enforcing ,进行下一步
> vim /etc/selinux/config # 或者 vim /etc/sysconfig/selinux
# 设置 SELINUX=enforcing 为 SELINUX=permissive(/disabled)
setenforce 0 # 让设置生效
getenforce # 查看
> nohup sh bin/mqnamesrv &
> tail -f ~/logs/rocketmqlogs/namesrv.log
> nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
> tail -f ~/logs/rocketmqlogs/broker.log
> export NAMESRV_ADDR=localhost:9876
> sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
SendResult [sendStatus=SEND_OK, msgId= ...
> sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
ConsumeMessageThread_%d Receive New Messages: [MessageExt...
> sh bin/mqshutdown broker
The mqbroker(36695) is running...
Send shutdown request to mqbroker(36695) OK
> sh bin/mqshutdown namesrv
The mqnamesrv(36664) is running...
Send shutdown request to mqnamesrv(36664) OK
> netstat -ntlp | grep java
tcp6 0 0 :::9876 :::* LISTEN 2247/java
tcp6 0 0 :::10909 :::* LISTEN 2277/java
tcp6 0 0 :::10911 :::* LISTEN 2277/java
tcp6 0 0 :::10912 :::* LISTEN 2277/java
# 根据实际情况放开端口,NameServer 端口、Broker 监听端口,必设置
firewall-cmd --zone=public --add-port=9876/tcp --permanent
firewall-cmd --zone=public --add-port=10911/tcp --permanent
firewall-cmd --reload
NameServer 的 9876 端口可正常映射
Broker 的 4 个端口需要内外网一致,即监听 13174,则该端口映射出去的端口也是 13174
broker 还会监听另外几个端口,同样的都是本地监听和映射的端口相同即可。
# 公网 IP
brokerIP1 = xx.xx.xx.xx
# 对应 broker 监听端口,默认为10911,调整后,影响4个端口
# 10909/10910/10911/10912 -> 13172/13173/13174/13175
listenPort = 13174
namesrvAddr = 192.168.160.2:9876
client - consumer 端断开,broker log 会有如下Error日志,强迫症表示后续继续研究
> tail -f ~/logs/rocketmqlogs/broker.log
2020-03-05 10:24:35 INFO NettyEventExecutor - NETTY EVENT: remove channel[ClientChannelInfo [channel=[id: 0x63017fd9, L:/192.168.160.2:10911 ! R:/192.168.150.144:54648], clientId=192.168.126.1@DEFAULT, language=JAVA, version=335, lastUpdateTimestamp=1583375060273]][192.168.150.144:54648] from ProducerManager groupChannelTable, producer group: CLIENT_INNER_PRODUCER
2020-03-05 10:24:35 WARN NettyEventExecutor - NETTY EVENT: remove not active channel[ClientChannelInfo [channel=[id: 0x63017fd9, L:/192.168.160.2:10911 ! R:/192.168.150.144:54648], clientId=192.168.126.1@DEFAULT, language=JAVA, version=335, lastUpdateTimestamp=1583375060273]] from ConsumerGroupInfo groupChannelTable, consumer group: Producer
2020-03-05 10:24:35 INFO NettyEventExecutor - unregister consumer ok, no any connection, and remove consumer group, Producer
2020-03-05 10:24:51 WARN PullMessageThread_4 - the consumer's group info not exist, group: Producer
2020-03-05 10:24:51 WARN PullMessageThread_5 - the consumer's group info not exist, group: Producer
2020-03-05 10:24:51 WARN PullMessageThread_1 - the consumer's group info not exist, group: Producer
2020-03-05 10:24:51 WARN PullMessageThread_3 - the consumer's group info not exist, group: Producer
2020-03-05 10:24:51 ERROR NettyServerNIOSelector_3_1 - processRequestWrapper response to /192.168.150.144:54648 failed
java.nio.channels.ClosedChannelException: null
at io.netty.channel.AbstractChannel$AbstractUnsafe.write(...)(Unknown Source) ~[netty-all-4.0.42.Final.jar:4.0.42.Final]
2020-03-05 10:24:51 ERROR NettyServerNIOSelector_3_1 - RemotingCommand [code=11, language=JAVA, version=335, opaque=1040, flag(B)=0, remark=null, extFields={queueId=1, maxMsgNums=32, sysFlag=2, suspendTimeoutMillis=15000, commitOffset=0, topic=newTopic, queueOffset=75, expressionType=TAG, subVersion=1583374069385, consumerGroup=Producer}, serializeTypeCurrentRPC=JSON]
2020-03-05 10:24:51 ERROR NettyServerNIOSelector_3_1 - RemotingCommand [code=24, language=JAVA, version=335, opaque=1040, flag(B)=1, remark=the consumer's group info not exist
See http://rocketmq.apache.org/docs/faq/ for further details., extFields=null, serializeTypeCurrentRPC=JSON]
在pom.xml中添加(使用jdk1.8)
<!-- RocketMq客户端相关依赖 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.6.1</version>
</dependency>
resources文件中properties中写入
# NameServer地址
apache.rocketmq.namesrvAddr=192.168.160.2:9876
# 生产者的组名
apache.rocketmq.producer.producerGroup=Producer
# 生产者的topic
apache.rocketmq.producer.topic=newTopic
# 生产者的tags
apache.rocketmq.producer.tags=newTag
# 消费者的组名
apache.rocketmq.consumer.consumerGroup=Producer
# 消费者的topic
apache.rocketmq.consumer.topic=newTopic
# 消费者的subExpression
apache.rocketmq.consumer.subExpression=newTag
创建 Producer.java,代码如下
@Component
public class Producer {
/**
* 生产者的组名
*/
@Value("${apache.rocketmq.producer.producerGroup}")
private String producerGroup;
/**
* NameServer 地址
*/
@Value("${apache.rocketmq.namesrvAddr}")
private String namesrvAddr;
@Value("${apache.rocketmq.producer.topic}")
private String topic;
@Value("${apache.rocketmq.producer.tags}")
private String tags;
@PostConstruct
public void defaultMQProducer() {
System.out.println("---------- RocketMQ Producer Start ----------");
System.out.println("producerGroup:" + producerGroup + ",namesrvAddr:" + namesrvAddr);
//生产者的组名
DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
//指定NameServer地址,多个地址以 ; 隔开
producer.setNamesrvAddr(namesrvAddr);
try {
/**
* Producer对象在使用之前必须要调用start初始化,初始化一次即可
* 注意:切记不可以在每次发送消息时,都调用start方法
*/
producer.start();
for (int i = 0; i < 100; i++) {
String messageBody = "我是消息内容:" + i;
String message = new String(messageBody.getBytes(), "utf-8");
//构建消息
Message msg = new Message(topic, tags, "key_" + i /* Keys */, message.getBytes());
//发送消息
SendResult result = producer.send(msg);
System.out.println("[Producer] 发送响应:MsgId:" + result.getMsgId() + ", SendStatus:" + result.getSendStatus());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
producer.shutdown();
}
}
}
创建 Consumer.java,代码如下
@Component
public class Consumer {
/**
* 消费者的组名
*/
@Value("${apache.rocketmq.consumer.consumerGroup}")
private String consumerGroup;
/**
* NameServer地址
*/
@Value("${apache.rocketmq.namesrvAddr}")
private String namesrvAddr;
@Value("${apache.rocketmq.consumer.topic}")
private String topic;
@Value("${apache.rocketmq.consumer.subExpression}")
private String subExpression;
@PostConstruct
public void defaultMQPushConsumer() {
System.out.println("---------- RocketMQ Consumer Start ----------");
System.out.println("consumerGroup:" + consumerGroup + ",namesrvAddr:" + namesrvAddr);
//消费者的组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
//指定NameServer地址,多个地址以 ; 隔开
consumer.setNamesrvAddr(namesrvAddr);
try {
//订阅PushTopic下Tag为push的消息
consumer.subscribe(topic, subExpression);
//设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
//如果非第一次启动,那么按照上次消费的位置继续消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//可以修改每次消费消息的数量,默认设置是每次消费一条
consumer.setConsumeMessageBatchMaxSize(1);
//设置消费模式为广播
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext context) {
try {
for (MessageExt messageExt : list) {
//System.out.println("messageExt: " + messageExt);//输出消息内容
String messageBody = new String(messageExt.getBody(), "utf-8");
System.out.println("[Consumer] 消费响应:MsgId:" + messageExt.getMsgId() + ", MsgBody:" + messageBody);//输出消息内容
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再试
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消费成功
}
});
consumer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
红色标识符,alt + enter 引入相应的包
Github RocketMQDemo
Apache RocketMQ 官方文档
阿里中间件博客-RocketMQ
RocketMQ命令使用详解
RocketMQ Client踩坑