服务通信与异步通信
SpringCloud Alibaba 微服务通信
Spring实现异步的方法
实现异步任务是为了使用户体验更好。
- AsyncRestTemplate
- @Async注解
- WebClient(SpringClient5.0引入)
- MQ消息队列
MQ
适用场景
- 异步处理
- 流量削峰填谷(防止因为人数过多,导致的服务器崩溃,达到一定限制后直接丢弃请求)
- 解耦微服务
MQ的选择
RocketMQ 是Alibaba出品 并且贡献给了 Apache 官方,现在已经被官方纳入重点维护项目。
关于MQ的选择,其实是一门通多门,RocketMQ的好处在于RocketMQ与其他MQ对比。
RocketMQ下载
详细的下载教程为如何下载安装RacketMQ
RocketMQ控制台
如何安装RocketMQ控制台,详细的介绍了如何安装RocketMQ并且适配RocketMQ版本 。
RocketMQ在SpringBoot中的配置
发送者
引入依赖rocketmq-spring-boot-starter
,并且要适配版本(主要是适配控制台的版本,与引入的控制台版本保持一致)。
rocketmq:
name-server: 127.0.0.1:9876
producer:
# 接受者组
group: test-group
//使用之前要将相关的实体类注入
@Autowired
private final RocketMQTemplate r;
//发送方法
r.convertAndSend("Topic的名称,自己定义","要发送的消息");
接收者
引入依赖rocketmq-spring-boot-starter
,并且要适配版本(主要是适配控制台的版本,与引入的控制台版本保持一致)。
rocketmq:
name-server: 127.0.0.1:9876
//创建一个类实现RocketMQ接口,实现onMessage方法,泛型写接收类的类型
@Service
@RocketMQMessageListener(topic = "这里写topic的名称,和生产者的一样",consumerGroup = "consumer-group")
//必须制定消费者组
public class AddBonusListener implements RocketMQlistener<T>{
@Override
public void onMessage(UserAddBonusMsgDTO message){
//当收到消息后,执行具体的业务
}
}
各类MQ在Java中的使用类以及注解
Rocket实现分布式事务
使用RocketMQ实现分布式事务
- 分析业务是否需要联动微服务
- 调用其他微服务需要异步调用还是同步调用
- 通过在RocketMQ中的RocketMQTraction进行分布式任务的书写
首先,使用RocketMQTemplate的sendMessageTransaction
方法发送分布式事务,该方法有4个参数,第一个是需要被二次监听函数监听的名称;第二个是topic需要和接受者的一样;第三个是message,也就是传递给接受者的一样,第四个是一个留空参数,可以传递一些自己想要传递的值。
编写一个类实现RocketMQLocalTransactionListener接口,用来向Broker服务器发送二次确认的方法,其中executeLocalTransaction方法中要实现二次确认,而checkLocalTransaction方法则是在我们出现异常情况时接收端向我们发送确认消息时执行的逻辑,根据其中返回Commit还是Fallback指定是否要提交事务。
需要注意的是,我们在实现向数据库中添加数据的时候,需要将其标注为事务, @Transactional(fallback = Exception.class) 指定发生什么异常就回滚。
@RocketMQTransactionListener(txProducerGroup = "tx-add-bonus-group")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AddBonusTransactionListener implements RocketMQLocalTransactionListener {
private final ShareService shareService;
private final RocketmqTransactionLogMapper rocketmqTransactionLogMapper;
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
MessageHeaders headers = msg.getHeaders();
String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);
Integer shareId = Integer.valueOf((String) headers.get("share_id"));
String dtoString = (String) headers.get("dto");
ShareAuditDTO auditDTO = JSON.parseObject(dtoString, ShareAuditDTO.class);
try {
this.shareService.auditByIdWithRocketMqLog(shareId, auditDTO, transactionId);
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
MessageHeaders headers = msg.getHeaders();
String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);
// select * from xxx where transaction_id = xxx
RocketmqTransactionLog transactionLog = this.rocketmqTransactionLogMapper.selectOne(
RocketmqTransactionLog.builder()
.transactionId(transactionId)
.build()
);
if (transactionLog != null) {
return RocketMQLocalTransactionState.COMMIT;
}
return RocketMQLocalTransactionState.ROLLBACK;
}
}
SpringCloud Stream
SpringCloud Stream为我们提供了更加简便以及高效操作MQ的渠道,目前SpringCloud支持Kafka、RocketMQ、RabbitMQ,是一个便捷集成MQ的框架。Stream只是为我们提供了一个方便使用MQ的方法,MQ自身的依赖还是要引入的。
生产者
- 使用
spring-cloud-starter-stream-rocketmq
引入对应的依赖,如果是rabbitMQ,引入的就是amqp。 - 在启动类上加上@EnableBinding(Source.class)
- 添加配置信息
spring:
cloud:
stream:
rocketmq:
binder:
#MQ服务器所在地址,这个和racketMQ配置的是同一个地址
name-server: 127.0.0.1:9876
bindings:
output:
#指定topic
destination: stream-test-topic
- 注入Source类(类型和主类加的注解一样),这里的OutPut表示自己向外发送,而InPut则相反。
@Autowired
private Source source;
@GetMapping()
public STring testStream(){
//这里需要一个Message类型的参数
//可以使用MessageBuilder.withPayLoad( 实体类 ).build() 来构建一个Message类
this.source.output().send(Message<> T)
}
消费者
- 使用
spring-cloud-starter-stream-rocketmq
引入对应的依赖,如果是rabbitMQ,引入的就是amqp。 - 在启动类上加上@EnableBinding(Sink.class)
- 添加配置信息
spring:
cloud:
stream:
rocketmq:
binder:
#MQ服务器所在地址
name-server: 127.0.0.1:9876
bindings:
itput:
#指定topic
destination: stream-test-topic
# 一定要设置消费者组 如果其他MQ可以不设置
group: binder-group
- 注入Source类(类型和主类加的注解一样),这里的OutPut表示自己向外发送,而InPut则相反。
@StreamListener(Sink.class)
public String receive(String messageBody){
//处理消息即可
}
自定义Stream接口实现消费消息
- 自定义Stream接口MySink
public interface MySource{
String MY_INPUT = "my-input";
@IPut(MY_INPUT)
SubscribableChannel input();
- 在启动类上注册上文自定义的接口类型 @EnableBinding({Sink.class , MySink.class})
- 在配置文件中加入自定义配置
spring:
cloud:
stream:
rocketmq:
binder:
#MQ服务器所在地址,这个和racketMQ配置的是同一个地址
# 配置了这个就可以不用单独配置rocketMQ了
name-server: 127.0.0.1:9876
bindings:
output:
#指定topic
destination: stream-test-topic
my-outout: #这个一定要和自己定义的名字相同
destination: stream-my-topic
- 注入自己定义的接口使用即可。
PS:这里要注意,接口可能会被MyBatis的扫描路径扫描到。
自定义Stream接口实现生产消息
- 自定义Stream接口MySource
public interface MySource{
String MY_OUTPUT = "my-output";
@OutPut(MY_OUTPUT)
MessageChannel output();
- 在启动类上加上上文自定义的接口类型 @EnableBinding({Sink.class , MySource.class})
- 在配置文件中加入自定义配置
spring:
cloud:
stream:
rocketmq:
binder:
#MQ服务器所在地址
name-server: 127.0.0.1:9876
bindings:
itput:
#指定topic
destination: stream-test-topic
# 一定要设置消费者组 如果其他MQ可以不设置
group: binder-group
my-input:
destination: stream-my-topic
group: my-group
- 注入自己定义的接口使用即可。
消息过滤
SpringCloud Stream 监控
Stream 错误处理
Spring Cloud Stream + RocketMQ实现分布式事务
重构生产者
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
#这里是因为使用Stream的send方法,所以要将channel名配置在这里,并且开启事务
bindings:
output:
producer:
transactional: true
group: tx-add-bonus-group
bindings:
output:
# 用来指定topic
destination: add-bonus
这里就是将RocketMQTemplate换成Stream的send方法,其他的代码几乎不用重构。因为send方法的参数太少,所以要用header来进行参数传递,并且header会将参数自动转换成String,配置文件中的名称一定要和二次监听中的名字一样。
重构消费者
spring:
cloud:
stream:
rocketmq:
binder:
#MQ服务器所在地址
name-server: 127.0.0.1:9876
bindings:
input:
#指定topic
destination: add-bonus
# 一定要设置消费者组 如果其他MQ可以不设置
group: binder-group
Input对象和OutPut接口对象声明后,如果要使用,就使用@Autowired进行注入。凡是设计到本地数据库的操作,最好使用事务另外封装一个方法,在MQ的方法调用即可。