CloudAlibabaRocketMQ-Stream-异步-7

异步

对于一些比较耗时的操作.可以将他们 以 异步的方式实现. 完成后在通知调用者

Spring实现的异步方法

在这里插入图片描述

除了这三种.还有一种基于MQ消息队列的方式实现异步
这里的异步.可以使Service层实现异步. 也可以是Controller层实现异步

MQ架构

在这里插入图片描述

MQ适用场景

1. 异步处理
2. 流量消峰填谷
3. 解耦微服务

MQ的选择

手记
案例

搭建MQ

手记

模式

1. 单Master模式
2. 多Master模式
3. ... ...

参考地址

RocketMQ 包含NameServer和Broker模式

搭建RocketMQ控制台

基于SpringBoot的控制台

手记

Rocket术语概念

在这里插入图片描述

Rocket进阶

适用Spring编程模型进行消息驱动的微服务.可以使用各种MQ.比如:kafka,ActiveMQ

RocketMQ开发指南

Spring 消息编程模型-生产者

1. ➕依赖
<dependency>
	<groupId>org.apache.rocketmq</groupId>
	<artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>
2. 写配置
rocketmq:
  name-server: 127.0.0.1:9876
  producer:
    # 小坑,必须指定group
  group: test-group
3. 适用RocketMQ
@Autowrited
private RocketMQTemplate rocketMQTemplate;
4. 发送消息到RocketMQ
rocketMQTemplate.convertAndSend(
	UserAddBoundMsgDTO.builder()
	.userId(share.getUserId())
	.bonus(50)
	.build(0);
)

在这里插入图片描述
总结:

想要使用 各种MQ在Spring中进行编程的话. 可以这么使用:SpringBoot整合了各种各样的MQ:

在这里插入图片描述

编写RocketMQ消费者

1. 实现RocketMQListener<T>:T为消息体. 生产者传递的消息体
2. 实现 onMessage(T t):
	当有消息来临的时候 进行处理的逻辑
3. 在实现类上,添加@Service: 将此类交给Spring管理
	添加@RocketMessageListener(consumerGroup = "test-group", topic = "add-bonus")
	add-bonus: 此处的add-bonus需要和生产者的数据一致
	cnsumerGroup: 消费者的Group. 
	生产者Group配置在yml文件.  消费者Group配置在注解中

在这里插入图片描述

分布式事务01

问题:

在Service层添加 @Transaction(rollbackFor = Exception.class)
如果发生Exception异常.数据库操作就回滚.

在这里插入图片描述

但是,在第四步,将数据写入缓存中发生了异常. 这时,数据库操作会回滚. 而MQ消息已经发送了. 导致用户积分
还是会增加.

导致审核没有成功,用户却增加了积分的情况.

事务消息

RocketMQ提供事务消息.

在这里插入图片描述

1. RocketMQ发送半消息	
2. 消息存储到MQServer内. 但是不能投递. 消费者不会接触阵容调休息.
3. 生产者执行本地事务. 
4. 生产者执行成果. 发送二次确认请求. 如果该请求将该请求标记为Commit. 消费者会接触到该请求
5. 如果接收到的是rollback,就将这条消息删除掉.
6. 如果长时间,半消息 为 未消费状态. 就去查询本地事务状态.
7. 生产者根据本地事务执行结果.

简单来说,就是一条消息由生产者发送.等待生产者执行本地事务. 如果本地事务执行成功的话.再消费这条消息

分布式消息状态

在这里插入图片描述

分布式事务编码

1. 发送半消息

新增表:

在这里插入图片描述

调用事务消息方法

rocketMQTemplate.sendMessageTransaction()
... ...

sendMssageTransaction:源码:

public TransactionSendResult sendMessageInTransaction(String txProducerGroup, String destination, Message<?> message, Object arg) throws MessagingException {
        try {
            TransactionMQProducer txProducer = this.stageMQProducer(txProducerGroup);
            org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(this.objectMapper, this.charset, destination, message);
            return txProducer.sendMessageInTransaction(rocketMsg, arg);
        } catch (MQClientException var7) {
            throw RocketMQUtil.convert(var7);
        }
    }

在这里插入图片描述

消息体: 还可以设置头部信息.例如 Header中存储TransactionID ==> UUID.randomUUID().toString();

在这里插入图片描述

实现RocketMQLocalTransactionListener

@Slf4j
@RocketMQTransactionListener(txProducerGroup = "tx-add-bonus-group")
public class MyRocketTransaction implements RocketMQLocalTransactionListener {
    /**
     * 执行本地事务
     * @param message
     * @param o
     * @return
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
        return null;
    }

    /**
     * 本地事务检查方法
     * 检查本地事务是否执行成功
     * @param message
     * @return
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
        return null;
    }
}
注解上的 txProducerGroup 的值,需要和 调用发送 事务型消息方法 中参数一致的值.

在这里插入图片描述
实现逻辑:

这里的逻辑大部分是 自己业务的逻辑. 需要调用的 RocketMQ 的API 的是,自己业务验证完毕.需要对事务型消息进行COMMIT或ROLLBACK.

在这里插入图片描述

这里的 RocketMQLocalTransactionState.COMMIT. 便是对 事务型消息提交. 确认,更改标记. 可被消费者消费
在catch中还有 ROLLBACK

SpringCloudStream

提供了更为通用操作MQ的方法. 用于构建消息驱动的微服务框架.

在这里插入图片描述

框架,Binder整合了Rocket,Rabbit各种MQ操作.

Stream编程模型

在这里插入图片描述

1. Destination Binder: 目标绑定器.: 与消息中间件通信的组件
2. Destination Bindings: 目标绑定
	2.1 Binding是连接应用程序和消息中间件的桥梁.用于消息的消费和生产. 由binder创建
3. Message: 消息

上图:

微服务集成了 Stream. Stream的Application创建了俩个Binging. 左边的Binding连接了
RabbitMQ. 右边的Binding连接了Kafka.
左边的RabbitMQ是Input. 右边的Kafka是Output
Input:微服务接收消息.
Output:微服务发送消息
图中的细致代码:

在这里插入图片描述

Stream:生产者

1. ➕依赖:

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>

2. 写注解:

@EnableBinding(Source.class)
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = UserCenterFeignConfiguration.class)
public class UserCenterApplication {

	public static void main(String[] args) {
		SpringApplication.run(UserCenterApplication.class, args);
	}

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}

}
@EnableBinding(Source.class) :在启动类上添加此注解

3. 写配置:

spring:
	cloud:
		stream:
			rocketmq:
				binder:
					name-server: 127.0.0.1:9876
			bindings:
				output:
					# 用来指定topic
					destination: stream-test-topic

Stream发送消息

@Autowired
private Source source;

@GetMapping("/test-stream")
public String sendStream(){
   source.output().send(
           MessageBuilder.
                   withPayload("消息体").
                   build()
   );
   return "success";
}

Stream:消费者

为用户中心整合Stream

1. ➕依赖
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
2. 写注解
@EnableBinding(Sink.class)
@SpringBootApplication
public class UserServiceApplication {

	public static void main(String[] args) {
		SpringApplication.run(UserServiceApplication.class, args);
	}

}
3. 写配置
spring:
	cloud:
		stream:
			rocketmq:
				binder:
					name-server: 127.0.0.1:9876
			bindings:
				output:
					# 用来指定topic
					destination: stream-test-topic
					# 一定要设置,要不应用不能启动.
					# 使用RocketMQ必须要设置.其他MQ可留空
					group: binder-group
4. 写代码
@Slf4j
@Service
public class TestStreamConsumer {

    @StreamListener(Sink.INPUT)
    public void receive(String messageBody){
        log.info("通过Stream收到消息了,收到的messageBody = 为:{}",messageBody);
    }

}

Stream:自定义接口-生产消息

public interface MySource {

    String MY_OUTPUT = "my-out-put";

    @Output(MY_OUTPUT)
    MessageChannel output();

}

使用:

@Autowired
    private MySource mySource;

    @GetMapping("/test-customer-interface")
    public void testCustomerInterface(){
        mySource.output().send(
          MessageBuilder
          .withPayload("自定义接口信息")
          .build()
        );
    }
使用起来跟之前的Source的使用方法一致
这里在学习时,mybatis报了一个错.原因是因为: mybatis将这个接口也扫描到了.但是这个接口并不是Mybatis的配置类
修改 mybatis扫描包即可

Stream:自定义接口-消费消息

1. 创建接口
public interface MySink {

    String MY_SINK = "my_input";

    @Input(MY_SINK)
    SubscribableChannel input();

}

自定义servier

@Slf4j
@Service
public class MyTestCustomerStream {

    @StreamListener(MySink.MY_SINK)
    public void receive(String messageBody){
        log.info("自定义接口消费:通过stream收到了消息:messageBody = {}",messageBody);
    }

}

透过现象看本质

在这里插入图片描述

Source,Sink Spring提供的接口.和我们写的自定义接口,MySource和MySink. 本质是一样的. 而 Processor接口是
能生产,能够消费的接口.
而yml文件的配置:其中有俩个属性和我们自定义的属性是一致的
cloud:
    stream:
      rocketmq:
        binder:
          name-server: 127.0.0.1:9876
        bindings:
          input:
            destination: stream-test-topic
            group: binder-group
          my-input:
            destination: stream-my-topic
            group: my-group
配置中的my-input和代码中的
public interface MySink {

    String MY_SINK = "my-input";

    @Input(MY_SINK)
    SubscribableChannel input();

}
是一致的.这是因为 Spring 使用IOC技术. 在启动类上书写 MySource.class 和 MySink.class .
会根据IOC创建 名为: my-input 和 my-output 的代理对象

消息过滤

手记

消费者消费消息 某种条件消费. 

CloudStream监控

SpringBoo-actuator

以actuator进行监控.

在这里插入图片描述

CloudStream异常处理

手记

错误处理:
	应用处理		系统处理

CloudStream+RocketMQ 重构生产者

// TODO 未学习

CloudStream+RocketMQ 重构消费者

// TODO 未学习
发布了36 篇原创文章 · 获赞 1 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/DXH9701/article/details/103956598