docker-compose搭建rocketmq双主模式

背景

项目据说需要使用消息中间件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,也可以模拟集群中宕机其中一台的情况。本机网络和虚拟机网络为同一个网段,虚拟机内容器之间为同一个网段。需要注意网络连接,这个会具体说明。

开始rockermq环境构建

docker 安装(略)

docker-compose安装

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)- (unames)(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

docker-compose.yml配置

整体配置文件

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的集群

#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的双主模式

#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 控制台  
  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.conf配置文件

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项目,引入依赖:

<!--rocketmq 消息中间件-->
<dependency>
	<groupId>org.apache.rocketmq</groupId>
	<artifactId>rocketmq-spring-boot-starter</artifactId>
	<version>2.1.0</version>
</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项目,引入依赖:

<!--rocketmq 消息中间件-->
<dependency>
	<groupId>org.apache.rocketmq</groupId>
	<artifactId>rocketmq-spring-boot-starter</artifactId>
	<version>2.1.0</version>
</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。

猜你喜欢

转载自blog.csdn.net/mofsfely2/article/details/109181205