Spring Cloud 終結篇之訊息驅動--stream 大集合
建立子工程 stream-sample
編寫pom檔案
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies>
建立啟動引導類 StreamApplication
@SpringBootApplication
public class StreamApplication {
public static void main(String[] args) {
SpringApplication.run(StreamApplication.class, args);
}
}
建立配置檔案
spring.application.name=stream-sample
server.port=63003
# RabbitMQ連線字串
spring.rabbitmq.host=192.168.0.201
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
# 繫結Channel到broadcast
spring.cloud.stream.bindings.myTopic-consumer.destination=broadcast
spring.cloud.stream.bindings.myTopic-producer.destination=broadcast
# 訊息分組示例
spring.cloud.stream.bindings.group-consumer.destination=group-topic
spring.cloud.stream.bindings.group-producer.destination=group-topic
spring.cloud.stream.bindings.group-consumer.group=Group-A
## 訊息分割槽配置
## 開啟消費者的消費分割槽功能
spring.cloud.stream.bindings.group-consumer.consumer.partitioned=true
## 兩個訊息分割槽
spring.cloud.stream.bindings.group-producer.producer.partition-count=2
# SpEL (Key resolver) 可以定義複雜表示式生成Key
# 我們這裡用最簡化的配置,只有索引引數為1的節點(消費者),才能消費訊息
spring.cloud.stream.bindings.group-producer.producer.partition-key-expression=1
# 當前消費者例項總數
spring.cloud.stream.instanceCount=2
# 最大值instanceCount-1,當前例項的索引號
spring.cloud.stream.instanceIndex=1
# 延遲訊息配置
spring.cloud.stream.bindings.delayed-consumer.destination=delayed-topic
spring.cloud.stream.bindings.delayed-producer.destination=delayed-topic
spring.cloud.stream.rabbit.bindings.delayed-producer.producer.delayed-exchange=true
# 異常訊息(單機版重試)
spring.cloud.stream.bindings.error-consumer.destination=error-out-topic
spring.cloud.stream.bindings.error-producer.destination=error-out-topic
# 重試次數(本機重試)
# 次數=1相當於不重試
spring.cloud.stream.bindings.error-consumer.consumer.max-attempts=2
# 異常訊息(requeue重試)
spring.cloud.stream.bindings.requeue-consumer.destination=requeue-topic
spring.cloud.stream.bindings.requeue-producer.destination=requeue-topic
# 必須把max-attempts設定為1,否則requeue不能生效
spring.cloud.stream.bindings.requeue-consumer.consumer.max-attempts=1
spring.cloud.stream.bindings.requeue-consumer.group=requeue-group
# 僅對當前requeue-consumer,開啟requeue
spring.cloud.stream.rabbit.bindings.requeue-consumer.consumer.requeueRejected=true
# 預設全域性開啟requeue
# spring.rabbitmq.listener.default-requeue-rejected=true
# 死信佇列配置
spring.cloud.stream.bindings.dlq-consumer.destination=dlq-topic
spring.cloud.stream.bindings.dlq-producer.destination=dlq-topic
spring.cloud.stream.bindings.dlq-consumer.consumer.max-attempts=2
spring.cloud.stream.bindings.dlq-consumer.group=dlq-group
# 開啟死信佇列(預設 topic.dlq)
spring.cloud.stream.rabbit.bindings.dlq-consumer.consumer.auto-bind-dlq=true
# Fallback配置
spring.cloud.stream.bindings.fallback-consumer.destination=fallback-topic
spring.cloud.stream.bindings.fallback-producer.destination=fallback-topic
spring.cloud.stream.bindings.fallback-consumer.consumer.max-attempts=2
spring.cloud.stream.bindings.fallback-consumer.group=fallback-group
# input channel -> fallback-topic.fallback-group.errors
management.security.enabled=false
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
建立 Topic
延時訊息
public interface DelayedTopic {
String INPUT = "delayed-consumer";
String OUTPUT = "delayed-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
死信佇列
public interface DlqTopic {
String INPUT = "dlq-consumer";
String OUTPUT = "dlq-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
異常訊息
public interface ErrorTopic {
String INPUT = "error-consumer";
String OUTPUT = "error-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
fallback降級
public interface FallbackTopic {
String INPUT = "fallback-consumer";
String OUTPUT = "fallback-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
分割槽分組
public interface GroupTopic {
String INPUT = "group-consumer";
String OUTPUT = "group-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
重入佇列
public interface RequeueTopic {
String INPUT = "requeue-consumer";
String OUTPUT = "requeue-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
我的 訊息
public interface MyTopic {
String INPUT = "myTopic-consumer";
String OUTPUT = "myTopic-producer";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
}
建立stream 流 訊息消費者
@Slf4j
@EnableBinding(value = {
Sink.class,
MyTopic.class,
GroupTopic.class,
DelayedTopic.class,
ErrorTopic.class,
RequeueTopic.class,
DlqTopic.class,
FallbackTopic.class
}
)
public class StreamConsumer {
private AtomicInteger count = new AtomicInteger(1);
@StreamListener(Sink.INPUT)
public void consume(Object payload) {
log.info("message consumed successfully, payload={}", payload);
}
// 自定義訊息廣播
@StreamListener(MyTopic.INPUT)
public void consumeMyMessage(Object payload) {
log.info("My message consumed successfully, payload={}", payload);
}
// 訊息分組 & 消費分割槽示例
@StreamListener(GroupTopic.INPUT)
public void consumeGroupMessage(Object payload) {
log.info("Group message consumed successfully, payload={}", payload);
}
// 延遲訊息示例
@StreamListener(DelayedTopic.INPUT)
public void consumeDelayedMessage(MessageBean bean) {
log.info("Delayed message consumed successfully, payload={}", bean.getPayload());
}
// 異常重試(單機版)
@StreamListener(ErrorTopic.INPUT)
public void consumeErrorMessage(MessageBean bean) {
log.info("Are you OK?");
if (count.incrementAndGet() % 3 == 0) {
log.info("Fine, thank you. And you?");
count.set(0);
} else {
log.info("What's your problem?");
throw new RuntimeException("I'm not OK");
}
}
// 異常重試(聯機版-重新入列)
@StreamListener(RequeueTopic.INPUT)
public void requeueErrorMessage(MessageBean bean) {
log.info("Are you OK?");
try {
Thread.sleep(3000L);
} catch (Exception e) {
}
// throw new RuntimeException("I'm not OK");
}
// 死信佇列
@StreamListener(DlqTopic.INPUT)
public void consumeDlqMessage(MessageBean bean) {
log.info("Dlq - Are you OK?");
if (count.incrementAndGet() % 3 == 0) {
log.info("Dlq - Fine, thank you. And you?");
} else {
log.info("Dlq - What's your problem?");
throw new RuntimeException("I'm not OK");
}
}
// Fallback + 升級版本
@StreamListener(FallbackTopic.INPUT)
public void goodbyeBadGuy(MessageBean bean,
@Header("version") String version) {
log.info("Fallback - Are you OK?");
if ("1.0".equalsIgnoreCase(version)) {
log.info("Fallback - Fine, thank you. And you?");
} else if ("2.0".equalsIgnoreCase(version)) {
log.info("unsupported version");
throw new RuntimeException("I'm not OK");
} else {
log.info("Fallback - version={}", version);
}
}
// 降級流程
@ServiceActivator(inputChannel = "fallback-topic.fallback-group.errors")
public void fallback(Message<?> message) {
log.info("fallback entered");
}
}
建立一個 messageBean
@Data
public class MessageBean {
private String payload;
}
最後一步 建立 Controller
@RestController
@Slf4j
public class Controller {
@Autowired
private MyTopic producer;
@Autowired
private GroupTopic groupTopicProducer;
@Autowired
private DelayedTopic delayedTopicProducer;
@Autowired
private ErrorTopic errorTopicProducer;
@Autowired
private RequeueTopic requeueTopicProducer;
@Autowired
private DlqTopic dlqTopicProducer;
@Autowired
private FallbackTopic fallbackTopicProducer;
// 簡單廣播訊息
@PostMapping("send")
public void sendMessage(@RequestParam(value = "body") String body) {
producer.output().send(MessageBuilder.withPayload(body).build());
}
// 訊息分組和訊息分割槽
@PostMapping("sendToGroup")
public void sendMessageToGroup(@RequestParam(value = "body") String body) {
groupTopicProducer.output().send(MessageBuilder.withPayload(body).build());
}
// 延遲訊息
@PostMapping("sendDM")
public void sendDelayedMessage(
@RequestParam(value = "body") String body,
@RequestParam(value = "seconds") Integer seconds) {
MessageBean msg = new MessageBean();
msg.setPayload(body);
log.info("ready to send delayed message");
delayedTopicProducer.output().send(
MessageBuilder.withPayload(msg)
.setHeader("x-delay", seconds * 1000)
.build());
}
// 異常重試(單機版)
@PostMapping("sendError")
public void sendErrorMessage(@RequestParam(value = "body") String body) {
MessageBean msg = new MessageBean();
msg.setPayload(body);
errorTopicProducer.output().send(MessageBuilder.withPayload(msg).build());
}
// 異常重試(聯機版 - 重新入列)
@PostMapping("requeue")
public void sendErrorMessageToMQ(@RequestParam(value = "body") String body) {
MessageBean msg = new MessageBean();
msg.setPayload(body);
requeueTopicProducer.output().send(MessageBuilder.withPayload(msg).build());
}
// 死信佇列測試
@PostMapping("dlq")
public void sendMessageToDlq(@RequestParam(value = "body") String body) {
MessageBean msg = new MessageBean();
msg.setPayload(body);
dlqTopicProducer.output().send(MessageBuilder.withPayload(msg).build());
}
// fallback + 升版
@PostMapping("fallback")
public void sendMessageToFallback(
@RequestParam(value = "body") String body,
@RequestParam(value = "version", defaultValue = "1.0") String version) {
MessageBean msg = new MessageBean();
msg.setPayload(body);
fallbackTopicProducer.output().send(
MessageBuilder.withPayload(msg)
.setHeader("version", version)
.build());
}
}
附:
1. 下載外掛
https://www.rabbitmq.com/community-plugins.html
找到rabbitmq_delayed_message_exchange
下載對應版本的外掛,3.6和3.7版本外掛不一樣
2. 下載以後解壓,copy到rabbitmq安裝目錄下的plugins資料夾
3. 安裝外掛
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
4. 安裝完一定要重啟RabbitMQ,不是單單重啟UI管理介面!
如果只是單單呼叫rabbitmqctl stop_app然後再rabbitmqctl start_app是沒有作用的!
正確的步驟是先rabbitmqctl stop,然後再直接執行rabbitmq-server
如果以上步驟還能使延遲佇列生效,在重啟完之後,換一個新的topic名字就好了
相關文章
- 訊息驅動式微服務:Spring Cloud Stream & RabbitMQ微服務SpringCloudMQ
- (十七) 整合spring cloud雲架構 -訊息驅動 Spring Cloud StreamSpringCloud架構
- Spring Cloud Stream微服務訊息框架SpringCloud微服務框架
- SpringCloud-Stream 訊息驅動SpringGCCloud
- SpringCloud(七)Stream訊息驅動SpringGCCloud
- 九. SpringCloud Stream訊息驅動SpringGCCloud
- Spring Cloud 快速入門(八)訊息系統整合框架 Spring Cloud StreamSpringCloud框架
- 最完整的 Spring Cloud 元件-訊息中介軟體 Spring Cloud Stream 使用教程SpringCloud元件
- Spring Cloud Stream如何消費自己生產的訊息?SpringCloud
- Spring Cloud Stream如何處理訊息重複消費?SpringCloud
- (十七) Java版本多使用者B2B2C商城原始碼 -訊息驅動 Spring Cloud StreamJava原始碼SpringCloud
- springCloud學習5(Spring-Cloud-Stream事件驅動)SpringGCCloud事件
- Java springcloud B2B2C o2o多使用者商城-訊息驅動 Spring Cloud StreamJavaSpringGCCloud
- java B2B2C Springboot多租戶電子商城系統-Spring Cloud Stream(訊息驅動)JavaSpring BootCloud
- 使用Spring Cloud Stream和RabbitMQ實現事件驅動的微服務SpringCloudMQ事件微服務
- 使用Spring Cloud Stream和Spring State Machine建立事件驅動的微服務案例SpringCloudMac事件微服務
- Storm概念學習系列之Stream訊息流 和 Stream Grouping 訊息流組ORM
- Spring Cloud構建微服務架構:訊息驅動的微服務(消費組)【Dalston版】SpringCloud微服務架構
- (十七)Java springcloud B2B2C o2o多使用者商城 springcloud架構-訊息驅動 Spring Cloud StreamJavaSpringGCCloud架構
- 使用spring.cloud.stream來傳送kafka訊息,並根據某欄位將訊息傳送到固定partition上SpringCloudKafka
- RocketMQ 整合 Spring Cloud StreamMQSpringCloud
- “訊息驅動、事件驅動、流 ”基礎概念解析事件
- spring cloud gateway之filter篇SpringCloudGatewayFilter
- spring cloud gateway 之限流篇SpringCloudGateway
- 跟我學SpringCloud | 第八篇:Spring Cloud Bus 訊息匯流排SpringGCCloud
- (四)spring cloud微服務分散式雲架構-配置中心和訊息匯流排(配置中心終結版)SpringCloud微服務分散式架構
- Spring Cloud Bus 訊息匯流排介紹SpringCloud
- Spring cloud系列教程第十篇- Spring cloud整合Eureka總結篇SpringCloud
- Spring Cloud Stream事件路由 - spring.ioSpringCloud事件路由
- Spring Cloud Stream同一通道根據訊息內容分發不同的消費邏輯SpringCloud
- 乾貨|Spring Cloud Bus 訊息匯流排介紹SpringCloud
- 死磕Spring之AOP篇 - Spring AOP註解驅動與XML配置SpringXML
- 驅動篇——總結與提升
- Spring Cloud Stream如何深度支援Apache Kafka?SpringCloudApacheKafka
- 『高階篇』docker之CICD(終結篇)(44)Docker
- laravel8.x --- 訊息佇列 [database] 驅動Laravel佇列Database
- 死磕 java集合之終結篇Java
- 【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客戶端元件問題, 實踐訊息非順序可達事件SpringCloud客戶端元件