Spring Cloud Alibaba - Spring Cloud Stream 整合 RocketMQ
Spring Cloud Stream
简介
在微服务的开发过程中,可能会经常用到消息中间件,通过消息中间件在服务与服务之间传递消息,不管你使用的是哪款消息中间件,比如RabbitMQ、Kafka和RocketMQ,那么消息中间件和服务之间都有一点耦合性,这个耦合性就是指如果我原来使用的RabbitMQ,现在要替换为RocketMQ,那么我们的微服务都需要修改,变动会比较大,因为这两款消息中间件有一些区别,如果我们使用Spring Cloud Stream来整合我们的消息中间件,那么这样就可以降低微服务和消息中间件的耦合性,做到轻松在不同消息中间件间切换,当然Spring Cloud Stream官方只支持rabbitmq 和 kafka,spring cloud alibaba新写了一个starter可以支持RocketMQ;
按照官方的定义,Spring Cloud Stream 是一个构建消息驱动微服务的框架;
Spring Cloud Stream解决了开发人员无感知的使用消息中间件的问题,因为Spring Cloud Stream对消息中间件的进一步封装,可以做到代码层面对消息中间件的无感知,甚至于动态的切换中间件(rabbitmq切换为rocketmq或者kafka),使得微服务开发的高度解耦,服务可以关注更多自己的业务流程;
核心概念
Spring Cloud Stream 内部有几个概念:Binder 、Binding、input、output;
1、Binder: 跟外部消息中间件集成的组件,用来创建Binding,各消息中间件都有自己的 Binder 实现;
比如 Kafka 的实现 KafkaMessageChannelBinder,RabbitMQ 的实现 RabbitMessageChannelBinder 以及 RocketMQ 的实现 RocketMQMessageChannelBinder;
2、Binding: 包括 Input Binding 和 Output Binding;
Binding 在消息中间件与应用程序提供的 Provider 和 Consumer 之间提供了一个桥梁,实现了开发者只需使用应用程序的 Provider 或 Consumer 生产或消费数据即可,屏蔽了开发者与底层消息中间件的接触;
3、input
应用程序通过input(相当于消费者consumer)与Spring Cloud Stream中Binder交互,而Binder负责与消息中间件交互,因此,我们只需关注如何与Binder交互即可,而无需关注与具体消息中间件的交互。
4、Output
output(相当于生产者producer)与Spring Cloud Stream中Binder交互;
组成 | 说明 |
---|---|
Binder | Binder是应用与消息中间件之间的封装,目前实现了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现; |
@Input | 该注解标识输入通道,通过该输入通道接收消息进入应用程序 |
@Output | 该注解标识输出通道,发布的消息将通过该通道离开应用程序 |
@StreamListener | 监听队列,用于消费者的队列的消息接收 |
@EnableBinding | 将信道channel和exchange、topic绑定在一起 |
Spring Cloud Stream 应用
消息生产者
1、创建SpringBoot应用31-rocket-spring-cloud-stream;
2、添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
配置文件
# 应用名称
spring.application.name=stream
########## RocketMQ 通用配置
# 客户端接入点,必填 -rocketmq 连接地址
spring.cloud.stream.rocketmq.binder.name-server=localhost:9876
# 日志级别
logging.level.com.alibaba.cloud.stream.binder.rocketmq=INFO
########## Consumer Config 消费者
# input 的配置:
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.group=test-group
########## Produce Config 生产者
# output 的配置如下: bingdings具体生产,消费的桥梁
spring.cloud.stream.bindings.output.destination=test-topic //目的地保持一致
spring.cloud.stream.bindings.output.content-type=text/plain
spring.cloud.stream.bindings.output.group=test-group
兼容性问题:
注意版本需要使用springboot2.2.5
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
消息发送:
@EnableBinding(Source.class)
@Service
public class SenderService {
@Autowired
private Source source;
public void send(String msg) throws Exception {
boolean flag = source.output().send(MessageBuilder.*withPayload*(msg).build());
System.*out*.println("消息发送:" + flag);
}
}
消息接收:
@EnableBinding(Sink.class)
public class ReceiveService {
@StreamListener("input")
public void receiveInput1(String receiveMsg) {
System.*out*.println("input 接收到的消息: " + receiveMsg);
}
}
可以通过调用SenderService中的方法进行发送信息,也可以通过在启动类中的Main方法中进行调用SenderService的方法进行发送信息:
@SuppressWarnings("all")
@EnableBinding(value = {Source.class, Sink.class}) //使得Source生效
@SpringBootApplication
public class StreamApplication implements CommandLineRunner {
@Autowired
private SenderService senderService;
@Autowired
private ReceiveService receiveService;
public static void main(String[] args) {
SpringApplication.run(StreamApplication.class, args);
}
//在main中调用发送信息方法
@Override
public void run(String... args) throws Exception {
senderService.send("hello rocketmq");
}
}
Spring Cloud Stream自定义信道
在前面的案例中,我们已经实现了一个基础的 Spring Cloud Stream 消息传递处理操作,但在操作之中使用的是系统提供的 Source (output)、Sink(input),接下来我们来看一下自定义信道名称;
public interface MySource {
String OUTPUT1 = "output1";
@Output(MySource.OUTPUT1)
MessageChannel output1();
String OUTPUT2 = "output2";
@Output(MySource.OUTPUT2)
MessageChannel output2();
}
public interface MySink {
String INPUT1 = "input1";
@Input(MySink.INPUT1)
SubscribableChannel input1();
String INPUT2 = "input1";
@Input(MySink.INPUT2)
SubscribableChannel input2();
}
server.port=8090
# 应用名称
spring.application.name=stream
########## RocketMQ 通用配置
# 客户端接入点,必填 -rocketmq 连接地址
spring.cloud.stream.rocketmq.binder.name-server=localhost:9876
# 日志级别
logging.level.com.alibaba.cloud.stream.binder.rocketmq=INFO
########## Consumer Config 消费者
# input 的配置:
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.group=test-group
########## Produce Config 生产者
# output 的配置如下: bingdings具体生产,消费的桥梁
spring.cloud.stream.bindings.output.destination=test-topic //目的地保持一致
spring.cloud.stream.bindings.output.content-type=text/plain
spring.cloud.stream.bindings.output.group=test-group
########## 自定义
# input 的配置:
spring.cloud.stream.bindings.input1.destination=test-topic1
spring.cloud.stream.bindings.input1.content-type=text/plain
spring.cloud.stream.bindings.input1.group=test-group1
# output 的配置:
spring.cloud.stream.bindings.output1.destination=test-topic1
spring.cloud.stream.bindings.output1.content-type=text/plain
spring.cloud.stream.bindings.output1.group=test-group1
SpringCloudStream RocketMQ事务消息
Apache RocketMQ在4.3.0版中已经支持分布式事务消息,这里RocketMQ采用了2PC的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息,如下图所示:
上图说明了事务消息的大致方案,其中分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程;
1.事务消息发送及提交:
(1) 发送消息(half消息);
(2) 服务端响应消息写入结果;
(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行);
(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)
2.补偿流程:
(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”;
(2) Producer收到回查消息,检查回查消息对应的本地事务的状态;
(3) 根据本地事务状态,重新Commit或者Rollback;
其中,补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况;
事务消息一共有三种状态:提交状态、回滚状态、中间状态;
TransactionStatus.CommitTransaction: 提交事务,代表消费者可以消费此消息;
TransactionStatus.RollbackTransaction: 回滚事务,代表消息将被删除,不能被消费;
TransactionStatus.Unknown: 中间状态,代表需要检查消息队列来确定状态;
MQ内部逻辑:
package com.springcloud.stream.stream.Transaction;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;
//MQ接收,并根据结果运行内部逻辑
@SuppressWarnings("all")
@RocketMQTransactionListener(txProducerGroup = "myTxProducerGroup", corePoolSize = 5, maximumPoolSize = 10)
public class TransactionListenerImpl implements RocketMQLocalTransactionListener {
/**
* 执行本地事务:也就是执行本地业务逻辑
*
* @param msg
* @param arg
* @return
*/
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
Object num = msg.getHeaders().get("test");
if ("1".equals(num)) {
System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " unknown");
return RocketMQLocalTransactionState.UNKNOWN;
}
else if ("2".equals(num)) {
System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " rollback");
return RocketMQLocalTransactionState.ROLLBACK;
}
System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " commit");
return RocketMQLocalTransactionState.COMMIT;
}
/**
* 回调检查
*
* @param msg
* @return
*/
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
System.out.println("check: " + new String((byte[]) msg.getPayload()));
return RocketMQLocalTransactionState.COMMIT;
}
}
消息发送:
@Component
public class Sender {
@Autowired
private MySource mySource;
public <T> void sendTransactionalMsg(T msg ,int num) throws Exception{
MessageBuilder builder = MessageBuilder.withPayload(msg)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
.setHeader("test",String.valueOf(num));
//.setHeader(RocketMQHeaders.TAGS,"binder");
Message message = builder.build();
mySource.outputTX().send(message);
}
}
自定义信道-重写Source
public interface MySource {
String OUTPUTTX = "outputTX";
@Output(MySource.OUTPUTTX)
MessageChannel outputTX();
}
自定义信道-重写Sink
public interface MySink {
String INPUTTX = "inputTX";
@Input(MySink.INPUTTX)
SubscribableChannel inputTX();
}
消费者接收消息:
@EnableBinding({MySink.class})
public class ReceiveService {
//spring cloud stream 里面发消息通过sink发送
@Autowired
private MySink mySink;
//消费者端接收到的消息
@StreamListener("inputTX")
public void receiveTransactionMessage(String receiveMsg) {
System.out.println("Transaction_input 接收到的消息: " + receiveMsg);
}
}
Spring Cloud Stream RocketMQ 配置选项
RocketMQ Binder Properties
*spring.cloud.stream.rocketmq.binder.name-server*
RocketMQ NameServer 地址(老版本使用 namesrv-addr 配置项);
Default: 127.0.0.1:9876.
*spring.cloud.stream.rocketmq.binder.access-key*
阿里云账号 AccessKey。
Default: null.
*spring.cloud.stream.rocketmq.binder.secret-key*
阿里云账号 SecretKey。
Default: null.
*spring.cloud.stream.rocketmq.binder.enable-msg-trace*
是否为 Producer 和 Consumer 开启消息轨迹功能
Default: true.
*spring.cloud.stream.rocketmq.binder.customized-trace-topic*
消息轨迹开启后存储的 topic 名称。
Default: RMQ_SYS_TRACE_TOPIC.
RocketMQ Consumer Properties
下面的这些配置是以 spring.cloud.stream.rocketmq.bindings..consumer. 为前缀的 RocketMQ Consumer 相关的配置。
*enable*
是否启用 Consumer;
默认值: true.
*tags*
Consumer 基于 TAGS 订阅,多个 tag 以 || 分割;
默认值: empty.
*sql*
Consumer 基于 SQL 订阅;
默认值: empty.
*broadcasting*
Consumer 是否是广播消费模式。如果想让所有的订阅者都能接收到消息,可以使用广播模式;
默认值: false.
*orderly*
Consumer 是否同步消费消息模式;
默认值: false.
*delayLevelWhenNextConsume*
异步消费消息模式下消费失败重试策略:
-1,不重复,直接放入死信队列
0,broker 控制重试策略
>0,client 控制重试策略
默认值: 0.
*suspendCurrentQueueTimeMillis*
同步消费消息模式下消费失败后再次消费的时间间隔;
默认值: 1000.
RocketMQ Provider Properties
下面的这些配置是以 spring.cloud.stream.rocketmq.bindings..producer. 为前缀的 RocketMQ Producer 相关的配置;
*enable*
是否启用 Producer;
默认值: true.
*group*
Producer group name;
默认值: empty.
*maxMessageSize*
消息发送的最大字节数;
默认值: 8249344.
*transactional*
是否发送事务消息;
默认值: false.
*sync*
是否使用同步得方式发送消息;
默认值: false.
*vipChannelEnabled*
是否在 Vip Channel 上发送消息;
默认值: true.
*sendMessageTimeout*
发送消息的超时时间(毫秒);
默认值: 3000.
*compressMessageBodyThreshold*
消息体压缩阀值(当消息体超过 4k 的时候会被压缩);
默认值: 4096.
*retryTimesWhenSendFailed*
在同步发送消息的模式下,消息发送失败的重试次数;
默认值: 2.
*retryTimesWhenSendAsyncFailed*
在异步发送消息的模式下,消息发送失败的重试次数;
默认值: 2.
*retryNextServer*
消息发送失败的情况下是否重试其它的 broker;
默认值: false.
Spring Cloud Alibaba - Spring Cloud Stream 整合 RocketMQ的更多相关文章
- Spring Cloud Alibaba学习笔记(1) - 整合Spring Cloud Alibaba
Spring Cloud Alibaba从孵化器版本毕业:https://github.com/alibaba/spring-cloud-alibaba,记录一下自己学习Spring Cloud Al ...
- Spring Cloud Alibaba 新一代微服务解决方案
本篇是「跟我学 Spring Cloud Alibaba」系列的第一篇, 每期文章会在公众号「架构进化论」进行首发更新,欢迎关注. 1.Spring Cloud Alibaba 是什么 Spring ...
- Spring Cloud Alibaba 之 Nacos
Nacos 技术讲解 一提到分布式系统就不的不提一下 CAP 原则 什么是CAP CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency).可用性(Availability ...
- Spring Cloud Alibaba 实战(十三) - Sleuth调用链监控
本文概要:大白话剖析调用链监控原理,然后学习Sleuth,Zipkin,然后将Sleuth整合Zipkin,最后学习Zipkin数据持久化(Elasticsearch)以及Zipkin依赖关系图 实战 ...
- Spring Cloud Alibaba 之Nacos
Nacos 技术讲解 一提到分布式系统就不的不提一下 CAP 原则 什么是CAP CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency).可用性(Availability ...
- Spring Cloud Alibaba 之 版本选择
alibaba 版本问题 一下是Spring cloud ,Spring Cloud Alibaba, Spring Boot 之间的版本选择 在版本选择上大家尽量选择稳定版,也就是Release 后 ...
- Spring Cloud Alibaba学习笔记(14) - Spring Cloud Stream + RocketMQ实现分布式事务
发送消息 在Spring消息编程模型下,使用RocketMQ收发消息 一文中,发送消息使用的是RocketMQTemplate类. 在集成了Spring Cloud Stream之后,我们可以使用So ...
- Spring Cloud Alibaba(五)RocketMQ 异步通信实现
本文探讨如何使用 RocketMQ Binder 完成 Spring Cloud 应用消息的订阅和发布. 介绍 RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的.高 ...
- Spring Cloud Alibaba整合Sentinel流控
前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...
随机推荐
- hdu 6030 矩阵快速幂
大致题意: 一条长度为n的项链,由红色珠子和蓝色珠子(分别用1和0表示)组成,在连续的素数子段中,红色珠子的个数不能少于蓝色珠子.问组成这个项链有多少种方案,求方案数模1000000007 分析: 首 ...
- Java:jar包与war包的差异
一般将项目分为两层:服务层和表现层(视图层),通常我们把服务层打包成jar,而把视图层的包打成war包. 仔细对比可以发现: jar包中包含了你写程序的所有服务或者第三方类库,它通常是作为幕后工作者, ...
- Mybatis学习(4)实现关联数据的查询
有了前面几章的基础,对一些简单的应用是可以处理的,但在实际项目中,经常是关联表的查询,比如最常见到的多对一,一对多等.这些查询是如何处理的呢,这一讲就讲这个问题.我们首先创建一个Article 这个表 ...
- PE文件头格式解析
前言: 昨天写了一题de1ctf的题,发现要脱壳,手脱之后发现要iat修复,我就发现自己在这块知识缺失了,win逆向,好像一直都是打ctf,然后用逆向方法论去肝的 其他方面倒是没有很深入学习,但实际上 ...
- Java实验项目六——使用DAO模式实现对职工表的操作
Program: 利用JDBC访问职工信息表,实现对职工信息的添加.更新.删除.按照职工号查找.查找全部职工的功能. Description:在这里我采用了DAO设计模式完成对职工表的操作,下面介绍一 ...
- 【Azure 应用服务】Azure Function App使用SendGrid发送邮件遇见异常消息The operation was canceled,分析源码逐步最终源端
问题描述 在使用Azure Function App的SendGrid Binging功能,调用SendGrid服务器发送邮件功能时,有时候遇见间歇性,偶发性异常.在重新触发SendGrid部分的Fu ...
- Louvain 论文笔记
Louvain Introduce Louvain算法是社区发现领域中经典的基于模块度最优化的方法,且是目前市场上最常用的社区发现算法.社区发现旨在发现图结构中存在的类簇(而非传统的向量空间). Al ...
- 个人博客开发之blog-api项目统一结果集api封装
前言 由于返回json api 格式接口,所以我们需要通过java bean封装一个统一数据返回格式,便于和前端约定交互, 状态码枚举ResultCode package cn.soboys.core ...
- Log4cpp配置文件及动态调整日志级别的方法
一.log4cpp概述 Log4cpp是一个开源的C++类库,它提供了C++程序中使用日志和跟踪调试的功能,它的优点如下: 提供应用程序运行上下文,方便跟踪调试: 可扩展的.多种方式记录日志,包括命令 ...
- 机器学习Sklearn系列:(四)朴素贝叶斯
3--朴素贝叶斯 原理 朴素贝叶斯本质上就是通过贝叶斯公式来对得到类别概率,但区别于通常的贝叶斯公式,朴素贝叶斯有一个默认条件,就是特征之间条件独立. 条件概率公式: \[P(B|A) = \frac ...