@llryiop/avatar-boot-cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/exam-question-generate-api.md +163 -0
- package/package.json +1 -1
- package/src/prompts.js +3 -3
- package/src/transform.js +1 -1
- package/templates/.claude/skills/avatar-boot-starter-feign/README.md +243 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/SKILL.md +47 -219
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/212/237/350/203/275/350/257/246/350/247/243.md +65 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//351/205/215/347/275/256/345/217/202/350/200/203.md +70 -0
- package/templates/.claude/skills/avatar-boot-starter-job/README.md +437 -0
- package/templates/.claude/skills/avatar-boot-starter-job/SKILL.md +35 -414
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/270/270/350/247/201/351/227/256/351/242/230.md +55 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +124 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//347/233/221/346/216/247/346/214/207/346/240/207.md +72 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/README.md +580 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/SKILL.md +36 -560
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/234/200/344/275/263/345/256/236/350/267/265.md +43 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/240/270/345/277/203/345/212/237/350/203/275.md +117 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//351/205/215/347/275/256/345/217/202/350/200/203.md +54 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/README.md +572 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/SKILL.md +40 -550
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/256/236/344/275/223/344/270/216/345/212/237/350/203/275.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/346/225/260/346/215/256/346/272/220.md +91 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//351/253/230/347/272/247/347/211/271/346/200/247/344/270/216/351/205/215/347/275/256.md +59 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/README.md +901 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/SKILL.md +40 -879
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/212/237/350/203/275/344/275/277/347/224/250.md +134 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//346/225/205/351/232/234/346/216/222/346/237/245.md +64 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/README.md +594 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/SKILL.md +52 -570
- package/templates/.claude/skills/avatar-boot-starter-oss/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//346/240/270/345/277/203/345/212/237/350/203/275.md +94 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//350/247/204/350/214/203/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +61 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/README.md +586 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/SKILL.md +42 -566
- package/templates/.claude/skills/avatar-boot-starter-redis/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +78 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//346/225/260/346/215/256/346/223/215/344/275/234.md +111 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//351/253/230/347/272/247/345/212/237/350/203/275.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/README.md +662 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/SKILL.md +48 -640
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//346/240/270/345/277/203/345/212/237/350/203/275.md +101 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/205/215/347/275/256/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +44 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/253/230/347/272/247/347/211/271/346/200/247.md +71 -0
- package/templates/.claude/skills/avatar-boot-starter-web/README.md +1007 -0
- package/templates/.claude/skills/avatar-boot-starter-web/SKILL.md +150 -1003
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-LogInfo/346/263/250/350/247/243.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/345/205/250/345/261/200/345/274/202/345/270/270/345/244/204/347/220/206.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/214/207/346/240/207/347/233/221/346/216/247.md +74 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/227/245/345/277/227/344/275/223/347/263/273.md +73 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/350/257/267/346/261/202/344/270/212/344/270/213/346/226/207.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +52 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//346/263/250/346/204/217/344/272/213/351/241/271.md +68 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//350/207/252/345/256/232/344/271/211/346/211/251/345/261/225/346/214/207/345/215/227.md +107 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//351/205/215/347/275/256/345/217/202/350/200/203.md +107 -0
- package/templates/.claude/skills/crud-generator/SKILL.md +133 -64
- package/templates/.claude/skills/database-design/README.md +207 -0
- package/templates/.claude/skills/database-design/SKILL.md +469 -82
- package/templates/.claude/skills/database-design/references//345/221/275/345/220/215/350/247/204/350/214/203.md +232 -0
- package/templates/.claude/skills/database-design/references//345/255/227/346/256/265/347/261/273/345/236/213/350/247/204/350/214/203.md +400 -0
- package/templates/.claude/skills/database-design/references//347/264/242/345/274/225/350/247/204/350/214/203.md +506 -0
- package/templates/avatar-scaffold-api/pom.xml +0 -5
- package/templates/avatar-scaffold-service/pom.xml +25 -87
- package/templates/avatar-scaffold-service/src/main/resources/application-dev.yaml +3 -5
- package/templates/avatar-scaffold-service/src/main/resources/application-local.yaml +2 -2
- package/templates/pom.xml +9 -18
|
@@ -1,662 +1,70 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: avatar-boot-starter-rocketmq
|
|
3
|
-
description:
|
|
3
|
+
description: Avatar Boot RocketMQ 模块使用指南。当用户询问 RocketMQ 消息发送消费、延迟消息、消息幂等性、消息可靠性投递、Tag过滤,或需要接入 avatar-boot-starter-rocketmq 时触发。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Avatar Boot
|
|
6
|
+
# Avatar Boot Starter RocketMQ 使用指南
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
你是 Avatar Boot RocketMQ 模块的使用顾问与开发助手。
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
- ✅ **注解驱动** - 使用 @RocketMQMessageListener 注解快速定义消费者
|
|
12
|
-
- ✅ **延迟消息** - 支持 18 个等级的延迟消息投递
|
|
13
|
-
- ✅ **事务消息** - 支持分布式事务消息,保证本地事务与消息发送的一致性
|
|
14
|
-
- ✅ **顺序消息** - 支持分区有序和全局有序消息消费
|
|
15
|
-
- ✅ **Tag 过滤** - 支持基于 Tag 的消息过滤,灵活路由消息
|
|
16
|
-
- ✅ **重试机制** - 内置消费重试策略,支持自定义重试次数
|
|
10
|
+
## 交互流程(必须遵守)
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
**每次被触发时,先通过 AskUserQuestion 工具询问用户意图:**
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
问题:"您好!我是 Avatar Boot RocketMQ 模块助手,请问您需要哪方面的帮助?"
|
|
15
|
+
选项:
|
|
16
|
+
1. **快速接入** - 添加依赖、配置 NameServer、发送/消费消息
|
|
17
|
+
2. **消息发送** - 同步/异步/单向/延迟消息
|
|
18
|
+
3. **消息消费** - @RocketMQMessageListener、Tag过滤、顺序消费
|
|
19
|
+
4. **消息幂等性** - 基于 Redis 的消息去重(可选)
|
|
20
|
+
5. **消息可靠性** - 本地文件存储 + 自动补偿投递(可选)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**根据用户选择,用 Read 工具按下方「文档读取路由」加载对应文档,然后给出具体指导。**
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
<dependency>
|
|
26
|
-
<groupId>com.iflytek.avatar.boot</groupId>
|
|
27
|
-
<artifactId>avatar-boot-starter-rocketmq</artifactId>
|
|
28
|
-
</dependency>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
> 版本由 Avatar Boot BOM 统一管理,无需指定 version。内置 rocketmq-spring-boot-starter 2.3.1。
|
|
32
|
-
|
|
33
|
-
### 2. 配置文件
|
|
34
|
-
|
|
35
|
-
在 `application.yml` 中添加 RocketMQ 配置:
|
|
36
|
-
|
|
37
|
-
```yaml
|
|
38
|
-
rocketmq:
|
|
39
|
-
name-server: localhost:9876 # NameServer 地址
|
|
40
|
-
producer:
|
|
41
|
-
group: ${spring.application.name}-producer # 生产者组名
|
|
42
|
-
send-message-timeout: 3000 # 发送超时时间(毫秒)
|
|
43
|
-
retry-times-when-send-failed: 2 # 同步发送失败重试次数
|
|
44
|
-
retry-times-when-send-async-failed: 2 # 异步发送失败重试次数
|
|
45
|
-
max-message-size: 4194304 # 最大消息大小(4MB)
|
|
46
|
-
compress-message-body-threshold: 4096 # 消息压缩阈值(4KB)
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 3. 发送第一条消息
|
|
50
|
-
|
|
51
|
-
```java
|
|
52
|
-
package com.example.producer;
|
|
53
|
-
|
|
54
|
-
import lombok.RequiredArgsConstructor;
|
|
55
|
-
import lombok.extern.slf4j.Slf4j;
|
|
56
|
-
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
|
57
|
-
import org.springframework.stereotype.Service;
|
|
58
|
-
|
|
59
|
-
@Slf4j
|
|
60
|
-
@Service
|
|
61
|
-
@RequiredArgsConstructor
|
|
62
|
-
public class SimpleProducer {
|
|
63
|
-
|
|
64
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
65
|
-
|
|
66
|
-
public void sendMessage(String topic, String message) {
|
|
67
|
-
rocketMQTemplate.convertAndSend(topic, message);
|
|
68
|
-
log.info("消息发送成功: topic={}, message={}", topic, message);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 4. 消费第一条消息
|
|
74
|
-
|
|
75
|
-
```java
|
|
76
|
-
package com.example.consumer;
|
|
77
|
-
|
|
78
|
-
import lombok.extern.slf4j.Slf4j;
|
|
79
|
-
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
|
80
|
-
import org.apache.rocketmq.spring.core.RocketMQListener;
|
|
81
|
-
import org.springframework.stereotype.Component;
|
|
82
|
-
|
|
83
|
-
@Slf4j
|
|
84
|
-
@Component
|
|
85
|
-
@RocketMQMessageListener(
|
|
86
|
-
topic = "test-topic",
|
|
87
|
-
consumerGroup = "test-consumer-group"
|
|
88
|
-
)
|
|
89
|
-
public class SimpleConsumer implements RocketMQListener<String> {
|
|
90
|
-
|
|
91
|
-
@Override
|
|
92
|
-
public void onMessage(String message) {
|
|
93
|
-
log.info("收到消息: {}", message);
|
|
94
|
-
// 处理业务逻辑
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## 生产者详解
|
|
100
|
-
|
|
101
|
-
### 同步发送
|
|
102
|
-
|
|
103
|
-
```java
|
|
104
|
-
@Service
|
|
105
|
-
@RequiredArgsConstructor
|
|
106
|
-
public class OrderProducer {
|
|
107
|
-
|
|
108
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 同步发送(等待 Broker 确认)
|
|
112
|
-
*/
|
|
113
|
-
public void syncSend(OrderEvent event) {
|
|
114
|
-
SendResult result = rocketMQTemplate.syncSend("order-topic", event);
|
|
115
|
-
log.info("同步发送结果: msgId={}, status={}", result.getMsgId(), result.getSendStatus());
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 同步发送(带 Tag)
|
|
120
|
-
*/
|
|
121
|
-
public void syncSendWithTag(OrderEvent event, String tag) {
|
|
122
|
-
// destination 格式: topic:tag
|
|
123
|
-
SendResult result = rocketMQTemplate.syncSend("order-topic:" + tag, event);
|
|
124
|
-
log.info("发送结果: msgId={}, tag={}", result.getMsgId(), tag);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 同步发送(带超时和延迟级别)
|
|
129
|
-
*/
|
|
130
|
-
public void syncSendWithTimeout(OrderEvent event) {
|
|
131
|
-
SendResult result = rocketMQTemplate.syncSend("order-topic",
|
|
132
|
-
MessageBuilder.withPayload(event).build(),
|
|
133
|
-
3000); // 超时 3 秒
|
|
134
|
-
log.info("发送结果: {}", result);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 异步发送
|
|
140
|
-
|
|
141
|
-
```java
|
|
142
|
-
@Service
|
|
143
|
-
@RequiredArgsConstructor
|
|
144
|
-
public class AsyncProducer {
|
|
145
|
-
|
|
146
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 异步发送(不阻塞当前线程)
|
|
150
|
-
*/
|
|
151
|
-
public void asyncSend(OrderEvent event) {
|
|
152
|
-
rocketMQTemplate.asyncSend("order-topic", event, new SendCallback() {
|
|
153
|
-
@Override
|
|
154
|
-
public void onSuccess(SendResult sendResult) {
|
|
155
|
-
log.info("异步发送成功: msgId={}", sendResult.getMsgId());
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@Override
|
|
159
|
-
public void onException(Throwable e) {
|
|
160
|
-
log.error("异步发送失败: event={}", event, e);
|
|
161
|
-
// 补偿处理:入库重试、告警
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 单向发送(不需要确认,最快但不保证送达)
|
|
168
|
-
*/
|
|
169
|
-
public void onewaySend(LogEvent event) {
|
|
170
|
-
rocketMQTemplate.sendOneWay("log-topic", event);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### 批量发送
|
|
176
|
-
|
|
177
|
-
```java
|
|
178
|
-
@Service
|
|
179
|
-
@RequiredArgsConstructor
|
|
180
|
-
public class BatchProducer {
|
|
181
|
-
|
|
182
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* 批量发送消息
|
|
186
|
-
*/
|
|
187
|
-
public void batchSend(List<OrderEvent> events) {
|
|
188
|
-
List<Message<OrderEvent>> messages = events.stream()
|
|
189
|
-
.map(event -> MessageBuilder.withPayload(event).build())
|
|
190
|
-
.collect(Collectors.toList());
|
|
191
|
-
|
|
192
|
-
SendResult result = rocketMQTemplate.syncSend("order-topic", messages, 5000);
|
|
193
|
-
log.info("批量发送结果: {}", result);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## 消费者详解
|
|
199
|
-
|
|
200
|
-
### 基本消费
|
|
201
|
-
|
|
202
|
-
```java
|
|
203
|
-
@Slf4j
|
|
204
|
-
@Component
|
|
205
|
-
@RocketMQMessageListener(
|
|
206
|
-
topic = "order-topic",
|
|
207
|
-
consumerGroup = "order-consumer-group",
|
|
208
|
-
consumeMode = ConsumeMode.CONCURRENTLY, // 并发消费(默认)
|
|
209
|
-
messageModel = MessageModel.CLUSTERING, // 集群模式(默认)
|
|
210
|
-
consumeThreadNumber = 20 // 消费线程数
|
|
211
|
-
)
|
|
212
|
-
public class OrderConsumer implements RocketMQListener<OrderEvent> {
|
|
213
|
-
|
|
214
|
-
@Override
|
|
215
|
-
public void onMessage(OrderEvent event) {
|
|
216
|
-
log.info("收到订单事件: orderNo={}", event.getOrderNo());
|
|
217
|
-
// 处理业务逻辑
|
|
218
|
-
// 抛出异常会触发重试
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### 带 Tag 过滤的消费
|
|
224
|
-
|
|
225
|
-
```java
|
|
226
|
-
@Slf4j
|
|
227
|
-
@Component
|
|
228
|
-
@RocketMQMessageListener(
|
|
229
|
-
topic = "order-topic",
|
|
230
|
-
consumerGroup = "order-create-group",
|
|
231
|
-
selectorType = SelectorType.TAG,
|
|
232
|
-
selectorExpression = "CREATE || UPDATE" // 只消费 CREATE 和 UPDATE 标签的消息
|
|
233
|
-
)
|
|
234
|
-
public class OrderCreateConsumer implements RocketMQListener<OrderEvent> {
|
|
235
|
-
|
|
236
|
-
@Override
|
|
237
|
-
public void onMessage(OrderEvent event) {
|
|
238
|
-
log.info("收到订单创建/更新事件: {}", event);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### 消费原始 MessageExt
|
|
244
|
-
|
|
245
|
-
```java
|
|
246
|
-
@Slf4j
|
|
247
|
-
@Component
|
|
248
|
-
@RocketMQMessageListener(
|
|
249
|
-
topic = "order-topic",
|
|
250
|
-
consumerGroup = "order-ext-group"
|
|
251
|
-
)
|
|
252
|
-
public class OrderExtConsumer implements RocketMQListener<MessageExt> {
|
|
253
|
-
|
|
254
|
-
@Override
|
|
255
|
-
public void onMessage(MessageExt messageExt) {
|
|
256
|
-
String msgId = messageExt.getMsgId();
|
|
257
|
-
int reconsumeTimes = messageExt.getReconsumeTimes();
|
|
258
|
-
String body = new String(messageExt.getBody(), StandardCharsets.UTF_8);
|
|
259
|
-
|
|
260
|
-
log.info("收到消息: msgId={}, 重试次数={}, body={}", msgId, reconsumeTimes, body);
|
|
261
|
-
|
|
262
|
-
if (reconsumeTimes >= 3) {
|
|
263
|
-
// 超过重试次数,记录到数据库人工处理
|
|
264
|
-
log.error("消息重试超过 3 次,进入人工处理: msgId={}", msgId);
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// 处理业务逻辑
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
## 延迟消息
|
|
274
|
-
|
|
275
|
-
RocketMQ 支持 18 个延迟级别:
|
|
276
|
-
|
|
277
|
-
| 级别 | 延迟时间 | 级别 | 延迟时间 |
|
|
278
|
-
|------|---------|------|---------|
|
|
279
|
-
| 1 | 1s | 10 | 6min |
|
|
280
|
-
| 2 | 5s | 11 | 7min |
|
|
281
|
-
| 3 | 10s | 12 | 8min |
|
|
282
|
-
| 4 | 30s | 13 | 9min |
|
|
283
|
-
| 5 | 1min | 14 | 10min |
|
|
284
|
-
| 6 | 2min | 15 | 20min |
|
|
285
|
-
| 7 | 3min | 16 | 30min |
|
|
286
|
-
| 8 | 4min | 17 | 1h |
|
|
287
|
-
| 9 | 5min | 18 | 2h |
|
|
288
|
-
|
|
289
|
-
### 发送延迟消息
|
|
290
|
-
|
|
291
|
-
```java
|
|
292
|
-
@Service
|
|
293
|
-
@RequiredArgsConstructor
|
|
294
|
-
public class DelayMessageProducer {
|
|
295
|
-
|
|
296
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* 发送延迟消息
|
|
300
|
-
* @param delayLevel 延迟级别(1-18)
|
|
301
|
-
*/
|
|
302
|
-
public void sendDelayMessage(String topic, Object payload, int delayLevel) {
|
|
303
|
-
Message<?> message = MessageBuilder.withPayload(payload).build();
|
|
304
|
-
// syncSend(destination, message, timeout, delayLevel)
|
|
305
|
-
SendResult result = rocketMQTemplate.syncSend(topic, message, 3000, delayLevel);
|
|
306
|
-
log.info("延迟消息发送成功: msgId={}, delayLevel={}", result.getMsgId(), delayLevel);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* 订单超时取消:30 分钟后检查(延迟级别 16)
|
|
311
|
-
*/
|
|
312
|
-
public void sendOrderTimeoutCheck(String orderNo) {
|
|
313
|
-
OrderTimeoutEvent event = new OrderTimeoutEvent(orderNo, LocalDateTime.now());
|
|
314
|
-
Message<?> message = MessageBuilder.withPayload(event).build();
|
|
315
|
-
rocketMQTemplate.syncSend("order-timeout-topic", message, 3000, 16);
|
|
316
|
-
log.info("订单超时检查消息已发送: orderNo={}, 30 分钟后触发", orderNo);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### 消费延迟消息
|
|
322
|
-
|
|
323
|
-
```java
|
|
324
|
-
@Slf4j
|
|
325
|
-
@Component
|
|
326
|
-
@RocketMQMessageListener(
|
|
327
|
-
topic = "order-timeout-topic",
|
|
328
|
-
consumerGroup = "order-timeout-group"
|
|
329
|
-
)
|
|
330
|
-
public class OrderTimeoutConsumer implements RocketMQListener<OrderTimeoutEvent> {
|
|
331
|
-
|
|
332
|
-
@Autowired
|
|
333
|
-
private OrderService orderService;
|
|
334
|
-
|
|
335
|
-
@Override
|
|
336
|
-
public void onMessage(OrderTimeoutEvent event) {
|
|
337
|
-
log.info("收到订单超时检查: orderNo={}", event.getOrderNo());
|
|
338
|
-
// 检查订单是否已支付,未支付则取消
|
|
339
|
-
orderService.cancelIfUnpaid(event.getOrderNo());
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
## 事务消息
|
|
345
|
-
|
|
346
|
-
### 事务消息流程
|
|
347
|
-
|
|
348
|
-
1. 生产者发送半事务消息(Half Message)到 Broker
|
|
349
|
-
2. Broker 存储成功后回调本地事务执行
|
|
350
|
-
3. 本地事务执行成功,提交消息;失败,回滚消息
|
|
351
|
-
4. Broker 定期回查未确认的事务消息状态
|
|
352
|
-
|
|
353
|
-
### 发送事务消息
|
|
354
|
-
|
|
355
|
-
```java
|
|
356
|
-
@Service
|
|
357
|
-
@RequiredArgsConstructor
|
|
358
|
-
public class TransactionalProducer {
|
|
359
|
-
|
|
360
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* 发送事务消息
|
|
364
|
-
*/
|
|
365
|
-
public void sendTransactionalMessage(OrderEvent event) {
|
|
366
|
-
Message<OrderEvent> message = MessageBuilder
|
|
367
|
-
.withPayload(event)
|
|
368
|
-
.setHeader("orderNo", event.getOrderNo())
|
|
369
|
-
.build();
|
|
370
|
-
|
|
371
|
-
// 发送事务消息
|
|
372
|
-
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
|
|
373
|
-
"order-tx-topic", message, event);
|
|
374
|
-
|
|
375
|
-
log.info("事务消息发送结果: msgId={}, localTxState={}",
|
|
376
|
-
result.getMsgId(), result.getLocalTransactionState());
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### 实现本地事务监听器
|
|
382
|
-
|
|
383
|
-
```java
|
|
384
|
-
package com.example.listener;
|
|
385
|
-
|
|
386
|
-
import com.example.event.OrderEvent;
|
|
387
|
-
import com.example.service.OrderService;
|
|
388
|
-
import lombok.RequiredArgsConstructor;
|
|
389
|
-
import lombok.extern.slf4j.Slf4j;
|
|
390
|
-
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
|
|
391
|
-
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
|
|
392
|
-
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
|
|
393
|
-
import org.springframework.messaging.Message;
|
|
394
|
-
import org.springframework.stereotype.Component;
|
|
395
|
-
|
|
396
|
-
@Slf4j
|
|
397
|
-
@Component
|
|
398
|
-
@RocketMQTransactionListener
|
|
399
|
-
@RequiredArgsConstructor
|
|
400
|
-
public class OrderTransactionListener implements RocketMQLocalTransactionListener {
|
|
401
|
-
|
|
402
|
-
private final OrderService orderService;
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* 执行本地事务
|
|
406
|
-
* 半事务消息发送成功后回调
|
|
407
|
-
*/
|
|
408
|
-
@Override
|
|
409
|
-
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
|
|
410
|
-
OrderEvent event = (OrderEvent) arg;
|
|
411
|
-
try {
|
|
412
|
-
log.info("执行本地事务: orderNo={}", event.getOrderNo());
|
|
413
|
-
// 执行本地数据库操作
|
|
414
|
-
orderService.createOrderLocal(event);
|
|
415
|
-
return RocketMQLocalTransactionState.COMMIT; // 提交消息
|
|
416
|
-
} catch (Exception e) {
|
|
417
|
-
log.error("本地事务执行失败: orderNo={}", event.getOrderNo(), e);
|
|
418
|
-
return RocketMQLocalTransactionState.ROLLBACK; // 回滚消息
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* 事务回查
|
|
424
|
-
* Broker 未收到确认时定期回查
|
|
425
|
-
*/
|
|
426
|
-
@Override
|
|
427
|
-
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
|
|
428
|
-
String orderNo = (String) msg.getHeaders().get("orderNo");
|
|
429
|
-
log.info("事务回查: orderNo={}", orderNo);
|
|
430
|
-
|
|
431
|
-
// 查询本地事务是否已执行
|
|
432
|
-
boolean exists = orderService.existsByOrderNo(orderNo);
|
|
433
|
-
if (exists) {
|
|
434
|
-
return RocketMQLocalTransactionState.COMMIT;
|
|
435
|
-
}
|
|
436
|
-
return RocketMQLocalTransactionState.UNKNOWN; // 继续回查
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
## 顺序消息
|
|
442
|
-
|
|
443
|
-
### 发送顺序消息
|
|
444
|
-
|
|
445
|
-
```java
|
|
446
|
-
@Service
|
|
447
|
-
@RequiredArgsConstructor
|
|
448
|
-
public class OrderlyProducer {
|
|
449
|
-
|
|
450
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* 发送顺序消息
|
|
454
|
-
* 相同 hashKey 的消息发送到同一队列,保证顺序
|
|
455
|
-
*/
|
|
456
|
-
public void sendOrderly(String topic, Object payload, String hashKey) {
|
|
457
|
-
SendResult result = rocketMQTemplate.syncSendOrderly(topic, payload, hashKey);
|
|
458
|
-
log.info("顺序消息发送成功: msgId={}, queue={}", result.getMsgId(),
|
|
459
|
-
result.getMessageQueue().getQueueId());
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* 订单状态变更:同一订单的状态变更消息保证顺序
|
|
464
|
-
*/
|
|
465
|
-
public void sendOrderStatusChange(String orderNo, String status) {
|
|
466
|
-
OrderStatusEvent event = new OrderStatusEvent(orderNo, status, LocalDateTime.now());
|
|
467
|
-
// 使用 orderNo 作为 hashKey,保证同一订单消息有序
|
|
468
|
-
rocketMQTemplate.syncSendOrderly("order-status-topic", event, orderNo);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### 顺序消费
|
|
474
|
-
|
|
475
|
-
```java
|
|
476
|
-
@Slf4j
|
|
477
|
-
@Component
|
|
478
|
-
@RocketMQMessageListener(
|
|
479
|
-
topic = "order-status-topic",
|
|
480
|
-
consumerGroup = "order-status-group",
|
|
481
|
-
consumeMode = ConsumeMode.ORDERLY // 顺序消费模式
|
|
482
|
-
)
|
|
483
|
-
public class OrderStatusConsumer implements RocketMQListener<OrderStatusEvent> {
|
|
484
|
-
|
|
485
|
-
@Override
|
|
486
|
-
public void onMessage(OrderStatusEvent event) {
|
|
487
|
-
log.info("顺序收到订单状态变更: orderNo={}, status={}",
|
|
488
|
-
event.getOrderNo(), event.getStatus());
|
|
489
|
-
// 按顺序处理订单状态变更
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
## Tag 消息过滤
|
|
495
|
-
|
|
496
|
-
### 发送带 Tag 的消息
|
|
497
|
-
|
|
498
|
-
```java
|
|
499
|
-
@Service
|
|
500
|
-
@RequiredArgsConstructor
|
|
501
|
-
public class TagProducer {
|
|
502
|
-
|
|
503
|
-
private final RocketMQTemplate rocketMQTemplate;
|
|
504
|
-
|
|
505
|
-
public void sendWithTag(OrderEvent event, OrderAction action) {
|
|
506
|
-
// destination 格式: topic:tag
|
|
507
|
-
String destination = "order-topic:" + action.name();
|
|
508
|
-
rocketMQTemplate.syncSend(destination, event);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
public enum OrderAction {
|
|
513
|
-
CREATE, UPDATE, CANCEL, COMPLETE
|
|
514
|
-
}
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
### 按 Tag 过滤消费
|
|
518
|
-
|
|
519
|
-
```java
|
|
520
|
-
// 只消费创建和取消的订单消息
|
|
521
|
-
@RocketMQMessageListener(
|
|
522
|
-
topic = "order-topic",
|
|
523
|
-
consumerGroup = "order-alert-group",
|
|
524
|
-
selectorType = SelectorType.TAG,
|
|
525
|
-
selectorExpression = "CREATE || CANCEL"
|
|
526
|
-
)
|
|
527
|
-
public class OrderAlertConsumer implements RocketMQListener<OrderEvent> {
|
|
528
|
-
@Override
|
|
529
|
-
public void onMessage(OrderEvent event) {
|
|
530
|
-
// 仅处理 CREATE 和 CANCEL 标签的消息
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
## 重试策略配置
|
|
536
|
-
|
|
537
|
-
### 消费重试
|
|
538
|
-
|
|
539
|
-
RocketMQ 默认消费失败重试 16 次,重试间隔逐步增大:
|
|
540
|
-
|
|
541
|
-
| 重试次数 | 间隔时间 | 重试次数 | 间隔时间 |
|
|
542
|
-
|---------|---------|---------|---------|
|
|
543
|
-
| 1 | 10s | 9 | 7min |
|
|
544
|
-
| 2 | 30s | 10 | 8min |
|
|
545
|
-
| 3 | 1min | 11 | 9min |
|
|
546
|
-
| 4 | 2min | 12 | 10min |
|
|
547
|
-
| 5 | 3min | 13 | 20min |
|
|
548
|
-
| 6 | 4min | 14 | 30min |
|
|
549
|
-
| 7 | 5min | 15 | 1h |
|
|
550
|
-
| 8 | 6min | 16 | 2h |
|
|
551
|
-
|
|
552
|
-
### 自定义重试次数
|
|
553
|
-
|
|
554
|
-
```java
|
|
555
|
-
@RocketMQMessageListener(
|
|
556
|
-
topic = "order-topic",
|
|
557
|
-
consumerGroup = "order-retry-group",
|
|
558
|
-
maxReconsumeTimes = 5 // 最大重试 5 次
|
|
559
|
-
)
|
|
560
|
-
public class RetryConsumer implements RocketMQListener<MessageExt> {
|
|
561
|
-
|
|
562
|
-
@Override
|
|
563
|
-
public void onMessage(MessageExt msg) {
|
|
564
|
-
if (msg.getReconsumeTimes() >= 3) {
|
|
565
|
-
log.warn("消息重试 {} 次,进入补偿流程: msgId={}", msg.getReconsumeTimes(), msg.getMsgId());
|
|
566
|
-
// 入库人工处理
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
// 正常业务处理,抛出异常会自动重试
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
## 最佳实践
|
|
575
|
-
|
|
576
|
-
### 1. 生产者
|
|
577
|
-
|
|
578
|
-
- **设置合理的超时时间**:`send-message-timeout` 建议 3-5 秒
|
|
579
|
-
- **启用重试**:同步和异步发送各设置 2-3 次重试
|
|
580
|
-
- **使用 Tag 分类消息**:便于消费端按需过滤
|
|
581
|
-
- **消息体控制大小**:单条消息不超过 4MB,大数据存 OSS 传引用
|
|
582
|
-
|
|
583
|
-
### 2. 消费者
|
|
584
|
-
|
|
585
|
-
- **幂等消费**:根据业务唯一标识(如订单号)去重
|
|
586
|
-
- **异常处理**:合理使用重试机制,超过重试次数入库人工处理
|
|
587
|
-
- **消费线程数**:`consumeThreadNumber` 根据业务类型设置,IO 密集型可设大
|
|
588
|
-
- **避免长时间消费**:单条消息处理时间建议不超过 15 秒
|
|
589
|
-
|
|
590
|
-
### 3. Topic 设计
|
|
591
|
-
|
|
592
|
-
- **按业务域划分 Topic**:如 order-topic、payment-topic、user-topic
|
|
593
|
-
- **使用 Tag 区分操作**:如 CREATE、UPDATE、DELETE
|
|
594
|
-
- **Topic 命名规范**:`{应用名}-{业务域}-topic`
|
|
595
|
-
|
|
596
|
-
### 4. 事务消息
|
|
597
|
-
|
|
598
|
-
- **本地事务要快**:避免在 executeLocalTransaction 中执行耗时操作
|
|
599
|
-
- **回查逻辑要幂等**:checkLocalTransaction 可能被多次调用
|
|
600
|
-
- **记录事务状态**:本地事务执行后记录状态,便于回查
|
|
601
|
-
|
|
602
|
-
## 常见问题
|
|
603
|
-
|
|
604
|
-
### 1. 消息发送超时
|
|
605
|
-
|
|
606
|
-
**原因**:NameServer 或 Broker 不可达
|
|
607
|
-
|
|
608
|
-
**解决**:
|
|
609
|
-
- 检查 `rocketmq.name-server` 配置是否正确
|
|
610
|
-
- 检查网络连通性:`telnet nameserver-host 9876`
|
|
611
|
-
- 增大 `send-message-timeout` 配置
|
|
612
|
-
- 检查 Broker 是否正常运行
|
|
613
|
-
|
|
614
|
-
### 2. 消费者无法消费消息
|
|
615
|
-
|
|
616
|
-
**原因**:消费者组名冲突或 Topic 不存在
|
|
617
|
-
|
|
618
|
-
**解决**:
|
|
619
|
-
- 检查 `consumerGroup` 是否唯一(不同 Topic 不能使用相同消费者组)
|
|
620
|
-
- 检查 Topic 是否已创建
|
|
621
|
-
- 检查 selectorExpression(Tag 过滤表达式)是否正确
|
|
622
|
-
- 查看消费者日志是否有注册成功的信息
|
|
623
|
-
|
|
624
|
-
### 3. 消息堆积
|
|
24
|
+
---
|
|
625
25
|
|
|
626
|
-
|
|
26
|
+
## 行为准则
|
|
627
27
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
-
|
|
631
|
-
-
|
|
632
|
-
-
|
|
28
|
+
1. **回答要具体**:引用具体的类名、注解、配置项
|
|
29
|
+
2. **主动提醒**:
|
|
30
|
+
- 不需要 `@EnableRocketMQ` 注解,引入依赖自动配置
|
|
31
|
+
- 消费者必须做幂等处理,RocketMQ 自带重试机制
|
|
32
|
+
- 幂等性功能需要 Redis 支持,未配置 Redis 时自动禁用
|
|
33
|
+
3. **使用中文回答**
|
|
34
|
+
4. **版本说明**:rocketmq-spring-boot-starter 2.3.4
|
|
633
35
|
|
|
634
|
-
|
|
36
|
+
---
|
|
635
37
|
|
|
636
|
-
|
|
38
|
+
## 文档读取路由
|
|
637
39
|
|
|
638
|
-
|
|
639
|
-
- 确保本地事务执行后有持久化状态可查
|
|
640
|
-
- 检查回查逻辑是否正确查询了本地事务状态
|
|
641
|
-
- 设置合理的事务回查间隔和次数
|
|
40
|
+
> 所有路径相对于 skill 目录 `docs/skills/avatar-boot-starter-rocketmq-skill/`
|
|
642
41
|
|
|
643
|
-
|
|
42
|
+
| 用户需求 | 需读取的文件 |
|
|
43
|
+
|:--|:--|
|
|
44
|
+
| 快速接入 / 消息发送 / 消息消费 | `references/核心功能.md` |
|
|
45
|
+
| 消息幂等性 / 消息可靠性 | `references/高级特性.md` |
|
|
46
|
+
| 配置说明 / 注意事项 | `references/配置与注意事项.md` |
|
|
644
47
|
|
|
645
|
-
|
|
48
|
+
---
|
|
646
49
|
|
|
647
|
-
|
|
648
|
-
- 确保 `consumeMode = ConsumeMode.ORDERLY`
|
|
649
|
-
- 避免消费异常导致的重试打乱顺序
|
|
650
|
-
- 确保使用正确的 hashKey 发送到同一队列
|
|
50
|
+
## 通用参考信息
|
|
651
51
|
|
|
652
|
-
|
|
52
|
+
### 延迟级别速查
|
|
653
53
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
54
|
+
| 级别 | 时间 | 级别 | 时间 |
|
|
55
|
+
|:--|:--|:--|:--|
|
|
56
|
+
| 1 | 1秒 | 10 | 6分钟 |
|
|
57
|
+
| 2 | 5秒 | 11 | 7分钟 |
|
|
58
|
+
| 3 | 10秒 | 14 | 10分钟 |
|
|
59
|
+
| 4 | 30秒 | 16 | 30分钟 |
|
|
60
|
+
| 5 | 1分钟 | 17 | 1小时 |
|
|
61
|
+
| 6 | 2分钟 | 18 | 2小时 |
|
|
657
62
|
|
|
658
|
-
|
|
63
|
+
### 消费者注解参数
|
|
659
64
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
-
|
|
65
|
+
| 参数 | 说明 | 默认值 |
|
|
66
|
+
|:--|:--|:--|
|
|
67
|
+
| `topic` | 主题名称 | - |
|
|
68
|
+
| `consumerGroup` | 消费者组名 | - |
|
|
69
|
+
| `selectorExpression` | Tag 过滤表达式 | `*` |
|
|
70
|
+
| `consumeMode` | 消费模式(CONCURRENTLY/ORDERLY) | `CONCURRENTLY` |
|