RocketMQ视频笔记第三章RocketMQ集成SpringBoot(动力节点)

22. Rocketmq集成SpringBoot

动力节点双哥最新RocketMQ视频

22.1 搭建rocketmq-producer(消息生产者)


22.1.1 创建项目,完整的pom.xml

| <?_xml version="1.0" encoding="UTF-8"_?>
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"

4.0.0

org.springframework.boot
spring-boot-starter-parent
2.6.3

_

com.powernode
01-rocketmq-producer
0.0.1-SNAPSHOT
rocketmq-producer
Demo project for Spring Boot

<java.version>1.8</java.version>



org.springframework.boot
spring-boot-starter-web
** **
** _**
** **
**org.apache.rocketmq **
**rocketmq-spring-boot-starter 2.0.2 **
** **

org.projectlombok
lombok
true


org.springframework.boot
spring-boot-starter-test
test





org.springframework.boot
spring-boot-maven-plugin



org.projectlombok
lombok





22.1.2 修改配置文件application.yml

| spring: application:
name: rocketmq-producerrocketmq:
name-server: 127.0.0.1:9876

rocketMq的nameServer地址

producer:
group: powernode-group

生产者组别

send-message-timeout: 3000

消息发送的超时时间

retry-times-when-send-async-failed: 2

异步消息发送失败重试次数

max-message-size: 4194304

# 消息的最大长度

22.1.3 我们在测试类里面测试发送消息

往powernode主题里面发送一个简单的字符串消息

| /** * 注入rocketMQTemplate,我们使用它来操作mq /
@Autowiredprivate RocketMQTemplate rocketMQTemplate;
/
* * 测试发送简单的消息 * * @throws Exception */
@Testpublic void testSimpleMsg() throws Exception {
// 往powernode的主题里面发送一个简单的字符串消息
SendResult sendResult = rocketMQTemplate.syncSend(“powernode”, “我是一个简单的消息”);
// 拿到消息的发送状态
System.out.println(sendResult.getSendStatus());
// 拿到消息的id
System.out.println(sendResult.getMsgId());
}
|
| — |

运行后查看控制台

22.1.4 查看rocketMq的控制台


查看消息细节

22.2 搭建rocketmq-consumer(消息消费者)


22.2.1 创建项目,完整的pom.xml

| <?_xml version="1.0" encoding="UTF-8"_?>
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"

4.0.0

org.springframework.boot
spring-boot-starter-parent ** **
2.6.3

_

com.powernode
02-rocketmq-consumer
0.0.1-SNAPSHOT
rocketmq-consumer
Demo project for Spring Boot

<java.version>1.8</java.version>



org.springframework.boot
spring-boot-starter-web
** **
** **
**
_ **
**org.apache.rocketmq **
rocketmq-spring-boot-starter
** 2.0.2 **


org.projectlombok
lombok
true


org.springframework.boot
spring-boot-starter-test
test





org.springframework.boot
spring-boot-maven-plugin



org.projectlombok
lombok





22.2.2 修改配置文件application.yml

| spring:
application:
name: rocketmq-consumerrocketmq:

name-server: 127.0.0.1:9876

22.2.3 添加一个监听的类SimpleMsgListener

消费者要消费消息,就添加一个监听

| package com.powernode.listener;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
_/** * 创建一个简单消息的监听 * _
_1.类上添加注解@Component和@RocketMQMessageListener * @RocketMQMessageListener(topic = “powernode”, consumerGroup = “powernode-group”) * topic指定消费的主题,consumerGroup指定消费组,一个主题可以有多个消费者组,一个消息可以被多个不同的组的消费者都消费 * _
2.实现RocketMQListener接口,注意泛型的使用,可以为具体的类型,如果想拿到消息 * 的其他参数可以写成MessageExt */
@Component@RocketMQMessageListener(topic = “powernode”, consumerGroup = “powernode-group”,messageModel = MessageModel.CLUSTERING)public class SimpleMsgListener implements RocketMQListener {
_/** * 消费消息的方法 * * @param message */ _
@Override public void onMessage(String message) { System.out.println(message);

}}

22.2.4 启动rocketmq-consumer

查看控制台,发现我们已经监听到消息了

23. RocketMQ发送对象消息和集合消息

我们接着在上面项目里面做

23.1 发送对象消息

主要是监听的时候泛型中写对象的类型即可

23.1.1 修改rocketmq-producer添加一个Order类

| package com.powernode.domain;
import lombok.AllArgsConstructor;import lombok.Data;
import lombok.NoArgsConstructor; import java.util.Date;
/** * 订单对象 /
@Data@AllArgsConstructor@NoArgsConstructorpublic class Order {
/
* * 订单号 /
private String orderId;
/
* * 订单名称 /
private String orderName;
/
* * 订单价格 /
private Double price;
/
* * 订单号创建时间 /
private Date createTime;
/
* * 订单描述 */
private String desc;

}

23.1.2 修改rocketmq-producer添加一个单元测试

| /** * 测试发送对象消息 * * @throws Exception */
@Testpublic void testObjectMsg() throws Exception {
Order order = new Order();
order.setOrderId(UUID.randomUUID().toString());
order.setOrderName(“我的订单”);
order.setPrice(998D);
order.setCreateTime(new Date());
order.setDesc(“加急配送”);
_// 往powernode-obj主题发送一个订单对象 _
rocketMQTemplate.syncSend(“powernode-obj”, order);

}

23.1.3 发送此消息

23.1.4 修改rocketmq-consumer也添加一个Order类(拷贝过来)

23.1.5 修改rocketmq-consumer添加一个ObjMsgListener

| package com.powernode.listener; import com.powernode.domain.Order;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/** * 创建一个对象消息的监听 * 1.类上添加注解@Component和@RocketMQMessageListener * 2.实现RocketMQListener接口,注意泛型的使用 /
@Component@RocketMQMessageListener(topic = “powernode-obj”, consumerGroup = “powernode-obj-group”)
public class ObjMsgListener implements RocketMQListener<Order> {
/
* * 消费消息的方法 * * @param message */
@Override public void onMessage(**Order **message) {
System.out.println(message);

}}

23.1.6 重启rocketmq-consumer后查看控制台

对象消息已经监听到了

23.2 发送集合消息

和对象消息同理,创建一个Order的集合,发送出去,监听方注意修改泛型中的类型为Object即可,这里就不做重复演示了

24. RocketMQ集成SpringBoot发送不同消息模式

24.1 发送同步消息

理解为:消息由消费者发送到broker后,会得到一个确认,是具有可靠性的
这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知等。
我们在上面的快速入门中演示的消息,就是同步消息,即
rocketMQTemplate.syncSend()
rocketMQTemplate.send()
rocketMQTemplate.convertAndSend()
这三种发送消息的方法,底层都是调用syncSend,发送的是同步消息

24.2 发送异步消息

rocketMQTemplate.asyncSend()

24.2.1 修改rocketmq-producer添加一个单元测试

| /** * 测试异步发送消息 * * @throws Exception /
@Testpublic void testAsyncSend() throws Exception {
// 发送异步消息,发送完以后会有一个异步通知
rocketMQTemplate.asyncSend(“powernode”, “发送一个异步消息”, new SendCallback() {
/
* * 成功的回调 * @param sendResult /
@Override public void onSuccess(SendResult sendResult) {
System.out.println(“发送成功”);
}
/
* * 失败的回调 * @param throwable */
@Override public void onException(Throwable throwable) {
System.out.println(“发送失败”);
} });
// 测试一下异步的效果
System.out.println(“谁先执行”);
// 挂起jvm 不让方法结束
System.in.read();

}

24.2.2 运行查看控制台效果

谁先发送打印在前面

24.3 发送单向消息

这种方式主要用在不关心发送结果的场景,这种方式吞吐量很大,但是存在消息丢失的风险,例如日志信息的发送

24.3.1 修改rocketmq-producer添加一个单元测试

| /** * 测试单向消息 * * @throws Exception */
@Testpublic void testOnWay() throws Exception {
// 发送单向消息,没有返回值和结果 rocketMQTemplate.sendOneWay(“powernode”, “这是一个单向消息”);

}

24.4 发送延迟消息

24.4.1 修改rocketmq-producer添加一个单元测试

| /** * 测试延迟消息 * * @throws Exception */
@Testpublic void testDelay() throws Exception {
// 构建消息对象
Message message = MessageBuilder.withPayload(“我是一个延迟消息”).build();
// 发送一个延时消息,延迟等级为4级,也就是30s后被监听消费
SendResult sendResult = rocketMQTemplate.syncSend(“powernode”, message, 2000, 4);
System.out.println(sendResult.getSendStatus());

}

24.4.2 运行后,查看消费者端,过了30s才被消费

这里注意的是RocketMQ不支持任意时间的延时
只支持以下几个固定的延时等级,等级1就对应1s,以此类推,最高支持2h延迟
private String messageDelayLevel = “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”;

24.5 发送顺序消息

24.5.1 修改Order表添加一个顺序字段

/** * 订单的流程顺序 */private Integer seq;

24.5.2 修改rocketmq-producer添加一个单元测试

| /** * 测试顺序消费 * mq会根据hash的值来存放到一个队列里面去 * * @throws Exception */
@Testpublic void testOrderly() throws Exception {
List orders = Arrays.asList(
new Order(UUID.randomUUID().toString().substring(0, 5), “张三的下订单”, null, null, null, 1),
new Order(UUID.randomUUID().toString().substring(0, 5), “张三的发短信”, null, null, null, 1),
new Order(UUID.randomUUID().toString().substring(0, 5), “张三的物流”, null, null, null, 1),
new Order(UUID.randomUUID().toString().substring(0, 5), “张三的签收”, null, null, null, 1),
new Order(UUID.randomUUID().toString().substring(0, 5), “李四的下订单”, null, null, null, 2),
new Order(UUID.randomUUID().toString().substring(0, 5), “李四的发短信”, null, null, null, 2),
new Order(UUID.randomUUID().toString().substring(0, 5), “李四的物流”, null, null, null, 2),
new Order(UUID.randomUUID().toString().substring(0, 5), “李四的签收”, null, null, null, 2) );
// 我们控制流程为 下订单->发短信->物流->签收 hash的值为seq,也就是说 seq相同的会放在同一个队列里面,顺序消费
orders.forEach(order -> {
rocketMQTemplate.syncSendOrderly(“powernode-obj”, order, String.valueOf(order.getSeq()));
});

}

24.5.3 发送消息

24.5.4 修改rocketmq-consumer的ObjMsgListener

| package com.powernode.listener; import com.powernode.domain.Order;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/** * @Author 武汉动力节点 * 创建一个对象消息的监听 *
1.类上添加注解@Component和@RocketMQMessageListener *
2.实现RocketMQListener接口,注意泛型的使用 * consumeMode 指定消费类型 * CONCURRENTLY 并发消费 * ORDERLY 顺序消费 messages orderly. one queue, one thread /
@Component@RocketMQMessageListener(topic = “powernode-obj”,
consumerGroup = “powernode-obj-group”,
consumeMode = ConsumeMode.ORDERLY)public class ObjMsgListener implements RocketMQListener {
/
* * 消费消息的方法 * * @param message */
@Override public void onMessage(Order message) {
System.out.println(message);

}}

24.5.5 重启rocketmq-consumer

查看控制台,消息按照我们的放入顺序进行消费了

24.6 发送事务消息

24.6.1 修改rocketmq-producer添加一个单元测试

| /** * 测试事务消息 * 默认是sync(同步的) * 事务消息会有确认和回查机制 * 事务消息都会走到同一个监听回调里面,所以我们需要使用tag或者key来区分过滤 * * @throws Exception */
@Testpublic void testTrans() throws Exception {
// 构建消息体
Message message = MessageBuilder.withPayload(“这是一个事务消息”).build();
// 发送事务消息(同步的) 最后一个参数才是消息主题
TransactionSendResult transaction =
rocketMQTemplate.sendMessageInTransaction(“powernode”, message, “消息的参数”);
// 拿到本地事务状态
System.out.println(transaction.getLocalTransactionState());
// 挂起jvm,因为事务的回查需要一些时间
System.in.read();

}

24.6.2 修改rocketmq-producer添加一个本地事务消息的监听(半消息)

| /** * 事务消息的监听与回查 * 类上添加注解@RocketMQTransactionListener 表示这个类是本地事务消息的监听类 * 实现RocketMQLocalTransactionListener接口 * 两个方法为执行本地事务,与回查本地事务 */
@Component@RocketMQTransactionListener(corePoolSize = 4,maximumPoolSize = 8)
public class TmMsgListener implements RocketMQLocalTransactionListener {
_/** _
_* 执行本地事务,这里可以执行一些业务 _
_* 比如操作数据库,操作成功就return RocketMQLocalTransactionState.COMMIT; _
_* 可以使用try catch来控制成功或者失败; _
_* @param msg * @param arg * @return / _
@Override public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
_// 拿到消息参数 _
System.out.println(arg);
_// 拿到消息头 _
System.out.println(msg.getHeaders());
_// 返回状态COMMIT,UNKNOWN _
return RocketMQLocalTransactionState.UNKNOWN;
}
_/
* _
_* 回查本地事务,只有上面的执行方法返回UNKNOWN时,才执行下面的方法 默认是1min回查 _
_* 此方法为回查方法,执行需要等待一会 _
_* xxx.isSuccess() 这里可以执行一些检查的方法 _
* 如果返回COMMIT,那么本地事务就算是提交成功了,消息就会被消费者看到 *
_ * @param msg * @return */ _
@Override public RocketMQLocalTransactionState
checkLocalTransaction(Message msg) {
System.out.println(msg);
return RocketMQLocalTransactionState.COMMIT;

}}

24.6.3 测试发送事务,建议断点启动

  1. 消息会先到事务监听类的执行方法,
  2. 如果返回状态为COMMIT,则消费者可以直接监听到
  3. 如果返回状态为ROLLBACK,则消息发送失败,直接回滚
  4. 如果返回状态为UNKNOW,则过一会会走回查方法
  5. 如果回查方法返回状态为UNKNOW或者ROLLBACK,则消息发送失败,直接回滚
  6. 如果回查方法返回状态为COMMIT,则消费者可以直接监听到

25. RocketMQ集成SpringBoot的消息过滤

25.1 tag过滤(常在消费者端过滤)

我们从源码注释得知,tag带在主题后面用:来携带,感谢注释

我们往下去看源码,在
org.apache.rocketmq.spring.support.RocketMQUtil 的getAndWrapMessage方法里面看到了具体细节,我们也知道了keys在消息头里面携带

25.1.1 修改rocketmq-producer添加一个单元测试

| /** * 发送一个带tag的消息 * * @throws Exception */
@Testpublic void testTagMsg() throws Exception {
// 发送一个tag为java的数据
rocketMQTemplate.syncSend(“powernode-tag:java”, “我是一个带tag的消息”);

}

25.1.2 发送消息

25.1.3 修改rocketmq-consumer添加一个TagMsgListener

| package com.powernode.listener;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.annotation.SelectorType;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/** * @Author 武汉动力节点 * 创建一个简单的标签消息的监听

  • 1.类上添加注解@Component和@RocketMQMessageListener * selectorType = SelectorType.TAG, 指定使用tag过滤。(也可以使用sql92 需要在配置文件broker.conf中开启enbalePropertyFilter=true) * selectorExpression = “java” 表达式,默认是*,支持"tag1 || tag2 || tag3"
  • 2.实现RocketMQListener接口,注意泛型的使用 /
    @Component@RocketMQMessageListener(topic = “powernode-tag”,
    consumerGroup = “powernode-tag-group”,
    selectorType = SelectorType.TAG,
    selectorExpression = “java”)
    public class TagMsgListener implements RocketMQListener {
    /
    * * 消费消息的方法 *
  • @param message */
    @Override public void onMessage(String message) {
    System.out.println(message);
    }} |
    | — |

25.1.4 重启rocketmq-consumer查看控制台

25.2 Key过滤(可以在事务监听的类里面区分)

25.2.1 修改rocketmq-producer添加一个单元测试

| /** * 发送一个带key的消息,我们使用事务消息 打断点查看消息头 * * @throws Exception */
@Testpublic void testKeyMsg() throws Exception {
// 发送一个key为spring的事务消息
Message message = MessageBuilder.withPayload(“我是一个带key的消息”) .setHeader(RocketMQHeaders.KEYS, “spring”)
.build();
rocketMQTemplate.sendMessageInTransaction(“powernode”, message, “我是一个带key的消息”);

}

25.2.2 断点发送这个消息,查看事务里面消息头


我们在mq的控制台也可以看到

26. RocketMQ集成SpringBoot消息消费两种模式

Rocketmq消息消费的模式分为两种:负载均衡模式和广播模式
负载均衡模式表示多个消费者交替消费同一个主题里面的消息
广播模式表示每个每个消费者都消费一遍订阅的主题的消息

26.1 再搭建一个消费者rocketmq-consumer-b,依赖和配置文件和rocketmq-consumer一致,记住端口修改一下,避免占用

26.2 rocketmq-consumer-b添加一个监听

| package com.powernode.listener;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/** * messageModel 指定消息消费的模式 * CLUSTERING 为负载均衡模式 * BROADCASTING 为广播模式 /
@Component@RocketMQMessageListener(topic = “powernode”,
consumerGroup = “powernode-group”,
* **
messageModel = MessageModel.CLUSTERING)
public class ConsumerBListener
implements RocketMQListener {
@Override public void onMessage(String message) {
System.out.println(message);

}}

26.3 修改rocketmq-consumer的SimpleMsgListener

| /** * 创建一个简单消息的监听 * 1.类上添加注解@Component和@RocketMQMessageListener * * @RocketMQMessageListener(topic = “powernode”, consumerGroup = “powernode-group”) * topic指定消费的主题,consumerGroup指定消费组,一个主题可以有多个消费者组,一个消息可以被多个不同的组的消费者都消费 * 2.实现RocketMQListener接口,注意泛型的使用 */
@Component@RocketMQMessageListener(topic = “powernode”,
consumerGroup = “powernode-group”, ** **
messageModel = MessageModel.CLUSTERING)
public class SimpleMsgListener
implements RocketMQListener {
@Override public void onMessage(String message) {
System.out.println(new Date());
System.out.println(message);

}}

26.4 启动两个消费者

26.5 在生产者里面添加一个单元测试并且运行

| /** * 测试消息消费的模式 * * @throws Exception */
@Testpublic void testMsgModel() throws Exception {
for (int i = 0; i < 10; i++) {
rocketMQTemplate.syncSend(“powernode”, “我是消息” + i);

}}

26.6 查看两个消费者的控制台,发现是负载均衡的模式


26.7 修改两个消费者的模式为BROADCASTING

重启测试,结果是广播模式,每个消费者都消费了这些消息

项目中 一般部署多态机器 消费者 2 - 3 根据业务可以选择具体的模式来配置

重置消费点位, 将一个组的消费节点 设置在之前的某一个时间点上去 从这个时间点开始往后消费


跳过堆积 选择一个组 跳过堆积以后 这个组里面的的所有都不会被消费了

27. 如何解决消息堆积问题?

一般认为单条队列消息差值>=10w时 算堆积问题

27.1 什么情况下会出现堆积

  1. 生产太快了
    生产方可以做业务限流
    增加消费者数量,但是消费者数量<=队列数量,适当的设置最大的消费线程数量(根据IO(2n)/CPU(n+1))
    动态扩容队列数量,从而增加消费者数量
  2. 消费者消费出现问题
    排查消费者程序的问题

28. 如何确保消息不丢失?

  1. 生产者使用同步发送模式 ,收到mq的返回确认以后 顺便往自己的数据库里面写
    msgId status(0) time
  2. 消费者消费以后 修改数据这条消息的状态 = 1
  3. 写一个定时任务 间隔两天去查询数据 如果有status = 0 and time < day-2
  4. 将mq的刷盘机制设置为同步刷盘
  5. 使用集群模式 ,搞主备模式,将消息持久化在不同的硬件上
  6. 可以开启mq的trace机制,消息跟踪机制

1.在broker.conf中开启消息追踪
traceTopicEnable=true
2.重启broker即可
3.生产者配置文件开启消息轨迹
enable-msg-trace: true
4. 消费者开启消息轨迹功能,可以给单独的某一个消费者开启
enableMsgTrace = true

在rocketmq的面板中可以查看消息轨迹
默认会将消息轨迹的数据存在 RMQ_SYS_TRACE_TOPIC 主题里面

29. 安全

  1. 开启acl的控制 在broker.conf中开启aclEnable=true
  2. 配置账号密码 修改plain_acl.yml
  3. 修改控制面板的配置文件 放开52/53行 把49行改为true 上传到服务器的jar包平级目录下即可

猜你喜欢

转载自blog.csdn.net/Javanewspaper/article/details/130642290