Base 理论
BASE 是 Basically Available(基本可用),Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。是对CAP中AP的一个扩展
**基本可用:**分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
**软状态:**允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
**最终一致:**最终一致是指经过一段时间后,所有节点数据都将会达到一致。
BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。
**对于CP来说,**放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致。
**对于AP来说,**放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。
顺便一提,**CAP理论中是忽略网络延迟,**也就是当事务提交时,从节点A复制到节点B,但是在现实中这个是明显不可能的,所以总会有一定的时间是不一致。同时CAP中选择两个,比如你选择了CP,并不是叫你放弃A。因为P出现的概率实在是太小了,大部分的时间你仍然需要保证CA。就算分区出现了你也要为后来的A做准备,比如通过一些日志的手段,让其他机器回复至可用。
事务消息
比较有代表性的就是阿里开源的RocketMQ 和 去哪儿开源的QMQ, 两个消息队列都实现了事务消息功能,但是实现方式各有不同
关键名词:
因为消息发送是一个远程调用,由于网络的不稳定,无法和本地事务的执行处于一个原子操作中,针对这个缺点,RocketMQ基于两阶段提交协议做了如下改动:
第一阶段:生产者向MQ服务器发送事务消息(prepare消息),服务端确认后回调通知生产者执行本地事务(此时消息为Prepare消息,存储于RMQ_SYS_TRANS_HALF_TOPIC队列中,不会被消费者消费)
第二阶段:生产者执行完本地事务后(业务执行完成,同时将消息唯一标记,如transactionId与该业务执行记录同时入库,方便事务回查),根据本地事务执行结果,返回Commit/Rollback/Unknow状态码
1、服务端若收到Commit状态码,则将prepare消息变为提交(正常消息,可被消费者消费)
2、收到Rollback则对消息进行回滚(丢弃消息)
3、若状态为Unknow,则等待MQ服务端定时发起消息状态回查,超过一定重试次数或者超时,消息会被丢弃
事务状态定时回查:(避免后续commit 和 rollback 消息投递丢失)
在第二阶段中,生产者在本地事务执行完成后,需要向MQ服务器返回响应状态码,发送状态码的过程也是通过Netty发送网络请求,假设由于网络原因发送失败怎么办?本地事务已经提交/回滚了,但是Commit/Rollback状态码却没发出去,那么MQ服务器上这条prepare消息状态岂不是无法被投递/回滚
因此,MQ服务端会定时扫描存储于RMQ_SYS_TRANS_HALF_TOPIC中的消息,若消息未被处理,则向消费发送者发起回调检查,检查消息对应本地事务执行状态。从而保证消息事务状态最终能和本地事务的状态一致。上图中的5、6、7就是MQ服务端定时回查步骤。
QMQ 事务消息
总结:
介绍了两种事务消息,对于我个人而言,QMQ实现的方案能更加适应于大多数业务。但是这里要注意事务消息并不是所有的分布式一致性都能使用,事务消息使用的场景只能是发出这个消息就能代表这个操作成功的场景,什么意思呢?举个例子,比如我们支付的时候会扣积分,扣券等等,如果我发一个扣积分的消息能代表一定成功吗?这个肯定是不行的,因为用户的积分可能不够,就会导致扣除失败。如果是发送一个赠送积分的消息那么就可以代表成功,因为赠送积分是属于加法,并没有太多的限制。