如何保证数据一致性,事务消息如何实现?
一条普通的MQ消息,从产生到被消费,大概流程如下:
1,生产者产生消息,发送带MQ服务器
2,MQ收到消息后,将消息持久化到存储系统。
3,MQ服务器返回ACk到生产者。
4,MQ服务器把消息push给消费者
5,消费者消费完消息,响应ACK
6,MQ服务器收到ACK,认为消息消费成功,即在存储中删除消息。
举个下订单的例子吧。订单系统创建完订单后,再发送消息给下游系统。如果订单创建成功,然后消息没有成功发送出去,下游系统就无法感知这个事情,出导致数据不一致。 如何保证数据一致性呢?可以使用事务消息。一起来看下事务消息是如何实现的吧。
1,生产者产生消息,发送一条半事务消息到MQ服务器
2,MQ收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
3,MQ服务器返回ACK确认到生产者,此时MQ不会触发消息推送事件
4,生产者执行本地事务
5,如果本地事务执行成功,即commit执行结果到MQ服务器;如果执行失败,发送rollback。
6,如果是正常的commit,MQ服务器更新消息状态为可发送;如果是rollback,即删除消息。
7,如果消息状态更新为可发送,则MQ服务器会push消息给消费者。消费者消费完就回ACK。
8,如果MQ服务器长时间没有收到生产者的commit或者rollback,它会反查生产者,然后根据查询到的结果执行最终状态。
如何保证数据的顺序消费
在KaRka中,Topic在单个分区的生产消费是有序的。通常我们申请多个分区是为了提高生产消费的吞吐量,但多个分区就会导致消费消息时无序。
保证顺序消费的方法有:
要想保证顺序消费,就必须要保证顺序消费的消息在同一个队列。
1.只申请1个分区;仅推荐在吞吐量低的顺序场景下用
2.这种场景申请多个分区,生产时使用消息Key;生产者发送消息时如果指定了Key,则这条消息会根据Key的Hash发送到对应的分区,也就是说带有相同Key的消息会被发送到相同的分区。(如果不携带Key的话是轮询发送到所有分区)
写 N 个内存 Queue,具有相同 key 的数据都到同一个内存 Queue;然后对于 N 个线程,每个线程分别消费一个内存 Queue 即可,这样就能保证顺序性。
顺序消费典型的应用场景:
1,用于同步数据库和redis之间的数据(单个消费者)
2,某些电商场景必须严格遵守消息的执行顺序,比如说待支付–已支付–开始发货–订单完成–评价。如果开始发货在已支付之前面执行,就会产生业务问题。
在使用消息key来确保消息发布到多个分区时,要注意key的hash函数,尽量避免大多数消息发布到一个分区,否则会出现流量倾斜。
消息中间件如何做到高可用?
以Kafka为例。
Kafka 的基础集群架构,由多个broker组成,每个broker都是一个节点。当你创建一个topic时,它可以划分为多个partition,而每个partition放一部分数据,分别存在于不同的 broker 上。也就是说,一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。
每个partition放一部分数据,如果对应的broker挂了,那这部分数据是不是就丢失了?那不是保证不了高可用吗?
Kafka 0.8 之后,提供了复制多副本机制来保证高可用,即每个 partition 的数据都会同步到其它机器上,形成多个副本。然后所有的副本会选举一个 leader 出来,让leader去跟生产和消费者打交道,其他副本都是follower。写数据时,leader 负责把数据同步给所有的follower,读消息时,直接读 leader 上的数据即可。如何保证高可用的?就是假设某个 broker 宕机,这个broker上的partition 在其他机器上都有副本的。如果挂的是leader的broker呢?其他follower会重新选一个leader出来。