项目据说需要使用消息中间件rocketmq,所以需弄个简单示例。至于为啥用docker-compose,那当然是因为它可以整体部署啦。经过这次经历,觉得很有必要将官方的文档路径记录下来。博客上每个人都是以自己的环境搭建的,很多情况下都不能完美符合自己的环境。只能通过多个博客以及相关官方文档,相互印证,得出符合自己的配置。
rocketmq简单了解:
1 什么场景需要使用消息中间件
用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
2 生产者
消息生产者,声明自己是消息生成者。一般由业务系统负责生产消息。
3 消费者
声明自己是消费者,一般是后台系统负责异步消费。
4 主题(Topic)
表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位
5 代理服务器(Broker Server)
消息中转角色,负责存储消息、转发消息。代理服务器在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。
6 名字服务(Name Server)
名称服务充当路由消息的提供者。生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但相互独立,没有信息交换。
其他详细信息请查看rocketmq的github官方文档地址:https://github.com/apache/rocketmq/tree/master/docs/cn
springboot应用两个,一个作为消息生产者,一个作为消息消费者
装了docker-compose,docker 的虚拟机一台。
注:不要问我为啥不弄两台虚拟机,就是偷懒。每个docker都有自己的ip,也可以模拟集群中宕机其中一台的情况。本机网络和虚拟机网络为同一个网段,虚拟机内容器之间为同一个网段。需要注意网络连接,这个会具体说明。
docker 安装(略)
docker-compose文档在docker官网地址:https://docs.docker.com/compose/
安装说明在:https://docs.docker.com/compose/install/
下载docker-compose:
sudo curl -L “https://github.com/docker/compose/releases/download/1.27.4/docker-compose- ( u n a m e − s ) − (uname -s)- (uname−s)−(uname -m)” -o /usr/local/bin/docker-compose
将可执行权限应用于二进制文件:
sudo chmod +x /usr/local/bin/docker-compose
查看安装:docker-compose --version
docker pull rocketmqinc/rocketmq-broker:4.5.0-alpine
docker pull apacherocketmq/rocketmq-console:2.0.0
注:控制台单独一个镜像rocketmq-console,namesrv和broker同一个镜像rocketmq-broker。都是在docker官网找的,比较新的,下载量较大的。我下载的时候必须加版本号。默认的latest不行,会报没有这个版本。
新建挂载目录用于在虚拟机内同步容器的日志和配置文件,由于镜像的不同会导致容器内配置文件的路径不同。如果你使用的其他版本的镜像,需要在创建好的容器里通过一层层cd的方式找到对应的日志和配置文件目录(PS:应该可以在dockerfile文件中寻找)。
虚拟机(相对于docker称为宿主机)内新建目录:
我习惯以对象的方式思考问题,所以我的文件目录方式可能和别人不大一样。这个没关系,看个人。具体目录如下:
新建文件docker-compose.yml,内容先空着,这个是docker-compose启动时的配置文件,需要在这个配置文件目录下启动。所以不要问这个docker-compose.yml放哪里,放哪里都可以,看怎样更合适。因为这个是为整个rocketmq环境的配置,所以我选择放rocketmq目录下。
/home/rocketmq/docker-compose.yml
nameserver-a 日志和数据存储目录
/home/rocketmq/nameserver-a/logs
/home/rocketmq/nameserver-a/store
nameserver-b 日志和数据存储目录
/home/rocketmq/nameserver-b/logs
/home/rocketmq/nameserver-b/store
broker-a 日志、数据存储目录以及配置文件(配置文件先空文本)
/home/rocketmq/broker-a/logs
/home/rocketmq/broker-a/store
/home/rocketmq/broker-a/conf/broker.conf
broker-b 日志、数据存储目录以及配置文件(配置文件先空文本)
/home/rocketmq/broker-b
/home/rocketmq/broker-b
/home/rocketmq/broker-b/conf/broker.conf
其他博客有提到用关闭防火墙的方式。但是虚拟机重启后,防火墙会重新打开。
所以还是开放端口来得好(开放的端口都是下面会用到的容器映射宿主机端口)。
firewall-cmd --zone=public --add-port=10909/tcp --permanent
firewall-cmd --zone=public --add-port=10911/tcp --permanent
firewall-cmd --zone=public --add-port=10912/tcp --permanent
firewall-cmd --zone=public --add-port=9876/tcp --permanent
firewall-cmd --zone=public --add-port=9877/tcp --permanent
#重启防火墙
firewall-cmd --reload
#重启docker
service docker restart
version: '3.5'
services:
#namesrv 名字服务集群
rmqnamesrv-a:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqnamesrv-a
ports:
- 9876:9876
volumes:
- /home/rocketmq/nameserver-a/logs:/home/rocketmq/logs
- /home/rocketmq/nameserver-a/store:/home/rocketmq/store
command: sh mqnamesrv
# 因为容器ip的不确定性,为docker-compose里的容器专门定义网路rmq(网络定义在最下边),以及各自容器的网络别名
networks:
rmq:
aliases:
- rmqnamesrv-a
rmqnamesrv-b:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqnamesrv-b
ports:
- 9877:9876 #同一个虚拟机上namesrv对外端口要不一致
volumes:
- /home/rocketmq/nameserver-b/logs:/home/rocketmq/logs
- /home/rocketmq/nameserver-b/store:/home/rocketmq/store
command: sh mqnamesrv
networks:
rmq:
aliases:
- rmqnamesrv-b
#broker 代理服务器双主模式
rmqbroker-a:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqbroker-a
ports:
- 10909:10909
#- 10911:10911
#- 10912:10912
volumes:
#宿主机目录映射容器目录(宿主机目录就是对应之前建立好的目录)
- /home/rocketmq/broker-a/logs:/home/rocketmq/logs
- /home/rocketmq/broker-a/store:/home/rocketmq/store
# 创建容器时会将宿主机的配置文件同步到容器
#如果发现容器并没有按照自己的conf文件运行,请检查是否容器内有两个配置文件(映射错了文件)
- /home/rocketmq/broker-a/conf/broker-a.conf:/root/rocketmq-4.5.0/conf/broker.conf
environment:
#JAVA_OPTS: "-Duser.home=/opt"
#出现exit 1的错误,提示内存溢出,所以这个应该是有用的
JAVA_OPT_EXT: "-server -Xms256m -Xmx256m -Xmn256m"
#autoCreateTopicEnable=true 我觉得这个配置放到broker.conf中更合理
#command: sh mqbroker -c ../conf/broker.conf autoCreateTopicEnable=true &
command: sh mqbroker -c ../conf/broker.conf
depends_on:
- rmqnamesrv-a
- rmqnamesrv-b
networks:
rmq:
aliases:
- rmqbroker-a
rmqbroker-b:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqbroker-b
ports:
#- 11909:10909
- 10911:10911
#- 10912:10912
volumes:
- /home/rocketmq/broker-b/logs:/home/rocketmq/logs
- /home/rocketmq/broker-b/store:/home/rocketmq/store
- /home/rocketmq/broker-b/conf/broker-b.conf:/root/rocketmq-4.5.0/conf/broker.conf
environment:
JAVA_OPT_EXT: "-server -Xms256m -Xmx256m -Xmn256m"
command: sh mqbroker -c ../conf/broker.conf
networks:
rmq:
aliases:
- rmqbroker-b
#rmqconsole 控制台
rmqconsole:
image: apacherocketmq/rocketmq-console:2.0.0
container_name: rmqconsole
ports:
- 9001:8080
environment:
#Java启动参数中指定Name Server地址
JAVA_OPTS: -Drocketmq.namesrv.addr=rmqnamesrv-a:9876;rmqnamesrv-b:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
depends_on:
- rmqnamesrv-a
- rmqnamesrv-b
networks:
rmq:
aliases:
- rmqconsole
networks:
rmq:
name: rmq
driver: bridge
docker-compose.yml中每个配置项的具体配置内容可以去查看官方文档:https://docs.docker.com/compose/ 。
docker-compose.yml把多个容器的镜像拉取、容器命名、创建容器、启动容器,容器配置等到一个文件中去,以达到一键部署和运行多个关联容器的目的。配置结构如下:
创建和启动的顺序一般情况安照文本中的先后定义,也可以通过配置 depends_on实现。
“depends_on 不会等到 db 和 redis 容器 ready 再启动,web 容器仅仅等到 redis 和 db 容器启动就开始启动(官方文档中特意提到)”,如下:
#rmqconsole 控制台
rmqconsole:
image: apacherocketmq/rocketmq-console:2.0.0
container_name: rmqconsole
ports:
- 9001:8080
environment:
#Java启动参数中指定Name Server地址
JAVA_OPTS: -Drocketmq.namesrv.addr=rmqnamesrv-a:9876;rmqnamesrv-b:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
depends_on:
- rmqnamesrv-a
- rmqnamesrv-b
networks:
rmq:
aliases:
- rmqconsole
注:rmqconsole 中的 depends_on两个名字服务namesrv,只是说明容器启动先后顺序。只在容器联合启动时生效,启动完成后关闭namesrv并不会影响 rmqconsole的运行。但是在联合启动时,如果依赖的容器启动失败,那么这个容器应该也会失败(猜想的,有兴趣的可以尝试)。
#namesrv 名字服务集群
rmqnamesrv-a:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqnamesrv-a
ports:
- 9876:9876
volumes:
- /home/rocketmq/nameserver-a/logs:/home/rocketmq/logs
- /home/rocketmq/nameserver-a/store:/home/rocketmq/store
command: sh mqnamesrv
networks:
rmq:
aliases:
- rmqnamesrv-a
rmqnamesrv-b:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqnamesrv-b
ports:
- 9877:9876 #同一个虚拟机上namesrv对外端口要不一致
volumes:
- /home/rocketmq/nameserver-b/logs:/home/rocketmq/logs
- /home/rocketmq/nameserver-b/store:/home/rocketmq/store
command: sh mqnamesrv
networks:
rmq:
aliases:
- rmqnamesrv-b
注:因为在同一个虚拟机上,需要namesrv对外端口区分一下;宿主机目录和容器目录的映射关系配置,不同的镜像对应容器中的目录可能不同(我看过目录没有问题,但是没有任何日志或数据文件生成,容器里的目录也没有…);networks是由于因为创建容器ip的不确定性,,为容器的创建网络别名:
networks:
rmq:
aliases:
- rmqconsole
为docker-compose里的容器专门定义网路rmq(网络定义在最下边):
networks:
rmq:
name: rmq
driver: bridge
#broker 代理服务器双主模式
rmqbroker-a:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqbroker-a
ports:
- 10909:10909
#- 10911:10911
#- 10912:10912
volumes:
#宿主机目录映射容器目录(宿主机目录就是对应之前建立好的目录)
- /home/rocketmq/broker-a/logs:/home/rocketmq/logs
- /home/rocketmq/broker-a/store:/home/rocketmq/store
# 创建容器时会将宿主机的配置文件同步到容器
#如果发现容器并没有按照自己的conf文件运行,请检查是否容器内有两个配置文件(映射错了文件)
- /home/rocketmq/broker-a/conf/broker-a.conf:/root/rocketmq-4.5.0/conf/broker.conf
environment:
#JAVA_OPTS: "-Duser.home=/opt"
#出现exit 1的错误,提示内存溢出,所以这个应该是有用的
JAVA_OPT_EXT: "-server -Xms256m -Xmx256m -Xmn256m"
#autoCreateTopicEnable=true 我觉得这个配置放到broker.conf中更合理
#command: sh mqbroker -c ../conf/broker.conf autoCreateTopicEnable=true &
command: sh mqbroker -c ../conf/broker.conf
depends_on:
- rmqnamesrv-a
- rmqnamesrv-b
networks:
rmq:
aliases:
- rmqbroker-a
rmqbroker-b:
image: rocketmqinc/rocketmq-broker:4.5.0-alpine
container_name: rmqbroker-b
ports:
#- 11909:10909
- 10911:10911
#- 10912:10912
volumes:
- /home/rocketmq/broker-b/logs:/home/rocketmq/logs
- /home/rocketmq/broker-b/store:/home/rocketmq/store
- /home/rocketmq/broker-b/conf/broker-b.conf:/root/rocketmq-4.5.0/conf/broker.conf
environment:
JAVA_OPT_EXT: "-server -Xms256m -Xmx256m -Xmn256m"
command: sh mqbroker -c ../conf/broker.conf
networks:
rmq:
aliases:
- rmqbroker-b
即使是不同的容器,同一个虚拟机内两个broker容器的内部端口也不能相同。
注:可以看出rocketmq提供了10909、10911、10912三个端口,可以任选两个端口。
还有就是端口对外映射必须一致 10909:10909 而不能是11909:10909。我不知道这个是否和后面说到的broker.conf 中的端口配置有关。总之,我这样配是可以的。不行的话,可以多试试。
#rmqconsole 控制台
rmqconsole:
image: apacherocketmq/rocketmq-console:2.0.0
container_name: rmqconsole
ports:
- 9001:8080
environment:
#Java启动参数中指定Name Server地址
JAVA_OPTS: -Drocketmq.namesrv.addr=rmqnamesrv-a:9876;rmqnamesrv-b:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
depends_on:
- rmqnamesrv-a
- rmqnamesrv-b
networks:
rmq:
aliases:
- rmqconsole
注:关于“#Java启动参数中指定Name Server地址”,在首次启动时rmqconsole并不会去连接两个namesrv。
environment:
#Java启动参数中指定Name Server地址
JAVA_OPTS: -Drocketmq.namesrv.addr=rmqnamesrv-a:9876;rmqnamesrv-b:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
这里的地址指的是rmqconsole连接两个namesrv容器的地址,你可以注意到rmqnamesrv-b:9876 用的是内部端口9876而不是对外接口9877(容器之间连接的原则就是容器的ip和内部接口。这里容器IP在上面已经说了,用别名替代了)
broker-a.conf
brokerClusterName=DefaultCluster
brokerName=broker-a
#brokerId master用0 slave用其他
brokerId=0
deleteWhen=04
#文件保留时长 48小时
fileReservedTime=48
#broker角色 -ASYNC_MASTER 异步复制 -SYNC_MASTER同步双写 -SLAVE
brokerRole=ASYNC_MASTER
#刷盘策略 - ASYNC_FLUSH 异步刷盘 - SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_MASTER
# 修改为你宿主机的 IP
brokerIP1=192.168.137.188
#自动创建主题
autoCreateTopicEnable=true
listenPort=10909
namesrvAddr=192.168.137.188:9876;192.168.137.188:9877
broker-b.conf
brokerClusterName=DefaultCluster
brokerName=broker-b
#brokerId master用0 slave用其他
brokerId=0
deleteWhen=04
#文件保留时长 48小时
fileReservedTime=48
#broker角色 -ASYNC_MASTER异步复制 -SYNC_MASTER同步双写 -SLAVE
brokerRole=ASYNC_MASTER
#刷盘策略 - ASYNC_FLUSH 异步刷盘 - SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_MASTER
# 修改为你宿主机的 IP
brokerIP1=192.168.137.188
#自动创建主题
autoCreateTopicEnable=true
#和docker-compose中的端口对应起来
listenPort=10911
namesrvAddr=192.168.137.188:9876;192.168.137.188:9877
注:
listenPort中的端口与docker-compose.yml中的对应;
192.168.137.188是虚拟机地址,namesrvAddr地址写的是应用(生产者或消费者)访问namesrv时的地址,所以必须是192.168.137.188;
brokerIP1:
如果是本地程序调用云主机 mq,这个需要设置成 云主机 IP
这个ip配置为内网访问,让mq只能内网访问,不配置默认为内网
brokerIP1 = xxxxx
官方解释: 网卡的 InetAddress 当前 broker 监听的 IP
brokerIP1 = xxx.xxx.xxx.xxx等号后面是docker宿主的出网地址,比如docker安装在虚拟机上,那就是你连接虚拟机时的地址。
注:最后一个解释刚好合适我的环境,所以是 192.168.137.188 ;
双主模式,所以 brokerId=0;
切换到docker-compose.yml文件目录:
cd /home/rocketmq/
指定配置文件启动:
docker-compose -f docker-compose.yml up
#上面是为了能看到启动日志,一般用下面的
#docker-compose -f docker-compose.yml up -d
启动成功后访问rmqconsole:http://192.168.137.188:9001
看到这两个说明,配置成功了。
一个简单的springboot项目,引入依赖:
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-spring-boot-starterartifactId>
<version>2.1.0version>
dependency>
yml中配置:
#消息中间件
# 消息中间件配置
rocketmq:
name-server: 192.168.137.188:9876;192.168.137.188:9877
producer:
group: my-group
注:本地应用访问虚拟机集群namesrv容器
启动类,启动时产生消息:
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.messaging.support.MessageBuilder;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
public class AdminApplication implements CommandLineRunner {
@Resource
private RocketMQTemplate rocketMQTemplate;
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class,args);
}
@Override
public void run(String... args) throws Exception {
//rocketMQTemplate.convertAndSend("test-topic-1", "HelloWorld!");
rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("测试服务集群").build());
System.out.println("AAA");
}
}
启动启动类,在rmqconsole,输入主题,生产组,点搜索,可以看到生产者应用信息。
一个简单的springboot项目,引入依赖:
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-spring-boot-starterartifactId>
<version>2.1.0version>
dependency>
yml配置:
#消息中间件
rocketmq:
name-server: 192.168.137.188:9876;192.168.137.188:9877
启动类启动时消费消息:
public class ProviderApp{
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
@Slf4j
@Service
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1")
public static class MyConsumer1 implements RocketMQListener<String> {
public void onMessage(String message) {
log.info("received message: {}", message);
}
}
}
启动启动类,查看效果:
docker-compose ps
cd /home/rocketmq/
docker-compose start
docker-compose stop
//以配置文件创建容器并启动
docker-compose -f docker-compose.yml up -d
//启动删除容器和挂载设置的作用,和上面的命令刚好相反 停用移除所有容器以及网络相关
docker-compose down
docker-compose down --remove-orphans
//进入容器查看目录
docker exec -it rmqbroker-a /bin/bash
vi /root/rocketmq-4.5.0/conf/broker.conf
//查看容器详细信息(ip)
docker inspect rmqbroker-a
测试namesrv集群时,手工关掉一个namesrv,重新打开时报exit 1:
经过查看日志发现提示内存溢出,才知道“JAVA_OPT_EXT: “-server -Xms256m -Xmx256m -Xmn256m””这个应该是有用的。
rmqconsole中的 environment - JAVA_OPTS- 配置 在首次启动时不会根据配置去连接namesrv ,即使随便乱想也不会报错。但是当关掉一个namesrv后,再刷新rmqconsole页面时就会根据这个去连接namesrv。