• Java并发编程
  • JVM
  • JMX
  • Java数据结构与算法
  • 动态字节码生成技术
  • 常用工具
  • 1.4 集群搭建

    2019-12-22 12:50:26 9,720 2

    本文首先会对rocketmq集群四种部署模式进行介绍,包括:单主模式,多master模式,多master多slave模式异步复制,多master多slave模式同步复制,对比各种模式的优缺点。接着将每一种模式部署成一个集群,因此总共有4个集群,由一个NameServer集群进行管理。最后会介绍常见部署错误问题的解决方案。

    1 部署模式介绍

    一个完整的RocketMQ集群由NameServer集群Broker集群Producer集群Consumer集群组成。本节主要介绍NameServer集群,Broker集群的搭建。

    一个NameServer集群可以管理多个Broker集群,每个Broker集群由多组broker复制组构成,多个broker复制组通过指定相同的集群名称,来构成一个Broker集群。

    具体来说,每个broker复制组都满足以下几点:

  • 包含一个master节点,零个或者多个slave节点,且这些节点需要指定相同的broker名称;不同的broker复制组的broker名称必须不同。

  • master和slave通过brokerId参数进行区分。master的 brokerId参数必须是 0,slave 的 brokerId 必须是大于0的整数,如果有多个slave,这些slave的brokerId需要指定为不同的值。

  • master可读可写,slave只可以读,master通过主从复制的方式将数据同步给slave,支持同步复制和异步复制两种复制方式,目前master宕机后,slave不能自动切换为master

  • 基于Broker复制组的特性,一个Broker集群通常有多种部署方式:

    1. 单个 Master 

            集群中只有一个broker复制组,且只包含一个master节点。这种方式部署风险较大,一旦 Broker 重启或者宕机时,会导致整个服务不可用,通常是开发调试时使用,不建议线上环境使用 

    2. 多 Master 模式 

            集群中有多个broker复制组,且都只有master节点,没有slave节点。例如 2 个 master 或者 3 个 master节点。

            优点: 配置简单,单个 Master 宕机或重启维护对应用无影响,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢)。性能最高。 

            缺点: 单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到影响。 

    3 多 Master 多 Slave 模式,异步复制 

            集群中有多个broker复制组,且每个复制组都有master节点,也有slave节点。例如:每个 master 配置一个 slave。HA 采用异步复制方式,主备有短暂消息延迟,毫秒级。 

            优点: 即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为 Master 宕机后,消费者仍然可以 从 Slave 消费,此过程对应用透明。不需要人工干预。性能同多 Master 模式几乎一样。 

            缺点:Master 宕机,磁盘损坏情况,会丢失少量消息。

    4. 多 Master 多 Slave 模式,同步复制 

            与第三种方式类似,不同的是,HA 采用同步复制,生产者发送发送消息时,只有再主备都写成功,才向应用返回成功。 

            优点: 数据与服务都无单点,Master 宕机情况下,消息无延迟,服务可用性与数据可用性都非常高     

            缺点: 性能比异步复制模式略低,大约低 10%左右,发送单个消息的 RT 会略高。


    接下来,笔者将将演示在Linux操作系统中如何搭建一个单节点NameServer集群,以及上述四种Broker集群,并由这个单节点的NameServer集群来管理这四个Broker集群。

    注意:在实际生产环境中,NameServer以及每个Broker节点(不管是master还是slave),都是部署在不同的机器上的。这里简单起见,将通过伪分布式的方式进行搭建,即所有节点都运行在一台机器上。如果读者希望搭建完整的分布式集群,可以使用vmvare/virtualbox等工具,只需要将本文的配置拷贝即可。

    2 前提条件

    wRocketMQ NameServer和Broker是基于Java 开发的,需要安装JDK,且需要保证二者版本的匹配。下图列出安装/运行RocketMQ需要的JDK版本。

    VersionClientBrokerNameServer
    4.0.0-incubating
    >=1.7
    >=1.8
    >=1.8
    4.1.0-incubating
    >=1.6
    >=1.8
    >=1.8
    4.2.0
    >=1.6
    >=1.8
    >=1.8
    4.3.x
    >=1.6
    >=1.8
    >=1.8
    4.4.x
    >=1.6
    >=1.8
    >=1.8
    4.5.x
    >=1.6
    >=1.8
    >=1.8
    4.6.x
    >=1.6
    >=1.8
    >=1.8

    本文以RocketMQ 4.6.0版本为例进行讲解,对应JDK版本为1.8。本文不讲解JDK如何安装,读者可自行查阅相关资料。确保JDK的版本>=1.8,可以通过如下方式验证:

    $ java -version
    java version "1.8.0_131"
    Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
    Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode

    3 下载安装

    下载

    该地址列出了RocketMQ所有发布的版本:https://github.com/apache/rocketmq/releases

    这里将RocketMQ安装到Linux文件系统的/opt目录,首先进入/opt目录

    cd /opt

    可以直接从github下载,但是网速较慢

    $ wget https://github.com/apache/rocketmq/archive/rocketmq-all-4.6.0.zip

    网速慢的同学也可以从国内镜像下载:

    $ wget https://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.6.0/rocketmq-all-4.6.0-bin-release.zip

    下载之后进行解压缩:

    $ unzip rocketmq-all-4.6.0-bin-release.zip

    解压目录说明

    rocketmq-all-4.6.0-bin-release
    ├── benchmark #性能测试脚本
    ├── bin       #命令行工具
    ├── conf      #配置文件目录
    ├── lib       #依赖的第三方类库
    ├── LICENSE
    ├── NOTICE
    └── README.md

    设置ROKCKET_HOME环境变量:

    在这里,将我们将RocketMQ安装包的解压目录设置为ROCKETMQ_HOME环境变量。例如笔者的解压目录为:

    $ pwd
    /opt/rocketmq-all-4.6.0-bin-release

    为了以后升级方便,我们创建一个软连接:

    sudo ln -s /opt/rocketmq-all-4.6.0-bin-release rocketmq

    修改/etc/profile,添加以下一行:

    export ROCKETMQ_HOME=/opt/rocketmq

    执行以下命令,使得环境变量生效

    source /etc/profile

    验证环境变量生效:

    $ echo $ROCKETMQ_HOME
    /opt/rocketmq

    3.1 启动NameServer

    启动

    $ nohup sh bin/mqnamesrv &

    验证启动成功

    $ jps -l
    3961157 sun.tools.jps.Jps
    3953057 org.apache.rocketmq.namesrv.NamesrvStartup #NameServer进程

    NameServer默认监听9876端口,也可以通过如下方式验证:

    $ lsof -iTCP -nP | grep 9876
    java    3953057 tianshouzhi.robin   65u  IPv6 134849198      0t0  TCP *:9876 (LISTEN)

    设置NAMESRV_ADDR环境变量,修改etc/profile,添加以下内容:

    export NAMESRV_ADDR=localhost:9876

    并执行"source /etc/profile"使得其生效

    3.2 启动Broker

            ${ROCKETMQ_HOME}/conf目录下,提供了我们讲解到的RocketMQ四种部署模式的demo配置文件,如下所示:

    conf
    ├── 2m-2s-async                   //多Master多Slave模式,异步复制
    │   ├── broker-a.properties
    │   ├── broker-a-s.properties
    │   ├── broker-b.properties
    │   └── broker-b-s.properties
    ├── 2m-2s-sync                    //多Master多Slave 模式,同步复制
    │   ├── broker-a.properties
    │   ├── broker-a-s.properties
    │   ├── broker-b.properties
    │   └── broker-b-s.properties
    ├── 2m-noslave                    //多Master模式
    │   ├── broker-a.properties
    │   ├── broker-b.properties
    │   └── broker-trace.properties
    └── broker.conf                   //单Master模式

    在实际生产环境中,你可以选择其中一种模式进行部署。从学习的角度,笔者将详细讲解每一种模式,每种模式部署为一个集群,因此总共会部署4个集群。

          另外,生产环境中至少需要部署为双主模式,每个机器只会部署一个broker,因此只使用broker.conf配置文件即可,根据要配置的节点的类型,将其他模式下的配置复制到broker.conf,或者直接修改broker.conf

    3.2.1 单Master模式

    修改配置文件:

    单master模式可以使用conf目录下的broker.conf 配置文件,内容如下所示:

    #集群名称
    brokerClusterName=single-master
    #broker复制组名称
    brokerName=broker-a
    #nameserver地址
    namesrvAddr=127.0.0.1:9876
    #brokerId,因为是master节点,所以这里设置为0
    brokerId=0
    #监听端口
    listenPort=10911
    #rocketmq定时清除
    deleteWhen=04
    #文件保留时间,默认48小时
    fileReservedTime=48
    #broker角色,异步复制
    brokerRole=ASYNC_MASTER
    #异步刷盘
    flushDiskType=ASYNC_FLUSH
    #存储目录
    storePathRootDir=/data/rocketmq/single-master/broker-a/store
    storePathCommitLog=/data/rocketmq/single-master/broker-a/store/commitlog

         注意:如果配置项名称或者值写错,broker启动时并不会报错,会使用默认值替代,常见错误:如在=号两边加了空格,这里是不需要的。

         启动通过bin目录下的mqbroker脚本。由于默认的配置,启动后会立即占用8G内存,如果机器内存不够,可以修改bin/runbroker.sh,找到以下这一行:

    JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g"

    将其修改为:

    JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g"


    启动:

    $ nohup sh bin/mqbroker -c conf/broker.conf &

    注意:broker启动时不会读取broker.conf中的配置,尽管也可以启动,但是如果需要使得配置文件生效,必须通过-c参数进行指定。

    验证启动成功:

    $ jps -l
    3961157 sun.tools.jps.Jps
    3960977 org.apache.rocketmq.broker.BrokerStartup
    3953057 org.apache.rocketmq.namesrv.NamesrvStartup

    NameServer默认监听在10911端口,也可以通过以下方式验证:

    $ lsof -iTCP -nP | grep 10911
    java    37686 tianshouzhi.robin  107u  IPv6 137040246      0t0  TCP *:10911 (LISTEN)


    如果启动失败,可以通过以下命令查看错误的具体信息:

    tail -200f ~/logs/rocketmqlogs/broker.log


    测试发送/消费消息

    安装包bin目录下提供了一个tools.sh工具,我们可以通过其来测试发送/接收消息。

    测试发送消息:

    执行以下命令将会往一个名为TopicTest主题中发送1000条消息

    $ sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer 
    SendResult [sendStatus=SEND_OK, msgId=FDBDDC0300FF00010001022700120225003C3D4EAC696720298203E7, 
    offsetMsgId=AC11000100002A9F0000000000037567, 
    messageQueue=MessageQueue [topic=TopicTest, brokerName=broker-a, queueId=3], 
    wqueueOffset=249]
    ...

    测试消费消息:

    执行以下命令,将会之前的消费1000条消息

    $ sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
    ConsumeMessageThread_%d Receive New Messages: [MessageExt… 
    ...

    这里我们是通过命令行工具来发送/消费消息,在后文中,我们将介绍如何通过API的方式来完成相同的功能。


    查看集群列表信息:

    $ sh bin/mqadmin clusterList -n localhost:9876
    #Cluster Name    #Broker Name   #BID        #Addr           #Version   #...(略)
    single-master    broker-a      0        192.168.1.3:10911     V4_6_0    …

    输出的每一列说明如下:

  • Cluster Name:集群的名称,即brokerClusterName配置项的值

  • Broker Name:Broker的名称,即brokerName配置项的值

  • BID:Broker的ID,这里显示为0,即brokerId配置项的值

  • Addr:监听的IP/端口,供生产者/消费者访问,端口即listenPort配置项的值

  • Version:broker的版本

  • 3.2.2 多Master模式

    这里演示的多master模式是双主模式:包含2个master节点,没有slave节点。如前所属,这里是伪分布式,在一台机器上启动两个master节点。我们需要对conf/2m-noslave目录下的2个配置文件进行一些修改,否则会与前面搭建的单master模式存在一些冲突,如监听的端口和保存数据的路径等。

    修改后的结果如下所示:

    conf/2m-noslave/broker-a.properties

    brokerClusterName=2m-noslave
    listenPort=11911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-broker-a
    brokerId=0
    deleteWhen=04
    fileReservedTime=48
    brokerRole=ASYNC_MASTER
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-noslave/broker-a/store/
    storePathCommitLog=/data/rocketmq/2m-noslave/broker-a/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-noslave/broker-a/store/consumequeue/

    conf/2m-noslave/broker-b.properties

    brokerClusterName=2m-noslave
    listenPort=12911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-broker-b
    brokerId=0
    deleteWhen=04
    fileReservedTime=48
    brokerRole=ASYNC_MASTER
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-noslave/broker-b/store/
    storePathCommitLog=/data/rocketmq/2m-noslave/broker-b/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-noslave/broker-b/store/consumequeue/

    在这里,我们将两个配置文件中的brokerClusterName都改成了2m-noslave,表名这两个broker节点将组成一个新的集群。也别修改了listenPort配置项以监听不同的端口,此外,我们修改了三个storePath前缀的配置项,将数据存储到不同的目录中。

    特别需要注意的是:一些同学可能认为brokerClusterName已经不同了,没有必要修改brokerName配置项,这是一种误解。在RocketMQ中,一个NameServer集群可以多个Broker集群,但是broker集群的名称并没有起到命名空间的作用,因此管理的所有Broker集群下的broker复制组的名称都不能相同。

    启动broker-a

    nohup sh bin/mqbroker -c conf/2m-noslave/broker-a.properties &

    启动broker-b

    nohup sh bin/mqbroker -c conf/2m-noslave/broker-b.properties &

    在启动之后,当我们在查看集群列表信息时,如下:

    $ sh bin/mqadmin clusterList -n localhost:9876
    #Cluster Name     #Broker Name     #BID  #Addr               #Version
    single-master     broker-a          0     192.168.1.3:10911   V4_6_0  
    2m-noslave        2m-broker-a       0     192.168.1.3:11911   V4_6_0  
    2m-noslave        2m-broker-b       0     192.168.1.3:12911   V4_6_0

    这里显示了2个broker集群:single-master和2m-noslave,其中后者存在两个节点。

    3.2.3 多 Master 多 Slave 模式,异步复制 

    该模式需要使用conf/2m-2s-async目录下的四个配置文件。同样我们需要修改brokerClusterName,listenPort,brokerName以及存储路径。特别需要注意的是对于slave,其brokerRole配置项需要为SLAVE,brokerId是需要时一个大于0的值。

    修改后的结果如下所示:

    conf/2m-2s-async/broker-a.properties

    brokerClusterName=2m-2s-async
    listenPort=13911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-2s-async-broker-a
    brokerId=0
    deleteWhen=04
    fileReservedTime=48
    brokerRole=ASYNC_MASTER
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-2s-async/broker-a-0/store/
    storePathCommitLog=/data/rocketmq/2m-2s-async/broker-a-0/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-2s-async/broker-a-0/store/consumequeue/

    conf/2m-2s-async/broker-a-s.properties

    brokerClusterName=2m-2s-async
    listenPort=14911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-2s-async-broker-a
    brokerId=1
    deleteWhen=04
    fileReservedTime=48
    brokerRole=SLAVE
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-2s-async/broker-a-1/store/
    storePathCommitLog=/data/rocketmq/2m-2s-async/broker-a-1/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-2s-async/broker-a-1/store/consumequeue/

    conf/2m-2s-async/broker-b.properties

    brokerClusterName=2m-2s-async
    listenPort=15911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-2s-async-broker-b
    brokerId=0
    deleteWhen=04
    fileReservedTime=48
    brokerRole=ASYNC_MASTER
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-2s-async/broker-b-0/store/
    storePathCommitLog=/data/rocketmq/2m-2s-async/broker-b-0/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-2s-async/broker-b-0/store/consumequeue/

    conf/2m-2s-async/broker-b-s.properties

    brokerClusterName=2m-2s-async
    listenPort=16911
    namesrvAddr=127.0.0.1:9876
    brokerName=2m-2s-async-broker-b
    brokerId=1
    deleteWhen=04
    fileReservedTime=48
    brokerRole=SLAVE
    flushDiskType=ASYNC_FLUSH
    storePathRootDir=/data/rocketmq/2m-2s-async/broker-b-1/store/
    storePathCommitLog=/data/rocketmq/2m-2s-async/broker-b-1/store/commitlog/
    storePathConsumerQueue=/data/rocketmq/2m-2s-async/broker-b-1/store/consumequeue/

    依次启动:

    nohup sh bin/mqbroker -c conf/2m-2s-async/broker-a.properties &
    nohup sh bin/mqbroker -c conf/2m-2s-async/broker-a-s.properties &
    nohup sh bin/mqbroker -c conf/2m-2s-async/broker-b.properties &
    nohup sh bin/mqbroker -c conf/2m-2s-async/broker-b-s.properties &

    查看集群信息:

    $ sh bin/mqadmin clusterList -n localhost:9876
    #Cluster Name     #Broker Name            #BID  #Addr                  #Version
    single-master     broker-a                0     172.17.0.1:10911       V4_6_0   
    2m-2s-async       2m-2s-async-broker-a    0     172.17.0.1:13911       V4_6_0   
    2m-2s-async       2m-2s-async-broker-a    1     172.17.0.1:14911       V4_6_0   
    2m-2s-async       2m-2s-async-broker-b    0     172.17.0.1:15911       V4_6_0   
    2m-2s-async       2m-2s-async-broker-b    1     172.17.0.1:16911       V4_6_0   
    2m-noslave        2m-broker-a             0     172.17.0.1:11911       V4_6_0   
    2m-noslave        2m-broker-b             0     172.17.0.1:12911       V4_6_0

    这里多出了2m-2s-async集群的四个broker节点信息。

    3.2.4 多 Master 多 Slave 模式,同步复制 

            该模式需要使用conf/2m-2s-sync目录下的四个配置文件,与异步复制最大的不同是,需要将master节点的brokerRole配置项需要改为SYNC_MASTER。这里不再赘述。如果是在同一台机器上搭建此模式,记得修对应的参数。

    3.3 停止

    bin目录安装包下有一个mqshutdown脚本,其既可以关闭Broker,也可以关闭NameServer。注意该脚本会将本机上启动的所有Broker或所有NameServer关闭。

    停止broker

    $ sh bin/mqshutdown broker
    The mqbroker(67521
    74023
    74153
    362837
    362958
    363070) is running...
    Send shutdown request to mqbroker(67521
    74023
    74153
    362837
    362958
    363070)

    停止nameserver

    $ sh bin/mqshutdown namesrv
    The mqnamesrv(3953057) is running...
    Send shutdown request to mqnamesrv(3953057) OK


    4  常见安装错误

    错误1:端口已被占用

    java.net.BindException: Address already in use
            at sun.nio.ch.Net.bind0(Native Method)
            at sun.nio.ch.Net.bind(Net.java:433)
            at sun.nio.ch.Net.bind(Net.java:425)
            at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
            at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
            at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
            at

    原因:重复监听了同一个端口,通常是对同一个配置文件启动了多次,或者配置listenPort端口未生效。

    错误2:MQ已启动

    java.lang.RuntimeException: Lock failed,MQ already started
            at org.apache.rocketmq.store.DefaultMessageStore.start(DefaultMessageStore.java:222)
            at org.apache.rocketmq.broker.BrokerController.start(BrokerController.java:853)
            at org.apache.rocketmq.broker.BrokerStartup.start(BrokerStartup.java:64)
            at org.apache.rocketmq.broker.BrokerStartup.main(BrokerStartup.java:58)

    原因:多个配置文件中,可能指定了相同的存储路径,检查配置是否正确。

    错误3:配置文件不存在

    java.io.FileNotFoundException: conf/2m-2s-async/broker-a-m.properties (No such file or directory)
            at java.io.FileInputStream.open0(Native Method)
            at java.io.FileInputStream.open(FileInputStream.java:195)
            at java.io.FileInputStream.<init>(FileInputStream.java:138)
            at java.io.FileInputStream.<init>(FileInputStream.java:93)
            at org.apache.rocketmq.broker.BrokerStartup.createBrokerController(BrokerStartup.java:128)
            at org.apache.rocketmq.broker.BrokerStartup.main(BrokerStartup.java:58)

    配置文件不存在,检查对应目录下是否有此文件

    错误4:内存分配失败

    未按照前文所属修改bin/runserver.sh,bin/runbroker.sh脚本,导致启动每一个节点时占用内存过多。如果本身机器内存就不足,可以不必同时运行这么多模式。