场景:
订单系统和运单系统
点外卖,用户下单 ,下单后触发运单系统生成运单
分布式系统:一个请求,由多个系统协同处理完成
微服务只是架构分布式系统的一种方法
订单系统,调用运单系统,
程序分析:
使用RPC远程过程调用,实现服务直接交互
分布式事务涉及:CAP |BASE
理论是实践之后的总结
尽管加了事务@Transaction,可是依然会发生如下异常:
订单失败,而运单依然有数据产生
原因是:运单系统内部处理的时候模拟耗时3秒,超过了请求时设置的超时时间2秒,所有调用方认为没有结果返回就认为异常了,
而其实运单服务还正常运行中。所以运单系统有了数据,而订单系统因为回滚,所以没有数据
需要找一个中间载体来规避上述这些问题:
这个载体需要具备的特性:
载体选择:自研、MQ、Redis、zookeeper、数据库
- 自研:seata |lcn|TCC各种框架研发
- Redis、zookeeper[可靠,redis、zookeeper主动内存型,数据不保证丢失]
- 数据库--数据库集群高可用
- MQ--本质:和redis 、zookeeper本质一样, 数据存取
特点:没有要求结构化的数据
还没有开箱即用的开源方案。针对自身的业务场景,进行定制化的解决
演示一种MQ的解决方案:
消息可发发送
可能的异常
- MQ发送失败
- mq调用成功,但是订单事务提交失败 --解决办法:mq操作和订单事务分离,把事务注解加到saveOrder方法上去,这样就保证了,order一定插入到数据库,才会去执行发送消息队列,如果order数据插入异常,则直接回滚,不去执行发送消息队列,保证了插入order数据和消息队列同时发生,或者同时失败
- mq调用网络不通【重发----】
实现方式:新建一张本地消息tb_distribute_message表。【里面需要有一个业务消息的唯一id,保证幂等】在插入订单的同时,插入一条本地消息,并标记为未消费,标记为已消费的节点是,mq的ack机制,发送回执的时候
-
Mq有ack机制[需要自己去开启],发送以后有回执,就消息会发送到一个名字叫做orderQueue的队列中去,会触发一个方式confirimCallback可以修改状态,
- 定时任务schedule可以去检查这张表,消息是否发送,这样就可以重发了
- 修改消息表的状态为已发送
- mq收到请求,突然断开网络,实际MQ已经存储消息【如何知晓MQ是否收到消息】
- MQ本身可靠性如何保障【自带高可靠方案,副本】
消息可靠消费
1.运单系统配置rabbitMQ
从之前创建的队列orderQueue中取值
1.先创建队列
这里涉及幂等(重复),
产生的场景:mq ack之后,返回消息给订单系统,,此时订单系统挂掉了,没有收到消息,本地消息表中消息状态仍然是未发送,这样定时任务去扫消息表的时候,发现未发送,于是又触发一次发送操作
运单表主键直接就是order_id
要有一个业务标记,能够识别是否为同一条数据。记录已经处理的数据(比如用redissetNx)
还有问题,就是运单系统消费消息,处理中途,运单系统挂掉了。也就是没有可靠消费==消费中途出了问题
可是ack是自动发送的,并不考虑消费的结果(是否可靠消费)。此时消息也被消费了。也不能再次触发了
所以此时应该修改自动ack为手动(代码控制),并做后续的业务处理
看上面的代码messageConsumer中处理
requeue = true标识重发
requeue = false标识不重发
两个系统之间交互,用MQ消息队列
用RabbitMQ,因为和spring同一个母公司,集成非常好
有Message Queue| 有topic/queue的概念 多个queue
mq一般都是自动ack,容易出问题,
需要改为手动ack.当消息正常处理后,告诉mq删除
channel.basicAck(false);
//如果消费者突然断电或者进程kill,MQ检测到连接断开,会向其他消费者重复
//出现可控的异常,告诉MQ,你帮我重发--根据具体的业务系统设计
//出现未知异常,运维日志监控,预警机制
channel.asicNack(tag,false,"requeue":false);//丢到了垃圾堆,回收站可以找到
CAP=就是三选二
多系统之间肯定是通过网络来交互,网络是不可靠的
分区容错:多个系统之间不能交互,仍然要对外提供服务
一致性:订单系统和运单系统数据一致
可用性:
是对其中某一个部分做出一些牺牲,不是完全不能兼顾,要做一些取舍
对于大数据量并发
生产者可以分布式部署
消费者分组group概念
所有不用担心数据量大的问题
基于分区
Base理论:就是对CAP理论的扩展【基本可用】通过修改状态来实现回滚
SAGA理论,每一个模块都很好的工作,保证
Seata开源分布式解决方案对之前这些操作封装