交易系统的消息服务如何保证100%可靠

栏目: 数据库 · 发布时间: 6年前

内容简介:分布式应用中,消息系统被大量使用,主要原因有:发送方和接收方不需要相互知道对方,一个只管发,一个只管收,大大简化了处理逻辑。如果发送方发送速度快于接收方的接收速度,消息系统就可以暂时将无法处理的消息缓存起来,让接收方慢慢处理。

分布式应用中,消息系统被大量使用,主要原因有:

逻辑解耦

发送方和接收方不需要相互知道对方,一个只管发,一个只管收,大大简化了处理逻辑。

适配动态流量

如果发送方发送速度快于接收方的接收速度,消息系统就可以暂时将无法处理的消息缓存起来,让接收方慢慢处理。

没有消息系统时,发送方就不得不配合接收方降低处理速度,从而拖慢了整个系统的性能。

那么消息系统能保证消息100%可靠到达吗?

答案是否定的。

因为消息系统是网络调用,只要涉及到网络,就不可能100%可靠,因为通信双方不可能无限次给对方发ACK确认。

那么消息系统如何尽可能保证消息的可靠达到呢?

一般来说,消息系统可以实现3种消息传输模式:

  • At least once;
  • At most once;
  • Exactly once.

这3种模式分别是:

  • 消息保证至少发送成功一次,也就是可能会重复发送;
  • 消息只保证最多发送一次,那就是要么成功,要么失败;
  • 消息保证发送成功且仅发送成功一次,这种理想情况基本不存在,也没有任何基于网络的消息系统能实现这种模式。

所以大部分消息系统都按照At least once来设计。

但是并不是说消息系统就能保证所有消息能100%可靠达到,只要是网络,就存在丢消息的可能性。

如果涉及到交易系统这类绝对不能丢消息的应用,怎么才能保证100%不丢消息,并保证所有消息处理一次且仅处理一次?

首先我们要排除分布式事务消息,因为这种模式不但对数据库提出了XA两阶段提交的要求(需要昂贵的商用数据库),还对消息服务器提出了XA的要求(只有少数如WebLogic的JMS提供此功能),并且性能十分差劲。

而非事务型消息系统,如ActiveMQ、RabbitMQ、Kafka等,不保证100%可靠性。

涉及到交易系统的订单消息,如果一个都不能丢,通过非100%可靠的消息系统,如何保证100%的可靠性?

仅仅依靠消息服务是无法保证的,我们必须在设计上做出更多的容错和自动恢复的机制,来保证100%的可靠性。

以定序服务为例,如果订单已经持久化到数据库中,并且经过定序,下一步,如何保证定序后的订单通过消息发送给撮合服务100%可靠?

解决方法需要从发送方和接收方同时考虑。

考虑接收方处理消息的逻辑,首先要保证接收方能处理重复消息,因此需要对每个订单的消息进行编号,也就是给每个消息标记一个递增的ID(只需要递增,不一定要求连续),这样,接收方维护一个当前ID,凡是收到比当前ID小的消息,直接丢弃。

但是,如果一个消息序列例如 A-B-C-D 在发送过程中丢掉了某个消息,变成了 A-B-D ,接收方如何能检测出丢失?

除了给每个消息附上一个唯一递增ID外,只需要发送方同时给每个消息附加上一条消息的ID,就可以形成一个微型“区块链”,利用这个链表,接收方很容易识别出漏掉的消息。

如果接收方识别出消息遗漏,它应该怎么从该错误恢复呢?方法也很简单,只需向接收方暴露数据库接口,让接收方自己从数据库中根据ID读取漏掉的消息,就相当于接收方总是能有序且无遗漏地处理所有消息。

假设正常的消息流如下所示:

┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
id=1  id=2  id=3  id=4 
pid=0 pid=1 pid=2 pid=3
msg-A msg-B msg-C msg-D
└─────┘ └─────┘ └─────┘ └─────┘

由于接收方可以根据递增ID去重,因此,重复发送消息可以被正常处理:

┌─────┐ ┌─────┐ ┌─────┐ ╔═════╗ ╔═════╗ ┌─────┐
id=1  id=2  id=3  id=2  id=3  id=4 
pid=0 pid=1 pid=2 pid=1 pid=2 pid=3
msg-A msg-B msg-C msg-B msg-C msg-D
└─────┘ └─────┘ └─────┘ ╚═════╝ ╚═════╝ └─────┘

如果消息出现丢失:

┌─────┐ ┌─────┐     ┌─────┐
id=1  id=2          id=4 
pid=0 pid=1       pid=3
msg-A msg-B         msg-D
└─────┘ └─────┘     └─────┘

那么,接收方只需要根据当前ID去数据库查询,直到读取到最新的ID为止:

┌───────────┐      ┌───────────┐      ┌───────────┐
  Sender   │─────>│    MQ     │─────>│ Receiver  
└───────────┘      └───────────┘      └───────────┘
                                           
                                           
                                           
                                           
                  ┌───────────┐            
      └───────────>│ Database  │<───────────┘
                   └───────────┘

整个过程中,极少量消息丢失不会对系统的可用性造成影响,这样就极大地减少了系统的运维成本和线上排错成本。

对于那些不太需要100%严格有序的消息队列,例如清算消息,就不需要这么复杂的设计,超时重发+定时扫描未处理的消息就足够了。

最后,无论是发送方还是接收方,为了提高收发消息的效率,应该总是使用批处理的方式。测试显示一次收发一条消息和一次收发10条消息时间上并无明细差异,而发送方采用batch落库+batch发送可以显著地提高TPS,当然,这需要消息服务器支持batch模式。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

x86汇编语言

x86汇编语言

李忠、王晓波、余洁 / 电子工业出版社 / 2013-1 / 56.00元

《x86汇编语言:从实模式到保护模式》采用开源的NASM汇编语言编译器和VirtualBox虚拟机软件,以个人计算机广泛采用的Intel处理器为基础,详细讲解了Intel处理器的指令系统和工作模式,以大量的代码演示了16/32/64位软件的开发方法,介绍了处理器的16位实模式和32位保护模式,以及基本的指令系统。 《x86汇编语言:从实模式到保护模式》是一本有趣的书,它没有把篇幅花在计算一......一起来看看 《x86汇编语言》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具