一些基本概念
首先有哪些角色?
- Producer:消息的发送者;举例:发信者
- Consumer:消息接收者;举例:收信者
- Broker:暂存和传输消息;举例:邮局
- NameServer:管理Broker;举例:各个邮局的管理机构
- Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
- Message Queue:相当于是Topic的分区;用于并行发送和接收消息

生产者(Producer)
消息生产者,负责产生消息,一般由业务系统负责产生消息。
Producer由用户进行分布式部署,消息由Producer通过多种负载均衡模式发送到Broker集群,发送低延时,支持快速失败。
RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送。同步和异步方式均需要Broker返回确认信息,单向发送不需要。
同步发送:同步发送指消息发送方发出数据后会在收到接收方发回响应之后才发下一个数据包。一般用于重要通知消息,例如重要通知邮件、营销短信。
异步发送:异步发送指发送方发出数据后,不等接收方发回响应,接着发送下个数据包,一般用于可能链路耗时较长而对响应时间敏感的业务场景,例如用户视频上传后通知启动转码服务。
单向发送:单向发送是指只负责发送消息而不等待服务器回应且没有回调函数触发,适用于某些耗时非常短但对可靠性要求并不高的场景,例如日志收集。
消费者(Consumer)
消息消费者,负责消费消息,一般是后台系统负责异步消费。
负责消费消息,一般是后台系统负责异步消费。一个消息消费者会从Broker服务器拉取消息、并将其提供给应用程序。从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费。
Pull:拉取型消费者(Pull Consumer)主动从消息服务器拉取信息,只要批量拉取到消息,用户应用就会启动消费过程,所以 Pull 称为主动消费型。
Push:推送型消费者(Push Consumer)封装了消息的拉取、消费进度和其他的内部维护工作,将消息到达时执行的回调接口留给用户应用程序来实现。所以 Push 称为被动消费类型,但从实现上看还是从消息服务器中拉取消息,不同于 Pull 的是 Push 首先要注册消费监听器,当监听器处触发后才开始消费消息。
代理商(Broker)
消息中转角色,负责存储消息,转发消息。
消息中转角色,负责存储消息、转发消息。代理服务器在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。
单个Broker节点与所有的NameServer节点保持长连接及心跳,并会定时将Topic信息注册到NameServer,顺带一提底层的通信和连接都是基于Netty实现的。
Broker消息存储,以Topic为纬度支持轻量级的队列,单机可以支撑上万队列规模,支持消息推拉模型。
官网上有数据显示:具有上亿级消息堆积能力,同时可严格保证消息的有序性。
主题(Topic)
表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。
名字服务(Name Server)
名称服务充当路由消息的提供者。生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但相互独立,没有信息交换。
生产者组(Producer Group)
同一类Producer的集合,这类Producer发送同一类消息且发送逻辑一致。如果发送的是事务消息且原始生产者在发送之后崩溃,则Broker服务器会联系同一生产者组的其他生产者实例以提交或回溯消费。
消费者组(Consumer Group)
同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。
集群消费(Clustering)
集群消费模式下,相同Consumer Group的每个Consumer实例平均分摊消息。
广播消费(Broadcasting)
广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。
普通顺序消息(Normal Ordered Message)
普通顺序消费模式下,消费者通过同一个消费队列收到的消息是有顺序的,不同消息队列收到的消息则可能是无顺序的。
严格顺序消息(Strictly Ordered Message)
严格顺序消息模式下,消费者收到的所有消息均是有顺序的。
消息(Message)
消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。RocketMQ中每个消息拥有唯一的Message ID,且可以携带具有业务标识的Key。系统提供了通过Message ID和Key查询消息的功能。
标签(Tag)
为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。
说句实在话, 阿里的文档不太好看懂, 至少学习rocketmq看文档不行, 乱七八糟(就感觉一个文档多个人写的, 但这些人又不相互交流), 对比起来我还是喜欢rabbitmq的文档
特性
角色和特性了解了解就好, 乱, 需要画图理解
架构设计
技术架构
RocketMQ架构上主要分为四部分,如上图所示:
-
Producer:消息发布的角色,支持分布式集群方式部署。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
-
Consumer:消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求。
-
NameServer:NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;路由信息管理,每个NameServer将保存关于- Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息。
-
BrokerServer:Broker主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker包含了以下几个重要子模块。
- Remoting Module:整个Broker的实体,负责处理来自clients端的请求。
- Client Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息
- Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。
- HA Service:高可用服务,提供Master Broker 和 Slave Broker之间的数据同步功能。
- Index Service:根据特定的Message key对投递到Broker的消息进行索引服务,以提供消息的快速查询。

部署架构

RocketMQ 网络部署特点
-
NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
-
Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId 来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。 注意:当前RocketMQ版本在部署架构上支持一Master多Slave,但只有BrokerId=1的从服务器才会参与消息的读负载。
-
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic 服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
-
Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,消费者在向Master拉取消息时,Master服务器会根据拉取偏移量与最大偏移量的距离(判断是否读老消息,产生读I/O),以及从服务器是否可读等因素建议下一次是从Master还是Slave拉取。
结合部署架构图,描述集群工作流程:
- 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。
- Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。
- 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。
- Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。
- Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。
集群特点
-
NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
-
Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。
-
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
-
Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
单机使用docker配置单个rocketmq
docker拉取并安装
docker pull foxiswho/rocketmq:server-4.7.0
docker pull foxiswho/rocketmq:broker-4.7.0
docker pull styletang/rocketmq-console-ng
执行配置创建docker
docker run -d -p 9876:9876 --name rmqnamesrv \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqnamesrv01/logs:/root/logs \
--user=root --privileged=true \
foxiswho/rocketmq:server-4.7.0
docker run -d -p 10911:10911 -p 10909:10909 --name rmqbroker \
-e "NAMESRV_ADDR=192.168.0.155:9876" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqbroker01/logs:/root/logs \
-v /home/bangiao/rmqbroker01/store:/root/store \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-a.properties:/etc/rocketmq/broker.conf \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run --name=rockemq-console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.0.155:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8180:8080 -t styletang/rocketmq-console-ng
至于其中的
broker-a.properties文件的配置如下
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
其中
brokerIP1是我虚拟机的地址
现在我们访问http://192.168.0.155:8180/
写个简单的HelloWorld
Consumer code:
public class HelloWorldConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");
consumer.setNamesrvAddr("192.168.0.155:9876");
consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
Producer code:
public class HelloWorldSyncProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.setNamesrvAddr("192.168.0.155:9876");
producer.start();
for (int i = 0; i < 100; i++) {
Message msg = new Message("TopicTest", "TagA", ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
不用 docker 方式的单机配置
但由于我虚拟机安装的
jdk版本是jdk11所以需要修改下配置, 如果jdk版本不是11则无所谓了这里我们参考了 Docker部署RocketMQ(JDK11)里面的方法, 当然如果是jdk11版本的你也可以不需要参考, 直接运行sh脚本就能够发现问题, 跟着提示删除掉也行
去rocketmq官网下载zip然后上传到虚拟机上
修改bin/runserver.sh
GC的机制,JDK11使用G1不使用CMS,删除JVM参数:
-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:-UseParNewGC -XX:+PrintGCDetails
删除GCLog相关整行:
JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
JDK11不再独立分jre,删除整行:
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
修改:`-Xloggc` 为 `-Xlog:gc`
修改类路径,否则提示找不到mainClass,增加一行:
export CLASSPATH=${BASE_DIR}/lib/rocketmq-namesrv-4.7.1.jar:${BASE_DIR}/lib/*:${BASE_DIR}/conf:${CLASSPATH}
删除整行:
export CLASSPATH=.:${BASE_DIR}/conf:${CLASSPATH}
修改为: (原本的配置太高了. 要几G)
JAVA_OPT="${JAVA_OPT} -server -Xms128M -Xmx128M -Xmn128m"
修改runbroker.sh
删除:#export CLASSPATH=.:${BASE_DIR}/conf:${CLASSPATH}
增加:export CLASSPATH=${BASE_DIR}/lib/rocketmq-namesrv-4.7.1.jar:${BASE_DIR}/lib/*:${BASE_DIR}/conf:${CLASSPATH}
删除:JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
删除:-XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+PrintGCDetails
删除:JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
修改原先的配置为:(原来的要好几G)
JAVA_OPT="${JAVA_OPT} -server -Xms128m -Xmx128m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
再去修改tools.sh
删除:JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib:${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext"
删除:#export CLASSPATH=.:${BASE_DIR}/conf:${CLASSPATH}
增加:export CLASSPATH=${BASE_DIR}/lib/rocketmq-namesrv-4.7.1.jar:${BASE_DIR}/lib/*:${BASE_DIR}/conf:${CLASSPATH}
之后就可以直接这个代码:
# 单机配置
nohup sh bin/mqnamesrv &
nohup sh bin/mqbroker -n 192.168.0.155:9876 -c conf/2m-noslave/broker-a.properties &
这里我选择使用docker的rocketmq console进行监控
docker run --name=rockemq-console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.0.155:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8180:8080 -t styletang/rocketmq-console-ng
使用docker配置rocketmq集群
docker pull foxiswho/rocketmq:server-4.7.0
docker pull foxiswho/rocketmq:broker-4.7.0
docker pull styletang/rocketmq-console-ng
首先配置两主broker方式集群
docker run -d -p 9876:9876 --name rmqnamesrv \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqnamesrv01/logs:/root/logs \
--user=root --privileged=true \
foxiswho/rocketmq:server-4.7.0
docker run -d -p 10911:10911 -p 10909:10909 --name rmqbroker \
-e "NAMESRV_ADDR=192.168.0.155:9876" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqbroker01/logs:/root/logs \
-v /home/bangiao/rmqbroker01/store:/root/store \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-a.properties:/etc/rocketmq/broker.conf \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d -p 20911:20911 -p 20909:20909 --name rmqbroker2 \
-e "NAMESRV_ADDR=192.168.0.155:9876" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqbroker02/logs:/root/logs \
-v /home/bangiao/rmqbroker02/store:/root/store \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-b.properties:/etc/rocketmq/broker.conf \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run --name=rockemq-console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.0.155:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8180:8080 -t styletang/rocketmq-console-ng
不过需要关注下配置
# broker-a.properties
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=10911
autoCreateTopicEnable=true
# broker-b.properties
brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=20911
autoCreateTopicEnable=true
用程序测试下是否可用
public class ClusterProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("cluster-group");
producer.setNamesrvAddr("192.168.0.155:9876");
producer.setSendMsgTimeout(10000);
producer.start();
AtomicLong aLong = new AtomicLong();
for (int i = 0; i < 1000000000; i++) {
Message message = new Message("cluster-topic", (aLong.incrementAndGet() + "").getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult result = producer.send(message);
System.out.println(result.getSendStatus());
}
TimeUnit.SECONDS.sleep(10);
producer.shutdown();
}
}
public class ClusterConsumer {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cluster-group");
consumer.setNamesrvAddr("192.168.0.155:9876");
consumer.subscribe("cluster-topic", "*");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
consumer.setConsumeTimeout(1);
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
msgs.stream().parallel().forEach(messageExt -> {
String message = null;
try {
message = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(message);
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.err.println("consumer start.");
latch.await();
}
}
完成两个master方式的配置, 现在我们在这个基础上添加一个nameserver
但这样呢, 需要重新编写docker指令
docker run -d -p 9876:9876 --name rmqnamesrv \
-v /home/bangiao/rmqnamesrv01/logs:/root/logs \
--user=root --privileged=true foxiswho/rocketmq:server-4.7.0
docker run -d -p 9877:9876 --name rmqnamesrv2 \
-v /home/bangiao/rmqnamesrv02/logs:/root/logs \
--user=root --privileged=true foxiswho/rocketmq:server-4.7.0
docker run -d -p 10911:10911 -p 10909:10909 --name rmqbroker \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-a.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker01/logs:/root/logs \
-v /home/bangiao/rmqbroker01/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d -p 20911:20911 -p 20909:20909 --name rmqbroker2 \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-b.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker02/logs:/root/logs \
-v /home/bangiao/rmqbroker02/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d --name=rockemq-console \
--link rmqnamesrv:namesrv --link rmqnamesrv2:namesrv2 \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=namesrv:9876;namesrv2:9877 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8180:8080 -t styletang/rocketmq-console-ng
试试看能不能用
改造下前面的代码
consumer.setNamesrvAddr("192.168.0.155:9876;192.168.0.155:9877");
producer.setNamesrvAddr("192.168.0.155:9876;192.168.0.155:9877");
顺带试试关闭一个broker启动一个broker, 然后再反之试试, 看下是否能够发送消息, 在切换的过程中可以查看下日志, 比如看下nameserver之类的日志
2020-08-19 13:36:36 INFO RemotingExecutorThread_2 - new broker registered, 192.168.0.155:10911 HAServer: 172.17.0.5:10912发现有日志, 所以估计是没有问题的
现在试试关闭和启动nameserver试试
我的测试都没有问题, 支持双nameserver双broker master都可以测试通过
那么现在把它改造成两主两从两nameserver
docker run -d -p 9876:9876 --name rmqnamesrv \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqnamesrv01/logs:/root/logs \
--user=root --privileged=true foxiswho/rocketmq:server-4.7.0
docker run -d -p 9877:9876 --name rmqnamesrv2 \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/bangiao/rmqnamesrv02/logs:/root/logs \
--user=root --privileged=true foxiswho/rocketmq:server-4.7.0
docker run -d -p 10911:10911 -p 10909:10909 --name rmqbroker \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-a.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker01/logs:/root/logs \
-v /home/bangiao/rmqbroker01/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d -p 20911:20911 -p 20909:20909 --name rmqbroker_s \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-a-s.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker02/logs:/root/logs \
-v /home/bangiao/rmqbroker02/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d -p 30911:30911 -p 30909:30909 --name rmqbroker2 \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-b.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker03/logs:/root/logs \
-v /home/bangiao/rmqbroker03/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d -p 40911:40911 -p 40909:40909 --name rmqbroker2_s \
-e "NAMESRV_ADDR=192.168.0.155:9876;192.168.0.155:9877" \
-e "JAVA_OPTS=-server -Xms128m -Xmx128m -Xmn128m -Duser.home=/opt" \
-e "JAVA_OPT=-server -Xms128m -Xmx128m -Xmn128m" \
-v /home/rocketmq-all-4.7.1/conf/2m-2s-async/broker-b-s.properties:/etc/rocketmq/broker.conf \
-v /home/bangiao/rmqbroker04/logs:/root/logs \
-v /home/bangiao/rmqbroker04/store:/root/store \
--user=root --privileged=true \
foxiswho/rocketmq:broker-4.7.0
docker run -d --name=rockemq-console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.0.155:9876;192.168.0.155:9877 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8180:8080 -t styletang/rocketmq-console-ng
配置文件两个master
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=10911
autoCreateTopicEnable=true
brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=30911
autoCreateTopicEnable=true
配置文件两个slave
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=20911
autoCreateTopicEnable=true
brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=1
deleteWhen=04
fileReservedTime=48
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.0.155
listenPort=40911
autoCreateTopicEnable=true
但我在单机上运行, 会出现问题, 不论我如何配置master停止后, 从节点slave无法变成主节点进行工作, 老是找不到topic
一直使用指令, 有空改成docker-compose
对了如果单机配置集群, 把jdk运行的空间调小一点, 要不然会出现
/home/rocketmq/rocketmq-4.7.0/bin/runbroker.sh: line 158: 28 Killed $JAVA ${JAVA_OPT} $@错误
基本上没发现新的问题, 等有问题了再修复, 我建议先把本地非docker的rocketmq集群教程看一遍再去试试docker上搭建, 这种比较方便, rocketmq阿里官网上的几个例子都是错的, 建议还是看博客学习比较好, 或者买书学习
最后测试源码
public class ClusterConsumer {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cluster-group");
consumer.setNamesrvAddr("192.168.0.155:9876;192.168.0.155:9877");
consumer.subscribe("cluster-topic", "*");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
consumer.setConsumeTimeout(1);
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
msgs.stream().parallel().forEach(messageExt -> {
String message = null;
try {
message = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(message);
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.err.println("consumer start.");
latch.await();
}
}
public class ClusterProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("cluster-group");
producer.setNamesrvAddr("192.168.0.155:9876;192.168.0.155:9877");
producer.setSendMsgTimeout(10000);
producer.start();
AtomicLong aLong = new AtomicLong();
for (int i = 0; i < 10; i++) {
Message message = new Message("cluster-topic", (aLong.incrementAndGet() + "").getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult result = producer.send(message);
System.out.println(result.getSendStatus());
}
TimeUnit.SECONDS.sleep(10);
producer.shutdown();
}
}