由于Rocketmq使用java写的,所以运行环境需要jdk
第一步:将文件jdk-8u212-linux-x64.tar.gz(jdk)
上传到 /root 目录
第二步:执行解压命令
# 将jdk解压到 /usr/local/ 目录
tar -xf jdk-8u212-linux-x64.tar.gz -C /usr/local/
# 切换到 /usr/local/ 目录, 显示列表, 查看解压缩的jdk目录
cd /usr/local
ll
第三步:配置环境变量
修改 /etc/profile 配置文件, 配置环境变量
vim /etc/profile
# 在文件末尾添加以下内容:
export JAVA_HOME=/usr/local/jdk1.8.0_212
export PATH=$JAVA_HOME/bin:$PATH
修改完后, 让环境变量立即生效
source /etc/profile
第四步:验证
java -version
----------------------------------------------------------------
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
javac -version
---------------
javac 1.8.0_212
第一步:下载 rocketmq 二进制文件
wget https://mirror.bit.edu.cn/apache/rocketmq/4.7.0/rocketmq-all-4.7.0-bin-release.zip
第二步:解压缩 rocketmq
将 rocketmq 解压到 /usr/local/ 目录 unzip rocketmq-all-4.7.0-bin-release.zip -d /usr/local/ # 修改一下文件夹名,改成 rocketmq 方便使用 mv /usr/local/rocketmq-all-4.7.0-bin-release /usr/local/rocketmq
第三步:配置环境变量 ROCKETMQ_HOME 和 PATH
为了后续操作方便可以配置环境变量,之后在任意位置都可以执行rocketmq的操作命令。
vim /etc/profile
# 在文件末尾添加以下内容:
export ROCKETMQ_HOME=/usr/local/rocketmq export PATH=$ROCKETMQ_HOME/bin:$PATH
修改完后, 让环境变量立即生效
source /etc/profile
第四步:减小 rocketmq 使用的内存
rocketmq需要启动两个服务: name server 和 broker, name server 默认配置JVM使用的内存是4g, broker默认配置JVM使用的内存是8g.开发环境中如果内存不足, 服务可能会无法启动, 可以通过降低两个服务的内存, 使服务可以正常启动, 也可以节省内存.
修改 name server 内存改为 256m
cd /usr/local/rocketmq/ # 编辑 bin/runserver.sh vim bin/runserver.sh # 找到文件中下面这一行: JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" # 将 -Xms4g -Xmx4g -Xmn2g 修改为 -Xms256m -Xmx256m -Xmn128m JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
修改
broker
内存改为 256m# 编辑 bin/runbroker.sh vim bin/runbroker.sh # 找到文件中下面这一行: JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g" # 将 -Xms8g -Xmx8g -Xmn4g 修改为 -Xms256m -Xmx256m -Xmn128m JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
第五步,启动 rocketmq
先启动
name server
# 进入 rocketmq 目录 cd /usr/local/rocketmq/ # 启动 name server nohup sh bin/mqnamesrv & # 查看运行日志, 看到"The Name Server boot success."表示启动成功 tail -f ~/logs/rocketmqlogs/namesrv.log
再启动
broker
# 启动 broker, 连接name server: localhost:9876 nohup sh bin/mqbroker -n localhost:9876 & # 查看运行日志, 看到"The broker[......:10911] boot success."表示启动成功 tail -f ~/logs/rocketmqlogs/broker.log
第六步. 关闭防火墙
rocketmq的通信会用到多个端口, 为了方便测试我们关闭防火墙
# 关闭防火墙 systemctl stop firewalld.service # 禁止防火墙开机启动 systemctl disable firewalld.service
第七步:测试
运行测试, 启动生产者发送消息, 启动消费者接收消息
# 通过环境变量, 告诉客户端程序name server的地址 export NAMESRV_ADDR=localhost:9876 # 启动生产者来测试发送消息 sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer # 启动消费者来测试接收消息 sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
关闭 broker
mqshutdown broker
关闭 nameserver
mqshutdown namesrv
cd /usr/local/rocketmq/
vim conf/broker.conf
末尾添加
brokerIP1=192.168.64.141
关闭 broker 服务
mqshutdown broker
重新使用 broker.conf 配置启动 broker
nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
(57条消息) RocketMQ (一) 安装_扎瓦江石的博客-CSDN博客
在开源项目 rocketmq-dashboard 中提供了rocketmq 的管理界面: 地址为: https://github.com/apache/rocketmq-dashboard
github 在国内访问缓慢, 也可以使用码云的镜像项目, 地址为: https://gitee.com/mirrors_apache/rocketmq-dashboard
打包的 jar 文件在 target 目录, 进入目录执行jar文件
# 进入 target 目录
cd target
# 运行管理界面
nohup java -jar rocketmq-console-ng-1.0.1.jar --server.port=8080 --rocketmq.config.namesrvAddr=localhost:9876 &
http://192.168.64.141:8080
RocketMQ (二) 双主双从同步复制集群方案_wanght笔记-CSDN博客
在管理界面中新建主题Topic1
,这里把写队列和读队列的数量都设置成3。
Topic1 主题,并创建了3写3读队列
perm
参数是设置队列的读写权限,下面表格列出了可配置的值及其含义:
取值 | 含义 |
---|---|
6 | 同时开启读写 |
4 | 禁写 |
2 | 禁读 |
生产者以轮询的方式向所有写队列发送消息,这些队列可能会分布在多个broker实例上。
一个 group 中的多个消费者,可以以负载均衡的方式来接收消息。
读取队列
被均匀分配给这些消费者,它们从指定的队列来接收消息。队列的分配可以采用不同的策略,这里简略介绍以下三种策略:
这是默认策略,它是这样分配队列的:
如果使用环形分配,在消费者的代码中需要设置分配策略,代码如下:
consumer.setAllocateMessageQueueStrategy(new AllocateMessageQueueAveragelyByCircle());
如果使用一致性哈希算法进行分配,在消费者的代码中需要设置分配策略,代码如下:
consumer.setAllocateMessageQueueStrategy(new AllocateMessageQueueConsistentHash());
这种算法依靠一致性哈希算法,看当前消费者可以落到哪个虚拟节点,该虚拟节点对应哪个队列。
添加依赖
org.apache.rocketmq
rocketmq-client
4.7.1
org.apache.rocketmq
rocketmq-store
4.7.1
同步消息发送要保证强一致性,发到master的消息向slave复制后,才会向生产者发送反馈信息。
这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
生产者代码
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.exception.RemotingException;
import java.util.Scanner;
//同步消息
public class Producer {
public static void main(String[] args) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
//1、新建生产者实例
DefaultMQProducer p = new DefaultMQProducer("p1"); //参数为生产者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
p.setNamesrvAddr("192.168.64.141:9876");
//3、启动(连接服务器)
p.start();
//4、发送消息
while (true){
System.out.println("请输入消息");
String s = new Scanner(System.in).nextLine();
//4.1、将要发送的消息封装到Message对象 import org.apache.rocketmq.common.message.Message;
//参数一为注册名字,相当于一级标签,参数二(可加可不加)为二级标签,参数三为需要发送的消息,格式为byte数组
Message msg = new Message("Topic1","Tap1",s.getBytes());
//4.2、会得到一个反馈信息:发送到哪里,是否成功,等等)
SendResult r = p.send(msg);
//打印反馈信息
System.out.println(r);
}
}
}
消费者代码
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
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.message.MessageExt;
import java.util.List;
public class Consumer {
public static void main(String[] args) throws MQClientException {
//1、新建消费者实例
DefaultMQPushConsumer c = new DefaultMQPushConsumer("c1");//参数为消费者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
c.setNamesrvAddr("192.168.64.141:9876");
//3、订阅消息(指定在哪接收消息)
// * - 所有标签
// Tag1 || Tag2 || Tag3 - 多种标签
// Tag1 - 一种标签
c.subscribe("Topic1","Tap1");
//4、设置处理消息的监听器
c.setMessageListener(new MessageListenerConcurrently() { //多个线程并发接收处理消息(同时接收处理多条消息)
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt msg : msgs) {
String s = new String(msg.getBody());
System.out.println("收到消息:" + s);
}
//表示消息处理成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//有异常使用这个返回,告诉服务器重新发送,共计18次。并且请求间隔越来越长,最长间隔2小时。如果18次都处理失败,消息会进入死信队列。
//return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
});
//5、启动(连接服务器)
c.start();
}
}
消息发送到 Rocketmq 服务器后, 延迟一定时间再向消费者进行投递。
比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。
msg.setDelayTimeLevel(3);
级别 | 延时时长 |
---|---|
1 | 1s |
2 | 5s |
3 | 10s |
4 | 30s |
5 | 1m |
6 | 2m |
7 | 3m |
8 | 4m |
9 | 5m |
10 | 6m |
11 | 7m |
12 | 8m |
13 | 9m |
14 | 10m |
15 | 20m |
16 | 30m |
17 | 1h |
18 | 2h |
生产者代码
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.exception.RemotingException;
import java.util.Scanner;
//延迟消息
public class Producer {
public static void main(String[] args) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
//1、新建生产者实例
DefaultMQProducer p = new DefaultMQProducer("p1"); //参数为生产者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
p.setNamesrvAddr("192.168.64.141:9876");
//3、启动(连接服务器)
p.start();
//4、发送消息
while (true){
System.out.println("请输入消息");
String s = new Scanner(System.in).nextLine();
//4.1、将要发送的消息封装到Message对象 import org.apache.rocketmq.common.message.Message;
//参数一为注册名字,相当于一级标签,参数二(可加可不加)为二级标签,参数三为需要发送的消息,格式为byte数组
Message msg = new Message("Topic1","Tap1",s.getBytes());
//4.2、设置延迟级别
msg.setDelayTimeLevel(3);
//4.3、会得到一个反馈信息:发送到哪里,是否成功,等等)
SendResult r = p.send(msg);
//打印反馈信息
System.out.println(r);
}
}
}
消费者代码
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
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.message.MessageExt;
import java.util.List;
public class Consumer {
public static void main(String[] args) throws MQClientException {
//1、新建消费者实例
DefaultMQPushConsumer c = new DefaultMQPushConsumer("c1");//参数为消费者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
c.setNamesrvAddr("192.168.64.141:9876");
//3、订阅消息(指定在哪接收消息)
// * - 所有标签
// Tag1 || Tag2 || Tag3 - 多种标签
// Tag1 - 一种标签
c.subscribe("Topic1","Tap1");
//4、设置处理消息的监听器
c.setMessageListener(new MessageListenerConcurrently() { //多个线程并发接收处理消息(同时接收处理多条消息)
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt msg : msgs) {
String s = new String(msg.getBody());
System.out.println("收到消息:" + s);
}
//表示消息处理成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//有异常使用这个返回,告诉服务器重新发送,共计18次。并且请求间隔越来越长,最长间隔2小时。如果18次都处理失败,消息会进入死信队列。
//return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
});
//5、启动(连接服务器)
c.start();
}
}
上图演示了 Rocketmq 顺序消息的基本原理:
生产者发送消息
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.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.util.List;
import java.util.Scanner;
//顺序消息
public class Producer {
static String[] msgs = {
"15103111039,创建",
"15103111065,创建",
"15103111039,付款",
"15103117235,创建",
"15103111065,付款",
"15103117235,付款",
"15103111065,完成",
"15103111039,推送",
"15103117235,完成",
"15103111039,完成"
};
public static void main(String[] args) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
//1、新建生产者实例
DefaultMQProducer p = new DefaultMQProducer("p1"); //参数为生产者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
p.setNamesrvAddr("192.168.64.141:9876");
//3、启动(连接服务器)
p.start();
//4、遍历数组发送消息
for (String s : msgs){
//4.1获取数组每个元素的id
Long orderId = Long.valueOf(s.split(",")[0]);
//4.2、将要发送的消息封装到Message对象 import org.apache.rocketmq.common.message.Message;
//参数一为注册名字,相当于一级标签,参数二(可加可不加)为二级标签,参数三为需要发送的消息,格式为byte数组
Message msg = new Message("Topic2",s.getBytes());//服务器上没有Topic2,服务器上可以设置,默认可以自动创建
//4.3、会得到一个反馈信息 参数一为要发送的消息,参数二为队列选择器,参数三为选择依据)
SendResult r = p.send(msg,
new MessageQueueSelector() {
@Override //参数一:队列信息,参数二:发送的消息对象,参数三:选择依据
public MessageQueue select(List list, Message message, Object o) {
Long orderId = (Long) o;
int index =(int) (orderId % list.size());
return list.get(index);
}
}, orderId);
System.out.println(r);
}
}
}
消费者接收消息
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class Consumer {
public static void main(String[] args) throws MQClientException {
//1、新建消费者实例
DefaultMQPushConsumer c = new DefaultMQPushConsumer("c2");//参数为消费者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
c.setNamesrvAddr("192.168.64.141:9876");
//3、订阅消息(指定在哪接收消息) //消费者没有设置标签,这里使用*号表示接收所有
c.subscribe("Topic2","*");
//4、设置处理消息的监听器,MessageListenerOrderly监听器可以按顺序处理消息
c.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt msg : list){
String s = new String(msg.getBody());
System.out.println("收到:" + s);
}
return ConsumeOrderlyStatus.SUCCESS ;
}
});
//5、启动
c.start();
}
}
RocketMQ 提供了可靠性消息,也叫事务消息。下面分析一下其原理。
下面来看 RocketMQ 的事务消息是如何来发送“可靠消息”的,只需要以下三步:
完成事务消息发送后,消费者就可以以正常的方式来消费数据。
RocketMQ 的自动重发机制在绝大多数情况下,都可以保证消息被正确消费。
假如消息最终消费失败了,还可以由人工处理进行托底。
上面分析的是正常情况下的执行流程。下面再来看两种错误情况:
回滚:
消息回查:
生产者发送消息
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.util.Scanner;
//事务消息
public class Producer {
public static void main(String[] args) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
//1、新建生产者实例
TransactionMQProducer p = new TransactionMQProducer("p3"); //参数为生产者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
p.setNamesrvAddr("192.168.64.141:9876");
//3、创建事务监听器
p.setTransactionListener(new TransactionListener() {
//3.1、执行本地事务
@Override //参数一为消息,参数二为业务数据参数
public LocalTransactionState executeLocalTransaction(Message message, Object o) {
System.out.println("半消息发送后,执行本地事务 ;业务数据参数为:" + o);
if (true){ //测试未知状态
return LocalTransactionState.UNKNOW;
}
//返回状态,要么成功要么失败
if (Math.random() < 0.5){
System.out.println("本地事务执行成功,提交消息");
//成功
return LocalTransactionState.COMMIT_MESSAGE;
}else {
System.out.println("本地事务执行失败,回滚消息");
//失败
return LocalTransactionState.ROLLBACK_MESSAGE;
}
//表示是未知状态(实际业务中不可能出现这么模糊状态)
//return LocalTransactionState.UNKNOW;
}
//3.2、处理Rocketmq服务器的事务回查操作
@Override
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
//可能原因
//1、由于网络中断,上面事务无法通知服务器
//2、上面的本地事务执行缓慢,超过一分钟
System.out.println("Rocketmq服务器正在回查事务状态");
//模拟网络中断,无法得到事务状态
return LocalTransactionState.ROLLBACK_MESSAGE;
}
});
//4、启动(连接服务器)
p.start();
//5、发送事务消息(先发送半消息),会触发一个监听器执行本地事务
while (true){
System.out.println("输入消息:");
String s = new Scanner(System.in).nextLine();
Message msg = new Message("Topic3",s.getBytes());
//参数二为传递给监听器的业务数据参数
p.sendMessageInTransaction(msg,"传递业务数据参数");
}
}
}
消费者接收消息
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
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.message.MessageExt;
import java.util.List;
public class Consumer {
public static void main(String[] args) throws MQClientException {
//1、新建消费者实例
DefaultMQPushConsumer c = new DefaultMQPushConsumer("c1");//参数为消费者组名字,无所谓,都可以
//2、设置name server(注册中心)的地址
c.setNamesrvAddr("192.168.64.141:9876");
//3、订阅消息(指定在哪接收消息)
c.subscribe("Topic3","Tap1");
//4、设置处理消息的监听器
c.setMessageListener(new MessageListenerConcurrently() { //多个线程并发接收处理消息(同时接收处理多条消息)
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt msg : msgs) {
String s = new String(msg.getBody());
System.out.println("收到消息:" + s);
}
//表示消息处理成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//有异常使用这个返回,告诉服务器重新发送,共计18次。并且请求间隔越来越长,最长间隔2小时。如果18次都处理失败,消息会进入死信队列。
//return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
});
//5、启动(连接服务器)
c.start();
}
}
master 收到消息后立即向生产者进行反馈。之后再以异步方式向 slave 复制消息。
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。