rocketmq客户端数监控_docker

如果想搭建一个本地玩的 rocketmq,可以继续向下看,绝对让你体会“傻瓜式”点点点安装


能够来看 rocketmq 的安装,证明已经了解基本概念,也就不在这班门弄斧了

rocketmq 有两个非常重要的概念,nameserver 和 broker,文章也是围绕这两个点进行安装,提供 便捷版本 & 可靠版本 两种方式

文章大纲如下:

  • 安装 nameserver
  • 便捷版安装 nameserver
  • 可靠版安装 nameserver
  • 安装 broker server
  • 便捷版安装 broker server
  • 可靠版安装 broker server
  • 安装 rocketmq 控制台
  • 创建 topic
  • 控制台创建
  • 命令行创建
  • rocketmq 项目实战
  • 结言

安装 nameserver

首先,通过 docker search 命令查看,准备拉取 rocketmq 的镜像

因为没有官方制作的镜像,所以我们选择一款名称为 foxiswho/rocketmq 的镜像,大家也可以使用 docker search rocketmq 自己搜索看一下

便捷版和可靠版的区别就是:安全版把重要文件通过映射的方式存放宿主机

便捷版安装 nameserver

使用 docker 的好处就是 “一行命令走天下,如果不是那就来两行”

便捷版就是纯属本地安装,容器被铲掉就铲掉了,数据不稀的存储,就像某银行的经典字母。I(爱)C(存)B(不)C(存)

docker run -d -p 9876:9876 \
--name rmqnamesrv \
foxiswho/rocketmq:server-4.5.1

简单明了,运行命令 docker ps 查看是否有名称为 rmqnamesrv 的 docker 程序

如果是自己搭个 rocketmq 玩一玩,便捷版就可以满足需求

如果选择便捷版模式,直接跳过下一节可靠版安装就行

可靠版安装 nameserver

为了避免,我铲掉个容器,数据都丢失的情况。特此推出可靠版本,把数据存储存放到宿主机

先创建个目录,为了规范以后的 docker 文章,所以我们固定化目录方式

${HOME} 表示当前用户主目录,小伙伴不喜欢可以自行替换

mkdir -p ${HOME}/docker/software/rocketmq/data/namesrv/store
mkdir -p ${HOME}/docker/software/rocketmq/data/namesrv/logs

目录文件创建完成之后,执行下列包含映射关系的 docker 命令语句即可

docker run -d -p 9876:9876 \
-v ${HOME}/docker/software/rocketmq/data/namesrv/logs:/opt/logs \
-v ${HOME}/docker/software/rocketmq/data/namesrv/store:/opt/store \
--name rmqnamesrv \
-e "MAX_POSSIBLE_HEAP=100000000" \
foxiswho/rocketmq:server-4.5.1

nameserver 到这里就安装结束了,接下来继续 broker 服务端安装

安装 broker server

老规矩,broker 同样便捷版本 & 可靠版本安排,上才艺~

因为 broker 也是从 rocketmq 的镜像包中启动,所以这里不会再拉取新镜像

便捷版安装 broker

安装 broker 之前,我们需要在本地初始化配置文件,防止项目连接不到 rocketmq

1)新建配置目录

mkdir -p ${HOME}/docker/software/rocketmq/config

2)新建配置文件 broker.conf

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# 此处为本地ip, 如果部署服务器, 需要填写服务器外网ip
brokerIP1 = xx.xx.xx.xx

最后执行创建 broker 容器命令,命令虽然比较长,但是职责比较单一

  1. 宿主机 10911、10909 和容器做一个映射
  2. 给 broker 起一个新的名字 rmqbroker
  3. 关联 rmqnamesrv 并起一个别名 namesrv 用作  namesrv 地址 ip
  4. 修改启动内存为 512 m
docker run -d \
-p 10911:10911 \
-p 10909:10909 \
--name rmqbroker \
--link rmqnamesrv:namesrv \
-v ${HOME}/docker/software/rocketmq/conf/broker.conf:/etc/rocketmq/broker.conf \
-e "NAMESRV_ADDR=namesrv:9876" \
-e "JAVA_OPTS=-Duser.home=/opt" \
-e "JAVA_OPT_EXT=-server -Xms512m -Xmx512m" \
foxiswho/rocketmq:broker-4.5.1

可靠版安装 broker

还是先把目录文件创建出来

mkdir -p ${HOME}/docker/software/rocketmq/data/broker/logs
mkdir -p ${HOME}/docker/software/rocketmq/data/broker/store

把 broker 相关数据目录存放宿主机最大的好处:后续铲掉了 broker 容器,再次新建 topic 等数据未丢失

docker run -d \
-p 10911:10911 \
-p 10909:10909 \
--name rmqbroker \
--link rmqnamesrv:namesrv \
-v ${HOME}/docker/software/rocketmq/conf/broker.conf:/etc/rocketmq/broker.conf \
-v ${HOME}/docker/software/rocketmq/data/broker/logs:/opt/logs \
-v ${HOME}/docker/software/rocketmq/data/broker/store:/opt/store \
-e "NAMESRV_ADDR=namesrv:9876" \
-e "JAVA_OPTS=-Duser.home=/opt" \
-e "JAVA_OPT_EXT=-server -Xms512m -Xmx512m" \
foxiswho/rocketmq:broker-4.5.1

安装 rocketmq 控制台

如果没有可视化控制台是怎么样的体验

  • 创建 topic 需要用又长又臭的命令,关键还记不住
  • 无法很好的测试 topic 提供者、消费者发送接收消息
  • 对于集群状态以及生产、消费数额统计无法得知等功能...

既然没有控制台有如上缺点,安装了后就是优点。首先,拉取控制台镜像包到本地

docker pull pangliang/rocketmq-console-ng

通过 docker 命令启动,直接执行即可,作者在安装控制台时因为一直错误差点嗝屁了

后面对于本地安装总结出,直接关联到 rmqnamesrv 容器上的 ip 就行

docker run -d \
--link rmqnamesrv:namesrv \
-e "JAVA_OPTS=-Drocketmq.config.namesrvAddr=namesrv:9876 -Drocketmq.config.isVIPChannel=false" \
--name rmqconsole \
-p 8088:8080 \
-t pangliang/rocketmq-console-ng

运行成功,稍等几秒启动时间,浏览器输入 localhost:8088 查看



rocketmq客户端数监控_apache_02

顶栏是控制台支持的功能清单,进来默认是英文的,像作者这种小学一年级的英文水平,必须右上角 Chinese 安排

创建 topic

作者在创建 topic 时也是吃尽了“苦头”,可以这么说,目前为止,按照网上教程,没有一个能把文章流程跑下来的

控制台创建

推荐采用控制台方式创建 topic,便捷的同时错误率也低

1)点击控制台 新增/更新 按钮创建 topic



rocketmq客户端数监控_rocketmq客户端数监控_03

2)根据规则创建自己想要的 topic



rocketmq客户端数监控_docker_04

集群名和 BROKER_NAME 选择默认即可,主题名你也随意,不要起中文名字 就行(别问我怎么知道的 ☹️)

命令行创建

rocketmq topic 可以在控制台创建,但是为了声明一个本镜像安装的一个 bug,还是选择通过原生命令测试

首先我们需要通过命令进入到容器内部,执行下述命令会进入容器的 /opt/rocketmq-4.4.0/bin 目录

docker exec -it rmqbroker bash

直接执行命令没商量,当然,报错也没商量

sh mqadmin updateTopic -b localhost:10911 -n localhost:9876 -t TopicTest-1

通过报错大概也能看出来一点,是某个算法签名照不到,这是为什么呢?



rocketmq客户端数监控_spring_05

后来经过网上案例分析,镜像本身携带的是 OpenJDK,并不是 Oracle 的,问题就出在了这,如何解决呢?

目录下执行 vi tools.sh 命令,修改一行命令指向 OpenJDK 的绝对路径就可以了



rocketmq客户端数监控_apache_06

将上图中的命令行修改为下述 JDK 的绝对路径即可

JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib:${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/lib/ext"

再次执行上述创建 topic 命令,出现创建成功标志就OK

create topic to localhost:10911 success.

rocketmq 项目实战

只有想到的方面多时,才能在够实际运用中发现更多的问题,这也是作者给读者最好的礼物

新建一个 springboot 项目,pom 文件如下,rocketmq 使用 apache 封装的 jar

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
   <dependency>
        <groupId>org.apache.rocketmqgroupId>
        <artifactId>rocketmq-clientartifactId>
        <version>4.4.0version>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintagegroupId>
                <artifactId>junit-vintage-engineartifactId>
            exclusion>
        exclusions>
    dependency>
dependencies>

进行测试消息发送消费者只需要四步

1)创建配置管理类

public class RmqConfig {
    /**
     * Name Server 地址,因为是集群部署 所以有多个用 分号 隔开
     */
    public static final String NAME_SERVER = "localhost:9876";
    /**
     * 主题名称 主题一般是服务器设置好 而不能在代码里去新建topic( 如果没有创建好,生产者往该主题发送消息 会报找不到topic错误)
     */
    public static final String TOPIC = "TestTopic";
}

2)新建消息生产端 Producer,负责生产消息

import lombok.Getter;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.springframework.stereotype.Component;

@Getter
@Component
public class Producer {
    /**
     * 生产者组
     */
    private static final String producerGroup = "test_producer";
    private DefaultMQProducer producer;

    public Producer() throws MQClientException {
        producerInit().start();
    }

    private DefaultMQProducer producerInit() {
        producer = new DefaultMQProducer(producerGroup);
        // 不开启 vip 通道 开通口端口会减 2
        producer.setVipChannelEnabled(false);
        // 绑定 name server
        producer.setNamesrvAddr(RmqConfig.NAME_SERVER);
        return producer;
    }
}

3)建立消息消费端 Consumer,负责消费消息

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;

@Slf4j
@Component
public class Consumer {
    /**
     * 消费者组
     */
    private static final String CONSUMER_GROUP = "test_consumer";

    public Consumer() throws MQClientException {
        consumerInit().start();
    }

    private DefaultMQPushConsumer consumerInit() throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_GROUP);
        consumer.setNamesrvAddr(RmqConfig.NAME_SERVER);
        // 消费模式:一个新的订阅组第一次启动从队列的最后位置开始消费 后续再启动接着上次消费的进度开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
        // 订阅主题和 标签( * 代表所有标签)下信息
        consumer.subscribe(RmqConfig.TOPIC, "*");
        // 注册消费的监听 并在此监听中消费信息, 并返回消费的状态信息
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            // msgs 中只收集同一个topic, 同一个 tag, 并且 key 相同的message
            // 会把不同的消息分别放置到不同的队列中
            try {
                for (Message msg : msgs) {
                    // 消费者获取消息 这里只输出 不做后面逻辑处理
                    String body = new String(msg.getBody(), "utf-8");
                    log.info("  >>> [消费端] 获取消息主题 topic :: {}, 消费消息 body :: {} ", msg.getTopic(), body);
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        return consumer;
    }
}

4)创建对应 Controller 测试类

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class Controller {

    @Autowired
    private Producer producer;

    @RequestMapping("/rocketmq/send")
    public Object callback() throws Exception {
        for (int i = 0; i 100; i++) {
            // 创建生产信息
            Message message = new Message(RmqConfig.TOPIC, "testTag", (String.format("[生产者] 发送第 【%d】 次 mq 消息", i)).getBytes());
            // 发送消息
            SendResult sendResult = producer.getProducer().send(message);
            log.info("  >>> [生产者] 发送消息返回对象 :: {}", sendResult);
        }

        return "执行成功";
    }
}

根据文章这么一套走下来,可以说是玩过 rocketmq 了,后面就需要更深入去了解 rocketmq 特性以及具体使用了

结言

文章主要针对 rocketmq 的安装以及使用做出了详细的介绍,文章的每一步都是作者试验过很多遍的成果,如果发现哪一步行不通可以找我