分布式事务的完整架构图
案例场景分析
案例一:用RestTemplate演示(不可靠生产,会出现问题)
创建一个订单模块
创建一个OrderDataBaseService服务
创建一个order的service服务,调用saveOrder()方法
创建一个运单模块
创建一个运单的controller层,定义一个lock接口
创建一个运单的service层,定义一个dispatch()方法,用来controller调用
定义一个测试用例
运行成功之后数据库会保存数据
首先是运单表保存数据
订单表保存数据
现在就来模拟分布式事务产生的错误问题
模拟分布式事务问题:在订单模块OrderService里面的dispatchHttpApi()方法中使用RestTemplate远程调用接口,这里模拟一个连接超时的错误,定义的是连接时间不能超过3秒
接下来去运单模块的controller层的lock()接口中,让他睡眠3秒,来达到订单模块一个错误问题
使用这个测试用例
启动之后就会在订单服务出现一个错误
查看数据库,运单表数据正常
但是订单表没有保存数据 ,因为在订单服务出现读以超时的错误,在createOrder()方法上面定义了事务的注解,出现错误的时候,进行了回滚。
就此,分布式事务问题就此产生。
案例二:使用rabbitmq解决
在OrderDataBaseService服务中,添加一个saveLocalMessage()方法,是用来记录消息是否发送,通过status这个字段判断
创建一个MQOrderService服务
创建一个OrderMQService服其中regCallback()方法是用来判断消息是否发送到交换机(rabbitmq是消息到了交换机之后,交换机自动发送给对应的对列)
在运单服务中创建一个OrderMqConsumer类,作为消费者,来监听order.queue对列
启动服务之后,使用这个测试用例
服务会一直报错如下错误:
消费者在消费的过程中出现异常,rabbitmq会出现什么问题和解决方案:
rabbitmq会出现死循环,死循环会造成我们服务的重试,若是个集群,那么就会重试每个集群,会把我们服务器冲垮,把我们rabbitmq的磁盘、内存消耗殆尽,让我们程序宕机
解决办法:1、控制重发次数 2、try+catch+手动ack 3、try+catch+手动ack+死信队列+人工干预
解法一:控制重发次数
解法二: try+catch+手动ack
OrderMqConsumer类的修改代码如下
使用try-catch的话,就不用在配置文件配置重试次数,就算配置意义不大,因为try-catch和重试次数互异,会使用不到重试次数
解法三:try+catch+手动ack+死信队列+人工干预(常用)
OrderMqConsumer类的代码如下
在订单服务里面配置rabbitmq的信息,配置死信队列
启动服务之后,使用案例二的测试用例,可以看见order.queue对列有一条信息
过了一会之后(此处时间是你配置rabbitmq刷新的时间,默认是5s)就会转移到dead.order.queue对列中,因为在服务端还是报错,rabbitmq就会把这条信息转移到死信队列里面存储
在运单服务中创建一个DeadMqConsumer类,用来消费死信队列信息
代码如下:
此处的channel.basicNack()方法的第三个参数为false,会直接把消息移除,不会在配置死信队列
具体的视屏参考这个RabbitMQ-39、高级-分布式事务-可靠生产和推送确认 | KuangStudy | 飞哥 | 狂神说 | 学相伴_哔哩哔哩_bilibili