项目据说需要使用消息中间件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
注:控制台单独一个镜像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.ymlnameserver-a 日志和数据存储目录
/home/rocketmq/nameserver-a/logs /home/rocketmq/nameserver-a/storenameserver-b 日志和数据存储目录
/home/rocketmq/nameserver-b/logs /home/rocketmq/nameserver-b/storebroker-a 日志、数据存储目录以及配置文件(配置文件先空文本)
/home/rocketmq/broker-a/logs /home/rocketmq/broker-a/store /home/rocketmq/broker-a/conf/broker.confbroker-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 restartdocker-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对外端口区分一下;宿主机目录和容器目录的映射关系配置,不同的镜像对应容器中的目录可能不同(我看过目录没有问题,但是没有任何日志或数据文件生成,容器里的目录也没有…);networks是由于因为创建容器ip的不确定性,,为容器的创建网络别名:
networks: rmq: aliases: - rmqconsole为docker-compose里的容器专门定义网路rmq(网络定义在最下边):
networks: rmq: name: rmq driver: bridge即使是不同的容器,同一个虚拟机内两个broker容器的内部端口也不能相同。 注:可以看出rocketmq提供了10909、10911、10912三个端口,可以任选两个端口。 还有就是端口对外映射必须一致 10909:10909 而不能是11909:10909。我不知道这个是否和后面说到的broker.conf 中的端口配置有关。总之,我这样配是可以的。不行的话,可以多试试。
注:关于“#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:9877broker-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); } } }启动启动类,查看效果:
测试namesrv集群时,手工关掉一个namesrv,重新打开时报exit 1: 经过查看日志发现提示内存溢出,才知道“JAVA_OPT_EXT: “-server -Xms256m -Xmx256m -Xmn256m””这个应该是有用的。
rmqconsole中的 environment - JAVA_OPTS- 配置 在首次启动时不会根据配置去连接namesrv ,即使随便乱想也不会报错。但是当关掉一个namesrv后,再刷新rmqconsole页面时就会根据这个去连接namesrv。