一、消息队列概述

1.1、消息队列由来

在运维场景中,我们经常会存在如下场景:一旦出现S1异常,C1将因为S1的影响而异常(C为客户端,s为服务端)

当然可以通过添加多个S的方式,实现高可用。但这样会变得难以维护,每增加一个S所有的C都要进行调整,增加了耦合性(软件开发强调高内聚低耦合)

可以通过在C和S之间添加负载均衡来达到降低耦合度的效果,但是在流量高峰期C1收到的压力,仍然会传递到后端S。如果S处理处理不过来可能就会导致异常(一般情况下前端的接收请求的速度是较快的,但是后端处理请求的速录会较慢)。因此消息队列就出来了(太阳底下没有新鲜事儿,难搞的我们就搞个中间层来屏蔽底层的复杂或不足),于是就变成了这样:

1.2、常用消息队列

常见的消息队列:

rabbitMQ地址: https://github.com/rabbitmq

Kafka地址: https://github.com/apache/kafka

RocketMQ地址:https://github.com/apache/rocketmq/

这里只针对rocketmq做说明,其他消息队列类似可自行 google了解

1.3、消息中间件原理

生产者生产消息,消费者消费消息(需要先订阅消息)。消息队列负责异步解耦。

二、RocketMQ介绍

rocketmq的前身是metaq,当前已在apache安家。

2.1、组件

  • Producer:消息的生产者
  • Consumer:消息消费者
  • Broker:暂存和处理消息
  • NameServer:管理Broker,以及topic等信息
  • Topic:区分消息的种类,逻辑分类;
  • Tag:消息的标签,在topic的基础上进一步分类
  • Message Queue:相当于是Topic的分区;用于并行发送和接收消息
namesrv:无状态服务,可集群部署,节点之间无数据同步
broker:分为master和slave,根据brokerid参数进行区分,0为master,大于或者等于0为slave。broker和namesrv建立长连接,定时注册topic和状态信息
producer:和namesrv建立长连接,定期从namesrv获取topic路由信息,并与master建立链接
consumer:和namesrv建立长连接,定期从namesrv获取topic路由信息,并与提供topci的master和slave建立长链接,并定时向master和slave发送心跳。consumer可以从slave或者master订阅消息

2.2、知识图谱

三、安装

3.1、环境要求

64bit oS;64bit JDK 1.8+;memory 4G+;

下载地址:https://rocketmq.apache.org/dowloading/releases/

[root@master rocketmq]# ls -l #1、准备的安装包
-rw-r--r-- 1 root root 12536572 4月 4 2019 rocketmq-all-4.5.0-bin-release.zip
-rw-r--r-- 1 root root 3178509 4月 4 2019 rocketmq-all-4.5.0-source-release.zip
[root@master rocketmq]# unzip rocketmq-all-4.5.0-bin-release.zip #2、解压
root@master rocketmq]# mv rocketmq-all-4.5.0-bin-release rocketmq #3、重命名
[root@master rocketmq]# cd rocketmq
[root@master rocketmq]# ls
benchmark bin conf lib LICENSE NOTICE README.md
[root@master rocketmq]# mkdir logs store/{commitlog,consumequeue,index} -p #默认会在root目录下创建logs目录,在tmp目录下创建,commitlog存储消息,consumequeue、index存储索引数据

config配置文件说明:

2m-2s-async: 2主2从异步

2m-2s-sync: 2主2从同步

2m-noslave: 2主

3.2、单机版安装

3.2.1、配置文件准备

[root@master conf]# cat single-broker.conf
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# 集群名称
brokerClusterName=My-Cluster
#broker名称
brokerName=broker_01
#broekr的id,0表示master,>0为slave节点
brokerId=0
#namesrv地址,分号分隔
namesrvAddr=www.mt.com:9876
#在发送消息的时候,自动创建服务器不存在的Topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许Broker自动创建Topic
autoCreateTopicEnable=true
#是否允许broker自动创建订阅组
autoCreateSubscriptionGroup=true
#broker监听端口号
listenPort=12121
#删除文件的时间点,默认凌晨4点
deleteWhen=04
#文件保留时间,默认48h
fileReservedTime=48
#commitlog文件默认大小,默认1G
mapedFileSizeCommitlog=1073741824
#ConsumeQueue每个文件默认存储多少条
mapedFileSizeConsumeQueue=300000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/opt/rocketmq/rocketmq/store
#commitlog存储路径
storePathCommitLog=/opt/rocketmq/rocketmq/store/commitlog
#消费队列存储路径
storePathConsumeQueue=/opt/rocketmq/rocketmq/store/consumequeue
#消费索引存储路径
storePathIndex=/opt/rocketmq/rocketmq/store/index
#checkpoint文件存储路径
storeCheckpoint=/opt/rocketmq/rocketmq/store/checkpoint
#abort文件存储路径
abortFile=/opt/rocketmq/rocketmq/store/abort
#限制的消息大小
maxMessageSize=65536
# flushCommitLogLeastPages=4
# flushConsumeQueueLeastPages=2
# flushCommitLogThroughInterval=10000
# flushConsumeQueueThoroughInterval=60000
# Broker的角色,ASYNC_MASTER,SYNC_MASTER,SLAVE
brokerRole=ASYNC_MASTER
#刷盘模式:ASYNC_FLUSH,SYNC_FLUSH;先写入磁盘(SYNC_FLUSH)还是先投递消息(ASYNC_FLUSH),不管SYNC还是ASYNC都要落盘
flushDiskType=ASYNC_FLUSH
#checkTranslationMessageEnable=false
#发消息线程池配置
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128

3.2.2、日志目录调整

根据需要调整日志路径
[root@master conf]# sed -i 's#\${user\.home}#/opt/rocketmq/rocketmq#g' logback_namesrv.xml
[root@master conf]# sed -i 's#\${user\.home}#/opt/rocketmq/rocketmq#g' logback_broker.xml

3.2.3、调整jvm配置

注意:配置够的就不需要了,这里主要是本地的虚拟机内存资源不够,因此调整JVM参数

修改runbroker.sh

JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m"

修改runserver.sh

JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

3.2.4、服务启动

#1、namesrv启动
[root@master rocketmq]# nohup sh bin/mqnamesrv &> /dev/null &
[root@master rocketmq]# netstat -tunlp |grep java #默认监听端口9876
tcp6 0 0 :::9876 :::* LISTEN 5544/java #2、broker启动
[root@master rocketmq]# nohup sh bin/mqbroker -c ./conf/single-master.properties &> /dev/null &
[2] 5564
[root@master rocketmq]# netstat -tunlp |grep java
tcp6 0 0 :::9876 :::* LISTEN 5544/java
tcp6 0 0 :::12119 :::* LISTEN 5571/java
tcp6 0 0 :::12121 :::* LISTEN 5571/java
tcp6 0 0 :::12122 :::* LISTEN 5571/java
[root@master rocketmq]#

3.2.5、控制台安装

由rocketmq-console为额外的插件

下载地址:https://github.com/apache/rocketmq-externals/master.zip

#1、修改配置application.properties
[root@master rocketmq-console]# vim src/main/resources/application.properties
server.address=0.0.0.0
server.port=8080 spring.application.name=rocketmq-console
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
logging.level.root=INFO
#日志文件路径,
logging.config=classpath:logback.xml
#需要修改nameserv的地址,或者在console启动后也可以修改
rocketmq.config.namesrvAddr=www.mt.com:9876
rocketmq.config.isVIPChannel=
rocketmq.config.dataPath=/tmp/rocketmq-console/data
rocketmq.config.enableDashBoardCollect=true
rocketmq.config.msgTrackTopicName=
rocketmq.config.ticketKey=ticket rocketmq.config.loginRequired=true #2、修改日志文件路径
[root@master rocketmq-console]# sed -i 's@\${user.home}@/opt/rocketmq/rocketmq@g' src/main/resources/logback.xml #修改文件路径
[root@master rocketmq-console]# sed -i 's@\${user.home}@/opt/rocketmq/rocketmq@g' src/test/resources/logback.xml #3、配置用户账号和密码文件
4root@master rocketmqconsole]# cat src/main/resources/users.properties
# This file supports hot change, any change will be auto-reloaded without Console restarting.
# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin) # Define Admin
admin=admin123!,1 # Define Users
user1=user1
user2=user2 #4、安装
[root@master rocketmq-console]# mvn clean package -Dmaven.test.skip=true
[root@master rocketmq-console]# java -jar target/rocketmq-console-ng-2.0.0.jar --rocketmq.config.namesrvAddr='www.mt.com:9876' #在配置文件中已经指定namesrvAddr这里可以不指定 #5、控制台访问
http://192.168.206.128:8080

3.2.6、发送消息

# 1.设置环境变量
export NAMESRV_ADDR=www.mt.com:9876
# 2.使用安装包的Demo发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer

3.2.7、接收消息

# 1.设置环境变量
export NAMESRV_ADDR=www.mt.com:9876
# 2.接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer

3.3、集群版安装

3.3.1、集群分类

集群分为:

  • 多master-noslave:在单个broker异常的情况下,异常broker上未被消费的数据在恢复之前是不可订阅

  • 多master-多slave(sync):主备之间会存在消息延迟(ms级),master异常的情况下会丢失少量消息

  • 多master-多slave(async):采用双写模式(master和slave都写入成功,才返回成功),在master宕机后当前版本无法正常切换

3.3.2、集群规划

注:所有的节点都要操作

# namerserver
[root@master rocketmq]# vim /etc/hosts #追加如下内容,namesrv也可以有多个,这里只安装了一个
192.168.206.128 www.mt.com [root@master rocketmq]# vim /etc/hosts #broker #追加如下内容
192.168.206.128 www.broker00.com
192.168.206.128 www.broker01.com
192.168.206.129 www.broker10.com
192.168.206.129 www.broker11.com 注:第一个数字为机器(0为128,1为129),第二个数字为主备(0为master,1为slave),非必须,仅供参考 其中broker00和broker11互为主备,broker00为master,broker11为slave
其中broker01和broker10互为主备,broker10为master,broker01为slave [root@master rocketmq]# mkdir -p \
/opt/rocketmq/rocketmq/{broker00,broker01}/store/{commitlog,consumequeue,index,checkpoint,abort} #在128上执行
[root@slave1 rocketmq]# mkdir -p \ /opt/rocketmq/rocketmq/{broker10,broker11}/store/{commitlog,consumequeue,index,checkpoint,abort} #在129上执行

3.3.3、环境初始化

[root@slave1 ~]# yum install java-1.8.0-openjdk-devel -y
[root@slave1 ~]# cat /etc/profile.d/java.sh
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64
export JAVA_BIN=$JAVA_HOME/bin:$JAVA_HOME/jre/bin
[root@slave1 ~]# source /etc/profile.d/java.sh
[root@slave1 ~]# setenforce 0
[root@slave1 ~]# iptables -F #拷贝192.168.206.128上的内容到192.168.206.129
[root@master rocketmq]# scp -r /opt/rocketmq/* root@192.168.206.129:/opt/rocketmq/ [root@slave1 rocketmq]# cat bin/runbroker.sh #JVM配置大小按照自己虚拟机的配置进行调整
JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m"
[root@slave1 rocketmq]# cat bin/runserver.sh #JVM配置大小按照自己虚拟机的配置进行调整
JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

3.3.4、配置文件

注意:日志和jvm调整参考单机版安装步骤,更多的参数可以从rocketmq-console->集群->配置中看到

3.3.4.1、broker00配置

#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker1
#0 表示 Master,>0 表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=www.mt.com:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=12121
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/opt/rocketmq/rocketmq/store/broker00
#commitLog 存储路径
storePathCommitLog=/opt/rocketmq/rocketmq/store/broker00/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/opt/rocketmq/rocketmq/store/broker00/consumequeue
#消息索引存储路径
storePathIndex=/opt/rocketmq/rocketmq/store/broker00/index
#checkpoint 文件存储路径
storeCheckpoint=/opt/rocketmq/rocketmq/store/broker00/checkpoint
#abort 文件存储路径
abortFile=/opt/rocketmq/rocketmq/store/broker00/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
#当前服务器的IP
brokerIP1=192.168.206.128
#供slave同步消息的地址
brokerIP2=192.168.206.128

3.3.4.2、broker01配置

#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker2
#0 表示 Master,>0 表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=www.mt.com:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=18080
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/opt/rocketmq/rocketmq/store/broker01
#commitLog 存储路径
storePathCommitLog=/opt/rocketmq/rocketmq/store/broker01/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/opt/rocketmq/rocketmq/store/broker01/consumequeue
#消息索引存储路径
storePathIndex=/opt/rocketmq/rocketmq/store/broker01/index
#checkpoint 文件存储路径
storeCheckpoint=/opt/rocketmq/rocketmq/store/broker01/checkpoint
#abort 文件存储路径
abortFile=/opt/rocketmq/rocketmq/store/broker01/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
#当前服务器的IP
brokerIP1=192.168.206.128
#供slave同步消息的地址
brokerIP2=192.168.206.128

3.3.4.3、broker10配置

brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker2
#0 表示 Master,>0 表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=www.mt.com:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=12121
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
store/broker10PathRootDir=/opt/rocketmq/rocketmq/store/broker10
#commitLog 存储路径
store/broker10PathCommitLog=/opt/rocketmq/rocketmq/store/broker10/commitlog
#消费队列存储路径存储路径
store/broker10PathConsumeQueue=/opt/rocketmq/rocketmq/store/broker10/consumequeue
#消息索引存储路径
store/broker10PathIndex=/opt/rocketmq/rocketmq/store/broker10/index
#checkpoint 文件存储路径
store/broker10Checkpoint=/opt/rocketmq/rocketmq/store/broker10/checkpoint
#abort 文件存储路径
abortFile=/opt/rocketmq/rocketmq/store/broker10/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
#当前服务器的IP
brokerIP1=192.168.206.129
#供slave同步消息的地址
brokerIP2=192.168.206.129

3.3.4.4、broker11配置

#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker1
#0 表示 Master,>0 表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=www.mt.com:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=18080
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/opt/rocketmq/rocketmq/store/broker11
#commitLog 存储路径
storePathCommitLog=/opt/rocketmq/rocketmq/store/broker11/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/opt/rocketmq/rocketmq/store/broker11/consumequeue
#消息索引存储路径
storePathIndex=/opt/rocketmq/rocketmq/store/broker11/index
#checkpoint 文件存储路径
storeCheckpoint=/opt/rocketmq/rocketmq/store/broker11/checkpoint
#abort 文件存储路径
abortFile=/opt/rocketmq/rocketmq/store/broker11/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128 #当前服务器的IP
brokerIP1=192.168.206.129
#供slave同步消息的地址
brokerIP2=192.168.206.129

3.3.5、服务启动

注意:此时namesrv配置和启动方式和单机版配置一样,不再单独说明

#1、在192.168.206.128上启动broker00和broker01
[root@master rocketmq]# nohup sh bin/mqbroker -c conf/2m-2s-sync/broker00.properties &> /dev/null &
[root@master rocketmq]# nohup sh bin/mqbroker -c conf/2m-2s-sync/broker01.properties &> /dev/null & #2、在192.168.206.129上启动broker10和broker11
[root@slave1 rocketmq]# nohup sh bin/mqbroker -c conf/2m-2s-sync/broker10.properties &> /dev/null &
[root@slave1 rocketmq]# nohup sh bin/mqbroker -c conf/2m-2s-sync/broker11.properties &> /dev/null & #3、控制台查看
http://192.168.206.128:8080/#/cluster 用户名/密码: admin/admin123!

3.3.6、日志检查

1、注意检查进程是否存在
2、检查/opt/rocketmq/rocketmq/logs/ 目录下所有日志插看是否有报错
3、控制台检查

四、运维

4.1、mqadmin命令

进入RocketMQ安装位置,在bin目录下执行./mqadmin {command} {args}

注意事项

  • mqadmin 参数不带会输出所有的command
  • 子命令可以通过-h获取帮助
  • 如果既有Broker地址(-b)配置项又有clusterName(-c)配置项,则优先以Broker地址执行命令;如果不配置Broker地址,则对集群中所有主机执行命令

4.1.2、Topic

名称 含义 命令选项 说明
updateTopic 创建更新Topic配置 -b Broker 地址,表示 topic 所在
Broker,只支持单台Broker,地址为ip:port
-c cluster 名称,表示 topic 所在集群(集群可通过
clusterList 查询)
-h- 打印帮助
-n NameServer服务地址,格式 ip:port
-p 指定新topic的读写权限( W=2|R=4|WR=6 )
-r 可读队列数(默认为 8)
-w 可写队列数(默认为 8)
-t topic 名称(名称只能使用字符
^[a-zA-Z0-9_-]+$ )
deleteTopic 删除Topic -c cluster 名称,表示删除某集群下的某个 topic (集群
可通过 clusterList 查询)
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic 名称(名称只能使用字符
^[a-zA-Z0-9_-]+$ )
topicList 查看 Topic 列表信息 -h 打印帮助
-c 不配置-c只返回topic列表,增加-c返回clusterName,
topic, consumerGroup信息,即topic的所属集群和订阅关系,没有参数
-n NameServer 服务地址,格式 ip:port
topicRoute 查看 Topic 路由信息 -t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
topicStatus 查看 Topic 消息队列offset -t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
topicClusterList 查看 Topic 所在集群列表 -t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
updateTopicPerm 更新 Topic 读写权限 -t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
-b Broker 地址,表示 topic 所在
Broker,只支持单台Broker,地址为ip:port
-p 指定新 topic 的读写权限( W=2|R=4|WR=6 )
-c cluster 名称,表示 topic 所在集群(集群可通过
clusterList 查询),-b优先,如果没有-b,则对集群中所有Broker执行命令
updateOrderConf 从NameServer上创建、删除、获取特定命名空间的kv配置,目前还未启用 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic,键
-v orderConf,值
-m method,可选get、put、delete
allocateMQ 以平均负载算法计算消费者列表负载消息队列的负载结果 -t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
-i ipList,用逗号分隔,计算这些ip去负载Topic的消息队列
statsAll 打印Topic订阅关系、TPS、积累量、24h读写总量等信息 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-a 是否只打印活跃topic
-t 指定topic

4.1.2、集群

名称 含义 命令选项 说明
clusterList 查看集群信息,集群、BrokerName、BrokerId、TPS等信息 -m 打印更多信息 (增加打印出如下信息 #InTotalYest,
#OutTotalYest, #InTotalToday ,#OutTotalToday)
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
-i 打印间隔,单位秒
clusterRT 发送消息检测集群各Broker RT。消息发往${BrokerName} Topic。 -a amount,每次探测的总数,RT = 总时间 /
amount
-s 消息大小,单位B
-c 探测哪个集群
-p 是否打印格式化日志,以|分割,默认不打印
-h 打印帮助
-m 所属机房,打印使用
-i 发送间隔,单位秒
-n NameServer 服务地址,格式 ip:port

4.1.3、Broker

名称 含义 命令选项 说明
updateBrokerConfig 更新 Broker 配置文件,会修改Broker.conf -b Broker 地址,格式为ip:port
-c cluster 名称
-k key 值
-v value 值
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
brokerStatus 查看 Broker 统计信息、运行状态(你想要的信息几乎都在里面) -b Broker 地址,地址为ip:port
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
brokerConsumeStats Broker中各个消费者的消费情况,按Message Queue维度返回Consume
Offset,Broker Offset,Diff,TImestamp等信息
-b Broker 地址,地址为ip:port
-t 请求超时时间
-l diff阈值,超过阈值才打印
-o 是否为顺序topic,一般为false
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
getBrokerConfig 获取Broker配置 -b Broker 地址,地址为ip:port
-n NameServer 服务地址,格式 ip:port
wipeWritePerm 从NameServer上清除 Broker写权限 -b Broker 地址,地址为ip:port
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
cleanExpiredCQ 清理Broker上过期的Consume Queue,如果手动减少对列数可能产生过期队列 -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-b Broker 地址,地址为ip:port
-c 集群名称
cleanUnusedTopic 清理Broker上不使用的Topic,从内存中释放Topic的Consume
Queue,如果手动删除Topic会产生不使用的Topic
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
-b Broker 地址,地址为ip:port
-c 集群名称
sendMsgStatus 向Broker发消息,返回发送状态和RT -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-b BrokerName,注意不同于Broker地址
-s 消息大小,单位B
-c 发送次数

4.1.4、消息

名称 含义 命令选项 说明
queryMsgById 根据offsetMsgId查询msg,如果使用开源控制台,应使用offsetMsgId,此命令还有其他参数,具体作用请阅读QueryMsgByIdSubCommand。 -i msgId
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
queryMsgByKey 根据消息 Key 查询消息 -k msgKey
-t Topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
queryMsgByOffset 根据 Offset 查询消息 -b Broker 名称,(这里需要注意
填写的是 Broker 的名称,不是 Broker 的地址,Broker 名称可以在 clusterList 查到)
-i query 队列 id
-o offset 值
-t topic 名称
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
queryMsgByUniqueKey 根据msgId查询,msgId不同于offsetMsgId,区别详见常见运维问题。-g,-d配合使用,查到消息后尝试让特定的消费者消费消息并返回消费结果 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-i uniqe msg id
-g consumerGroup
-d clientId
-t topic名称
checkMsgSendRT 检测向topic发消息的RT,功能类似clusterRT -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic名称
-a 探测次数
-s 消息大小
sendMessage 发送一条消息,可以根据配置发往特定Message Queue,或普通发送。 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic名称
-p body,消息体
-k keys
-c tags
-b BrokerName
-i queueId
consumeMessage 消费消息。可以根据offset、开始&结束时间戳、消息队列消费消息,配置不同执行不同消费逻辑,详见ConsumeMessageCommand。 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic名称
-b BrokerName
-o 从offset开始消费
-i queueId
-g 消费者分组
-s 开始时间戳,格式详见-h
-d 结束时间戳
-c 消费多少条消息
printMsg 从Broker消费消息并打印,可选时间段 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic名称
-c 字符集,例如UTF-8
-s subExpress,过滤表达式
-b 开始时间戳,格式参见-h
-e 结束时间戳
-d 是否打印消息体
printMsgByQueue 类似printMsg,但指定Message Queue -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-t topic名称
-i queueId
-a BrokerName
-c 字符集,例如UTF-8
-s subExpress,过滤表达式
-b 开始时间戳,格式参见-h
-e 结束时间戳
-p 是否打印消息
-d 是否打印消息体
-f 是否统计tag数量并打印
resetOffsetByTime 按时间戳重置offset,Broker和consumer都会重置 -h 打印帮助
-n NameServer 服务地址,格式 ip:port
-g 消费者分组
-t topic名称
-s 重置为此时间戳对应的offset
-f 是否强制重置,如果false,只支持回溯offset,如果true,不管时间戳对应offset与consumeOffset关系
-c 是否重置c++客户端offset

4.1.5、消费

名称 含义 命令选项 说明
consumerProgress 查看订阅组消费状态,可以查看具体的client IP的消息积累量 -g 消费者所属组名
-s 是否打印client IP
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
consumerStatus 查看消费者状态,包括同一个分组中是否都是相同的订阅,分析Process
Queue是否堆积,返回消费者jstack结果,内容较多,使用者参见ConsumerStatusSubCommand
-h 打印帮助
-n NameServer 服务地址,格式 ip:port
-g consumer group
-i clientId
-s 是否执行jstack
getConsumerStatus 获取 Consumer 消费进度 -g 消费者所属组名
-t 查询主题
-i Consumer 客户端 ip
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
updateSubGroup 更新或创建订阅关系 -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-b Broker地址
-c 集群名称
-g 消费者分组名称
-s 分组是否允许消费
-m 是否从最小offset开始消费
-d 是否是广播模式
-q 重试队列数量
-r 最大重试次数
-i 当slaveReadEnable开启时有效,且还未达到从slave消费时建议从哪个BrokerId消费,可以配置备机id,主动从备机消费
-w 如果Broker建议从slave消费,配置决定从哪个slave消费,配置BrokerId,例如1
-a 当消费者数量变化时是否通知其他消费者负载均衡
deleteSubGroup 从Broker删除订阅关系 -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-b Broker地址
-c 集群名称
-g 消费者分组名称
cloneGroupOffset 在目标群组中使用源群组的offset -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-s 源消费者组
-d 目标消费者组
-t topic名称
-o 暂未使用

4.1.6、连接

名称 含义 命令选项 说明
consumerConnection 查询 Consumer 的网络连接 -g 消费者所属组名
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
producerConnection 查询 Producer 的网络连接 -g 生产者所属组名
-t 主题名称
-n NameServer 服务地址,格式 ip:port
-h 打印帮助

4.1.7、NameSrv

名称 含义 命令选项 说明
updateKvConfig 更新NameServer的kv配置,目前还未使用 -s 命名空间
-k key
-v value
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
deleteKvConfig 删除NameServer的kv配置 -s 命名空间
-k key
-n NameServer 服务地址,格式 ip:port
-h 打印帮助
getNamesrvConfig 获取NameServer配置 -n NameServer 服务地址,格式 ip:port
-h 打印帮助
updateNamesrvConfig 修改NameServer配置 -n NameServer 服务地址,格式 ip:port
-h 打印帮助
-k key
-v value

4.1.8、其他

名称 含义 命令选项 说明
startMonitoring 开启监控进程,监控消息误删、重试队列消息数等 -n NameServer 服务地址,格式 ip:port
-h 打印帮助

4.2、mqadmin常用方法

4.2.1、mqadmin命令异常处理

[root@master rocketmq]# sh bin/mqadmin clusterlist
报错" unable to calculate a request signature. error=[10015:signature-failed] "
[root@master rocketmq]# cat bin/tools.sh #修改
[root@master rocketmq]# cat /etc/profile.d/java.sh
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64
export JAVA_BIN=$JAVA_HOME/bin:$JAVA_HOME/jre/bin
[root@master rocketmq]# source /etc/profile.d/java.sh [root@master rocketmq]# cat /etc/profile.d/java.sh
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64
export JAVA_BIN=$JAVA_HOME/bin:$JAVA_HOME/jre/bin
[root@master rocketmq]# sh bin/mqadmin -h
OpenJDK 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
这里的warning信息,可以在bin/tools.sh 中去掉这两个参数

4.2.2、mqadmin常用方法

1、调整topic分片数量
[root@master rocketmq]# sh bin/mqadmin topiclist -n www.mt.com:9876 #查看当前的tpoic
topic01
...
[root@master rocketmq]#
[root@master rocketmq]# sh bin/mqadmin updatetopic -t topic01 -c rocketmq-cluster -w 4 -r 4 -n www.mt.com:9876
create topic to 192.168.206.129:12121 success.
create topic to 192.168.206.128:12121 success.
TopicConfig [topicName=topic01, readQueueNums=4, writeQueueNums=4, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false] 2、更新topic的权限 / 同upTopicPerm
[root@master rocketmq]# sh bin/mqadmin updatetopic -t topic01 -c rocketmq-cluster -p 6 -n www.mt.com:9876
create topic to 192.168.206.129:12121 success.
create topic to 192.168.206.128:12121 success.
TopicConfig [topicName=topic01, readQueueNums=8, writeQueueNums=8, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false],禁读的意思是禁止发送消息,禁写的意思是禁止消息写入 3、更新CID重试Topic的分片数量
[root@master rocketmq]# sh bin/mqadmin updatetopic -t topic01 -c rocketmq-cluster -p 6 -n www.mt.com:9876
create topic to 192.168.206.129:12121 success.
create topic to 192.168.206.128:12121 success.
TopicConfig [topicName=topic01, readQueueNums=8, writeQueueNums=8, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false],禁读的意思是禁止发送消息,禁写的意思是禁止消息写入 4、查看集群内所有的topic
[root@master rocketmq]# sh bin/mqadmin topiclist -c rocketmq-cluster -n www.mt.com:9876
#Cluster Name #Topic #Consumer Group
rocketmq-cluster broker2
rocketmq-cluster broker1
... 5、查看topic所属集群
[root@master rocketmq]# sh bin/mqadmin topicClusterlist -t topic01 -n www.mt.com:9876
rocketmq-cluster 6、查看topic路由信息
[root@master rocketmq]# sh bin/mqadmin topicroute -t topic01 -n www.mt.com:9876 {
"brokerDatas":[
{
"brokerAddrs":{0:"192.168.206.129:12121",1:"192.168.206.128:18080"
},
"brokerName":"broker2",
"cluster":"rocketmq-cluster"
},
{
"brokerAddrs":{0:"192.168.206.128:12121",1:"192.168.206.129:18080"
},
"brokerName":"broker1",
"cluster":"rocketmq-cluster"
}
],
"filterServerTable":{},
"queueDatas":[
{
"brokerName":"broker2",
"perm":6,
"readQueueNums":8,
"topicSynFlag":0,
"writeQueueNums":8
},
{
"brokerName":"broker1",
"perm":6,
"readQueueNums":8,
"topicSynFlag":0,
"writeQueueNums":8
}
]
} 7、查看topic统计信息
[root@master rocketmq]# sh bin/mqadmin topicstatus -t topic01 -n www.mt.com:9876
#Broker Name #QID #Min Offset #Max Offset #Last Updated
broker1 0 0 0
broker1 1 0 0
broker1 2 0 0
broker1 3 0 0
broker1 4 0 0
broker1 5 0 0
broker1 6 0 0
broker1 7 0 0
broker2 0 0 0
broker2 1 0 0
broker2 2 0 0
broker2 3 0 0
broker2 4 0 0
broker2 5 0 0
broker2 6 0 0
broker2 7 0 0
在消息不均衡的情况下,maxOffset会相差较大,普通消息不回出现这种情况。在顺序消息时会出现这种情况,需要重新设置shardingkey避免热点问题 8、查看broker状态信息
[root@master rocketmq]# sh bin/mqadmin brokerstatus -b 192.168.206.128:12121 -n www.mt.com:9876
EndTransactionQueueSize : 0
EndTransactionThreadPoolQueueCapacity: 100000
bootTimestamp : 1604289742467
brokerVersion : 313
brokerVersionDesc : V4_5_0
commitLogDirCapacity : Total : 45.6 GiB, Free : 40.3 GiB.
commitLogDiskRatio : -1.0
commitLogMaxOffset : 0
commitLogMinOffset : -1
consumeQueueDiskRatio : -1.0
dispatchBehindBytes : 0
dispatchMaxBuffer : 0
earliestMessageTimeStamp : -1
getFoundTps : 0.0 0.0 0.0
getMessageEntireTimeMax : 9
getMissTps : 0.0 0.03331390022486883 0.033317785033650966
getTotalTps : 0.0 0.03331390022486883 0.033317785033650966
getTransferedTps : 0.0 0.0 0.0
msgGetTotalTodayMorning : 0
msgGetTotalTodayNow : 0
msgGetTotalYesterdayMorning : 0
msgPutTotalTodayMorning : 0
msgPutTotalTodayNow : 0
msgPutTotalYesterdayMorning : 0
pageCacheLockTimeMills : 0
pullThreadPoolQueueCapacity : 100000
pullThreadPoolQueueHeadWaitTimeMills: 0
pullThreadPoolQueueSize : 0
putMessageAverageSize : 0.0
putMessageDistributeTime : [<=0ms]:0 [0~10ms]:0 [10~50ms]:0 [50~100ms]:0 [100~200ms]:0 [200~500ms]:0 [500ms~1s]:0 [1~2s]:0 [2~3s]:0 [3~4s]:0 [4~5s]:0 [5~10s]:0 [10s~]:0
putMessageEntireTimeMax : 0
putMessageSizeTotal : 0
putMessageTimesTotal : 1
putTps : 0.0 0.0 0.0
queryThreadPoolQueueCapacity : 20000
queryThreadPoolQueueHeadWaitTimeMills: 0
queryThreadPoolQueueSize : 0
remainHowManyDataToFlush : 0 B
remainTransientStoreBufferNumbs : 2147483647
runtime : [ 0 days, 7 hours, 24 minutes, 19 seconds ]
sendThreadPoolQueueCapacity : 10000
sendThreadPoolQueueHeadWaitTimeMills: 0
sendThreadPoolQueueSize : 0
startAcceptSendRequestTimeStamp : 0 参数:putMessageDistributeTime 表示发送消息耗时的热力图,可关注 9、禁写broker,用于升级broker
#sh mqadmin wipeWritePerm -b 192.168.206.128:12121 -n www.mt.com:9876 10、更新broker配置,不需要重启
[root@master rocketmq]# sh bin/mqadmin updatebrokerconfig -k sendThreadPoolQueueCapacity -v 10000 -b 192.168.206.128:12121 -n www.mt.com:9876
update broker config success, 192.168.206.128:12121 11、查看producer的机器列表
[root@master rocketmq]# sh bin/mqadmin producerconnection -g group01 -t topic01 -n www.mt.com:9876 12、查看consumer的网络连接,用来排查订阅关系
[root@master rocketmq]# sh bin/mqadmin consumerconnection -g group01 -n www.mt.com:9876 13、查看broker上所有消费者的消费状态,类似于consumerprogress
[root@master rocketmq]# sh bin/mqadmin brokerConsumeStats -b 192.168.206.128:12121 -n www.mt.com:9876 14、查看broker上所有消费者的消费状态,类似于consumerprogress
[root@master rocketmq]# sh bin/mqadmin consumerprogress -g group01 -n www.mt.com:9876
QID:为分片在broker上的id,BrokerOffset:消息发送位点,ConsumeOffset:消息消费位点,Diff:消息堆积量 15、查看topic指定时间的消息
[root@master rocketmq]# sh bin/mqadmin printMsg -b 开始时间(格式:yyyy-MM-dd#HH:mm:ss:SSS) -e 结束时间(格式同开始时间) [-d 是否打印消息体] [ -s 消息标签 tagA||TAGB] [-c 编码格式] -t topic01
死信topic:%DLQ%cid;cid注意替换为自己的,如果控制台看不到可能是没有读取权限可先授权 16、重置消费者消费位点
[root@master rocketmq]# sh bin/mqadmin resetOffsetByTime -g 消费者CID -s 时间(格式:yyyy-MM-dd#HH:mm:ss:SSS) -t Topic名称
注意:重置CID的消费位点,先查看消费者的对接数量,只有消费者在线才能重置消费位点,控制台上重置消费位点重置的是业务topic的消费位点。如果重试之后总的堆积量很大,每个的堆积量很小,查看consumerprogress看是否有重试队列堆积。如果重试队列有大量堆积需要清理重试队列用该命令,重试topic的方式:%RETRY%cid

4.3、使用mqadmin收发消息

待补充

4.4、日志

文件名称 用途 说明
${rocketmq_home}/store/commitlog/* 消息存储文件,用户存放生产者提交的消息 1)配置项mapedFileSizeCommitLog限制文件大小;2)deleteWhen确定旧数据的删除时间;3)diskMaxUsedSpaceRatio磁盘空间使用率多大时开始清理旧数据;4)cleanFileForciblyEnable当磁盘空间不足时,是否强制删除旧文件
${rocketmq_home}/store/config/consumerOffset.json 消费位点存储文件 Borker自动管理,切勿手动操作
${rocketmq_home}/store/config/delayOffset.json 定时消息消费位点 Borker自动管理,切勿手动操作
${rocketmq_home}/store/config/subscriptionGroup.json 订阅组配置 Borker自动管理,切勿手动操作
${rocketmq_home}/store/config/topics.json topic配置 Borker自动管理,切勿手动操作
${rocketmq_home}/logs/rocketmqlogs/broker.log 通过register信息取人到broker的注册
${rocketmq_home}/logs/rocketmqlogs/remoting.log 判断网络何时断开
${rocketmq_home}/logs/rocketmqlogs/failover_controller.log 主备切换时间点
${rocketmq_home}/logs/rocketmqlogs/filter.log 控制过滤
${rocketmq_home}/logs/rocketmqlogs/lock.log consumer分配queue的绑定
${rocketmq_home}/logs/rocketmqlogs/time.log 定时消息
${rocketmq_home}/logs/rocketmqlogs/translation.log 事务消息
${rocketmq_home}/logs/rocketmqlogs/stats.log broker的tps
${rocketmq_home}/logs/rocketmqlogs/store.log TPS信息
${rocketmq_home}/logs/rocketmqlogs/dup.log 主备切换
${rocketmq_home}/logs/rocketmqlogs/dup-stats.log 性能监 :任务数
${rocketmq_home}/logs/rocketmqlogs/dup-study.log 著别异步复制状态

五、消息发送示例

  • 创建maven项目
Intelij IDEA->FIle->New Project->[1、选择JDK ;2、create from archetype不勾选;]->New Project[如下图] -> Project name和project Directory 选择

.png)

创建project参考如图:

  • 导入rocketmq客户端依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>

maven依赖引入后注意检查是否导入成功

  • 消息发送者步骤
1.创建producer,并制定生产者组名
2.指定Namesrv地址
3.启动producer
4.创建消息对象,指定主题Topic、Tag和消息体
5.发送消息
6.关闭生产者producer
  • 消息消费者步骤分析
1.创建消费者Consumer,制定消费者组名
2.指定Namesrv地址
3.订阅主题Topic和Tag
4.设置回调函数,处理消息
5.启动消费者consumer

5.1、普通消息发送

5.1.1 消息发送

1)发送同步消息

这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。

public class SyncProducer {
public static void main(String[] args) throws Exception {
//1.创建producer,并指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("group1");
//2.指定Namesrv地址
producer.setNamesrvAddr("192.168.206.128:9876");
//3.启动producer
producer.start();
//4.创建消息对象,指定主题Topic、Tag和消息体
for (int i=0;i<10;i++) {
Message msg = new Message("base","tag1",("Hello world,I am base"+i).getBytes());
// 参数内容:topic,tag,body
//5.发送消息
SendResult result = producer.send(msg); //同步消息发送模式,会等待broker回传一个结果,程序才继续往下走
SendStatus status = result.getSendStatus(); //发送状态
String msgID = result.getMsgId(); //消息id
int queueID = result.getMessageQueue().getQueueId(); //消息接收队列的id
System.out.println("发送状态:"+result+"消息ID:"+msgID+"队列ID:"+queueID);
TimeUnit.SECONDS.sleep(1); //等待1s,然偶
}
//6.关闭生产者producer
producer.shutdown();
}
}

2)发送异步消息

异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。

public class AsyncProducer {
public static void main(String[] args) throws Exception {
//1.创建producer,并指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("group1");
//2.指定Namesrv地址
producer.setNamesrvAddr("192.168.206.128:9876");
//3.启动producer
producer.start();
//4.创建消息对象,指定主题Topic、Tag和消息体
for (int i=0;i<10;i++) {
Message msg = new Message("base","tag2",("Hello world,I am base"+i).getBytes());
// 参数内容:topic,tag,body
//5.发送异步消息
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
//发送成功的回调函数
System.out.println("发送结果:" + sendResult); }
@Override
public void onException(Throwable e) {
//发送失败的回调函数
System.out.println("发送异常:" + e);
}
});
TimeUnit.SECONDS.sleep(1); //等待1s,然偶
}
//6.关闭生产者producer
producer.shutdown();
}
}

3)单向发送消息

这种方式主要用在不特别关心发送结果的场景,例如日志发送。

public class OnewayProducer {
public static void main(String[] args) throws Exception{
// 实例化消息生产者Producer
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
// 设置NameServer的地址
producer.setNamesrvAddr("www.mt.com:9876");
// 启动Producer实例
producer.start();
for (int i = 0; i < 100; i++) {
// 创建消息,并指定Topic,Tag和消息体
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
// 发送单向消息,没有任何返回结果
producer.sendOneway(msg);
}
// 如果不再发送消息,关闭Producer实例。
producer.shutdown();
}
}

5.1.2 消费消息

1)集群模式

消费者采用负载均衡方式消费消息,多个消费者共同消费队列消息,每个消费者处理的消息不同

public class Consumer {
public static void main(String[] args) throws Exception { // 1.创建消费者Consumer,制定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
// 2.指定Namesrv地址
consumer.setNamesrvAddr("192.168.206.128:9876");
// 3.订阅主题Topic和Tag
consumer.subscribe("base","tag2");
// 4.设置回调函数,处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
//接收消息内容
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.println(list);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 5.启动消费者consumer
consumer.start();
// 实例化消息生产者,指定组名 }
}

2)广播模式

消费者采用广播的方式消费消息,每个消费者消费的消息都是相同的

public static void main(String[] args) throws Exception {
// 实例化消息生产者,指定组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
// 指定Namesrv地址信息.
consumer.setNamesrvAddr("www.mt.com:9876");
// 订阅Topic
consumer.subscribe("Test", "*");
//广播模式消费
consumer.setMessageModel(MessageModel.BROADCASTING);
// 注册回调函数,处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n",
Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消息者
consumer.start();
System.out.printf("Consumer Started.%n");
}

5.2、顺序消息

消息有序指的是可以按照消息的发送顺序来消费(FIFO)。RocketMQ可以严格的保证消息有序,可以分为分区有序或者全局有序。

顺序消费的原理解析,在默认的情况下消息发送会采取Round Robin轮询方式把消息发送到不同的queue(分区队列);而消费消息的时候从多个queue上拉取消息,这种情况发送和消费是不能保证顺序。但是如果控制发送的顺序消息只依次发送到同一个queue中,消费的时候只从这个queue上依次拉取,则就保证了顺序。当发送和消费参与的queue只有一个,则是全局有序;如果多个queue参与,则为分区有序,即相对每个queue,消息都是有序的。

下面用订单进行分区有序的示例。一个订单的顺序流程是:创建、付款、推送、完成。订单号相同的消息会被先后发送到同一个队列中,消费时,同一个OrderId获取到的肯定是同一个队列。

5.2.1 顺序消息生产

/**
* Producer,发送顺序消息
*/
public class Producer { public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); producer.setNamesrvAddr("www.mt.com:9876"); producer.start(); String[] tags = new String[]{"TagA", "TagC", "TagD"}; // 订单列表
List<OrderStep> orderList = new Producer().buildOrders(); Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = sdf.format(date);
for (int i = 0; i < 10; i++) {
// 加个时间前缀
String body = dateStr + " Hello RocketMQ " + orderList.get(i);
Message msg = new Message("TopicTest", tags[i % tags.length], "KEY" + i, body.getBytes()); SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Long id = (Long) arg; //根据订单id选择发送queue
long index = id % mqs.size();
return mqs.get((int) index);
}
}, orderList.get(i).getOrderId());//订单id System.out.println(String.format("SendResult status:%s, queueId:%d, body:%s",
sendResult.getSendStatus(),
sendResult.getMessageQueue().getQueueId(),
body));
} producer.shutdown();
} /**
* 订单的步骤
*/
private static class OrderStep {
private long orderId;
private String desc; public long getOrderId() {
return orderId;
} public void setOrderId(long orderId) {
this.orderId = orderId;
} public String getDesc() {
return desc;
} public void setDesc(String desc) {
this.desc = desc;
} @Override
public String toString() {
return "OrderStep{" +
"orderId=" + orderId +
", desc='" + desc + '\'' +
'}';
}
} /**
* 生成模拟订单数据
*/
private List<OrderStep> buildOrders() {
List<OrderStep> orderList = new ArrayList<OrderStep>(); OrderStep orderDemo = new OrderStep();
orderDemo.setOrderId(15103111039L);
orderDemo.setDesc("创建");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111065L);
orderDemo.setDesc("创建");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111039L);
orderDemo.setDesc("付款");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103117235L);
orderDemo.setDesc("创建");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111065L);
orderDemo.setDesc("付款");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103117235L);
orderDemo.setDesc("付款");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111065L);
orderDemo.setDesc("完成");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111039L);
orderDemo.setDesc("推送");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103117235L);
orderDemo.setDesc("完成");
orderList.add(orderDemo); orderDemo = new OrderStep();
orderDemo.setOrderId(15103111039L);
orderDemo.setDesc("完成");
orderList.add(orderDemo); return orderList;
}
}

5.2.2 顺序消费消息

/**
* 顺序消息消费,带事务方式(应用可控制Offset什么时候提交)
*/
public class ConsumerInOrder { public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new
DefaultMQPushConsumer("please_rename_unique_group_name_3");
consumer.setNamesrvAddr("www.mt.com:9876");
/**
* 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
* 如果非第一次启动,那么按照上次消费的位置继续消费
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer.subscribe("TopicTest", "TagA || TagC || TagD"); consumer.registerMessageListener(new MessageListenerOrderly() { Random random = new Random(); @Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
context.setAutoCommit(true);
for (MessageExt msg : msgs) {
// 可以看到每个queue有唯一的consume线程来消费, 订单对每个queue(分区)有序
System.out.println("consumeThread=" + Thread.currentThread().getName() + "queueId=" + msg.getQueueId() + ", content:" + new String(msg.getBody()));
} try {
//模拟业务逻辑处理中...
TimeUnit.SECONDS.sleep(random.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
return ConsumeOrderlyStatus.SUCCESS;
}
}); consumer.start(); System.out.println("Consumer Started.");
}
}

5.3、延时消息/定时消息

比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

5.3.1 启动消息消费者

public class ScheduledMessageConsumer {
public static void main(String[] args) throws Exception {
// 实例化消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ExampleConsumer");
// 订阅Topics
consumer.subscribe("TestTopic", "*");
// 注册消息监听者
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> messages, ConsumeConcurrentlyContext context) {
for (MessageExt message : messages) {
// Print approximate delay time period
System.out.println("Receive message[msgId=" + message.getMsgId() + "] " + (System.currentTimeMillis() - message.getStoreTimestamp()) + "ms later");
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 启动消费者
consumer.start();
}
}

5.3.2 发送延时消息

public class ScheduledMessageProducer {
public static void main(String[] args) throws Exception {
// 实例化一个生产者来产生延时消息
DefaultMQProducer producer = new DefaultMQProducer("ExampleProducerGroup");
// 启动生产者
producer.start();
int totalMessagesToSend = 100;
for (int i = 0; i < totalMessagesToSend; i++) {
Message message = new Message("TestTopic", ("Hello scheduled message " + i).getBytes());
// 设置延时等级3,这个消息将在10s之后发送(现在只支持固定的几个时间,详看delayTimeLevel)
message.setDelayTimeLevel(3);
// 发送消息
producer.send(message);
}
// 关闭生产者
producer.shutdown();
}
}

5.3.3 验证

您将会看到消息的消费比存储时间晚10秒

5.3.4 使用限制

// org/apache/rocketmq/store/config/MessageStoreConfig.java
private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";

现在RocketMq并不支持任意时间的延时,需要设置几个固定的延时等级,从1s到2h分别对应着等级1到18

5.4、批量消息

批量发送消息能显著提高传递小消息的性能。限制是这些批量消息应该有相同的topic,相同的waitStoreMsgOK,而且不能是延时消息。此外,这一批消息的总大小不应超过4MB。

5.4.1 发送批量消息

如果您每次只发送不超过4MB的消息,则很容易使用批处理,样例如下:

String topic = "BatchTest";
List<Message> messages = new ArrayList<>();
messages.add(new Message(topic, "TagA", "OrderID001", "Hello world 0".getBytes()));
messages.add(new Message(topic, "TagA", "OrderID002", "Hello world 1".getBytes()));
messages.add(new Message(topic, "TagA", "OrderID003", "Hello world 2".getBytes()));
try {
producer.send(messages);
} catch (Exception e) {
e.printStackTrace();
//处理error
}

如果消息的总长度可能大于4MB时,这时候最好把消息进行分割

public class ListSplitter implements Iterator<List<Message>> {
private final int SIZE_LIMIT = 1024 * 1024 * 4;
private final List<Message> messages;
private int currIndex;
public ListSplitter(List<Message> messages) {
this.messages = messages;
}
@Override
public boolean hasNext() {
return currIndex < messages.size();
}
@Override
public List<Message> next() {
int nextIndex = currIndex;
int totalSize = 0;
for (; nextIndex < messages.size(); nextIndex++) {
Message message = messages.get(nextIndex);
int tmpSize = message.getTopic().length() + message.getBody().length;
Map<String, String> properties = message.getProperties();
for (Map.Entry<String, String> entry : properties.entrySet()) {
tmpSize += entry.getKey().length() + entry.getValue().length();
}
tmpSize = tmpSize + 20; // 增加日志的开销20字节
if (tmpSize > SIZE_LIMIT) {
//单个消息超过了最大的限制
//忽略,否则会阻塞分裂的进程
if (nextIndex - currIndex == 0) {
//假如下一个子列表没有元素,则添加这个子列表然后退出循环,否则只是退出循环
nextIndex++;
}
break;
}
if (tmpSize + totalSize > SIZE_LIMIT) {
break;
} else {
totalSize += tmpSize;
} }
List<Message> subList = messages.subList(currIndex, nextIndex);
currIndex = nextIndex;
return subList;
}
}
//把大的消息分裂成若干个小的消息
ListSplitter splitter = new ListSplitter(messages);
while (splitter.hasNext()) {
try {
List<Message> listItem = splitter.next();
producer.send(listItem);
} catch (Exception e) {
e.printStackTrace();
//处理error
}
}

5.5、过滤消息

在大多数情况下,TAG是一个简单而有用的设计,其可以来选择您想要的消息。例如:

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_EXAMPLE");
consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");

消费者将接收包含TAGA或TAGB或TAGC的消息。但是限制是一个消息只能有一个标签,这对于复杂的场景可能不起作用。在这种情况下,可以使用SQL表达式筛选消息。SQL特性可以通过发送消息时的属性来进行计算。在RocketMQ定义的语法下,可以实现一些简单的逻辑。下面是一个例子:

------------
| message |
|----------| a > 5 AND b = 'abc'
| a = 10 | --------------------> Gotten
| b = 'abc'|
| c = true |
------------
------------
| message |
|----------| a > 5 AND b = 'abc'
| a = 1 | --------------------> Missed
| b = 'abc'|
| c = true |
------------

5.5.1 SQL基本语法

RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。

  • 数值比较,比如:>,>=,<,<=,BETWEEN,=;
  • 字符比较,比如:=,<>,IN;
  • IS NULL 或者 IS NOT NULL;
  • 逻辑符号 AND,OR,NOT;

常量支持类型为:

  • 数值,比如:123,3.1415;
  • 字符,比如:'abc',必须用单引号包裹起来;
  • NULL,特殊的常量
  • 布尔值,TRUEFALSE

只有使用push模式的消费者才能用使用SQL92标准的sql语句,接口如下:

public void subscribe(finalString topic, final MessageSelector messageSelector)

5.5.2 消息生产者

发送消息时,你能通过putUserProperty来设置消息的属性

DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.start();
Message msg = new Message("TopicTest",
tag,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
);
// 设置一些属性
msg.putUserProperty("a", String.valueOf(i));
SendResult sendResult = producer.send(msg); producer.shutdown();

5.5.3 消息消费者

用MessageSelector.bySql来使用sql筛选消息

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
// 只有订阅的消息有这个属性a, a >=0 and a <= 3
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();

5.6、事务消息

5.6.1 流程分析

上图说明了事务消息的大致方案,其中分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程。

1)事务消息发送及提交

(1) 发送消息(half消息)。

(2) 服务端响应消息写入结果。

(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)。

(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)

2)事务补偿

(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”

(2) Producer收到回查消息,检查回查消息对应的本地事务的状态

(3) 根据本地事务状态,重新Commit或者Rollback

其中,补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况。

3)事务消息状态

事务消息共有三种状态,提交状态、回滚状态、中间状态:

  • TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息。
  • TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
  • TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。

4.6.1 发送事务消息

1) 创建事务性生产者

使用 TransactionMQProducer类创建生产者,并指定唯一的 ProducerGroup,就可以设置自定义线程池来处理这些检查请求。执行本地事务后、需要根据执行结果对消息队列进行回复。回传的事务状态在请参考前一节。

public class Producer {
public static void main(String[] args) throws MQClientException, InterruptedException {
//创建事务监听器
TransactionListener transactionListener = new TransactionListenerImpl();
//创建消息生产者
TransactionMQProducer producer = new TransactionMQProducer("group6");
producer.setNamesrvAddr("www.mt.com");
//生产者这是监听器
producer.setTransactionListener(transactionListener);
//启动消息生产者
producer.start();
String[] tags = new String[]{"TagA", "TagB", "TagC"};
for (int i = 0; i < 3; i++) {
try {
Message msg = new Message("TransactionTopic", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
TimeUnit.SECONDS.sleep(1);
} catch (MQClientException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//producer.shutdown();
}
}

2)实现事务的监听接口

当发送半消息成功时,我们使用 executeLocalTransaction 方法来执行本地事务。它返回前一节中提到的三个事务状态之一。checkLocalTranscation 方法用于检查本地事务状态,并回应消息队列的检查请求。它也是返回前一节中提到的三个事务状态之一。

public class TransactionListenerImpl implements TransactionListener {

    @Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
System.out.println("执行本地事务");
if (StringUtils.equals("TagA", msg.getTags())) {
return LocalTransactionState.COMMIT_MESSAGE;
} else if (StringUtils.equals("TagB", msg.getTags())) {
return LocalTransactionState.ROLLBACK_MESSAGE;
} else {
return LocalTransactionState.UNKNOW;
} } @Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
System.out.println("MQ检查消息Tag【"+msg.getTags()+"】的本地事务执行结果");
return LocalTransactionState.COMMIT_MESSAGE;
}
}

5.6.2 使用限制

  1. 事务消息不支持延时消息和批量消息。
  2. 为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次,但是用户可以通过 Broker 配置文件的 transactionCheckMax参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = transactionCheckMax ) 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。用户可以通过重写 AbstractTransactionCheckListener 类来修改这个行为。
  3. 事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。当发送事务消息时,用户还可以通过设置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionMsgTimeout 参数。
  4. 事务性消息可能不止一次被检查或消费。
  5. 提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
  6. 事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。

rocketmq详解-[个人版]-第一章的更多相关文章

  1. Linux常用命令详解(第一章)(ls、man、pwd、cd、mkdir、echo、touch、cp、mv、rm、rmdir、)

    本章命令(共11个): 1 2 3 4 5 6 ls man pwd cd mkdir echo touch cp mv rm rmdir 1. " ls " 作用:列出指定目录下 ...

  2. RocketMQ详解(二)安装使用详解

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  3. RocketMQ详解(一)原理概览

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  4. RocketMQ详解(三)启动运行原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  5. RocketMQ详解(四)核心设计原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  6. 《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析

    第三章 Android控件架构与自定义控件详解 1.Android控件架构下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的.PhoneWin ...

  7. ASP.NET页面与IIS底层交互和工作原理详解(第一回)

    引言 我查阅过不少Asp.Net的书籍,发现大多数作者都是站在一个比较高的层次上讲解Asp.Net.他们耐心.细致地告诉你如何一步步拖放控件.设置控件属性.编写CodeBehind代码,以实现某个特定 ...

  8. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  9. CATIA 基础详解 第01章 CATIA初认识

    1.1 CATIA V5产品介绍 CATIA V5是基于美国IBM公司与法国达索系统公司(Dassault Systèmes)软件解决方案推出的新一代产品,它致力于满足以设计流程为中心的设计需求.它提 ...

随机推荐

  1. cmd/powershell常用命令 git常用命令

    cmd/powershell: 1. 新建文件夹: mkdir directoryName 2. 新建文件: cmd: type nul>fileName (空文件) powershell: n ...

  2. Ambari仓库安装教程

    Ambari仓库安装教程 如果用户需要后续使用Ambari server进行安装Hadoop则必须搭建一个内部的yum源,否则直接下载速度将会很慢,当然该服务仅要搭建一个即可,可以一直使用. 一.Ce ...

  3. h5 返回上一页面方法

    //以下方法仅供参考1.返回上一页,不刷新history.html window.history.go(-1);  javascript:window.history.go(-1) 2.返回上一页并刷 ...

  4. 涨知识的一个pwn题:de1ctf_2019_weapon

    没做出来,wtcl,看了师傅们的wp才找到思路,收获了很多 怎么说呢,这个题很简单但是很巧妙,逆起来几乎无难度 漏洞点位于free函数,一个简单的UAF漏洞 然后接下来说说我一开始的思路 由于程序没有 ...

  5. 每日10句:day1

    1,plt.style.use('ggplot') #使用R语言的图像配色方案 2,for a,b in zip(x,y): plt.text(a,b+1,'%.0f'%b,ha='center',v ...

  6. PLC模拟量输入和数字量输入是什么

    数字信号输入输出: 就是开关闭合,断开. 模拟量输入输出: 就是一个数值.比如:液位1.5米,温度30度,这样的数. 输入单元 输入单元是PLC与被控设备相连的输入接口,是信号进入PLC的桥梁,它的作 ...

  7. Jetbrains全系列产品 2020最新激活方法 (即时更新)

    即时更新:http://idea.itmatu.com/key Jetbrains全系列产品 2020最新激活方法 JMFL04QVQA-eyJsaWNlbnNlSWQiOiJKTUZMMDRRVlF ...

  8. Codeforces 1404 D. Game of Pairs

    Codeforces 1404 D.Game of Pairs 给定\(2n\)个数\(1,2,...,2n\),A 和 B 将进行交互,规则如下: A 需要将元素分成 n 组 \(\mathbf{p ...

  9. 「SHOI2014」三叉神经树

    「SHOI2014」三叉神经树 给你一颗由\(n\)个非叶子结点和\(2n+1\)个叶子结点构成的完全三叉树,每个叶子结点有一个输出:\(0\)或\(1\),每个非叶子结点的输出为自己的叶子结点中较多 ...

  10. .net 实现 一二级分类

    public List<Model.Category> CategoryPid(int id = 0) { string sql = "select * from categor ...