能坚持别人不能坚持的,才能拥有别人不能拥有的。 关注
编程大道
公众号,让我们一同坚持心中所想,一起成长!!
如何保证分布式系统中接口调用的顺序性?
分布式是当下比较流行的一个话题,很多大型的互联网公司都是分布式系统,将一个大而全的系统拆分成多个小而精的一个个的功能单一、职责集中的子系统,系统之间通过约定好的协议、规则进行调用,降低系统之间的耦合度,避免牵一发而动全身。
虽然分布式系统的架构有很多的好处,但不得不说它也存在很多需要特别注意的问题。我们今天要讲的分布式系统中接口的调用顺序,就是其中一个很常见的问题。
一般来说,我们多个接口的调用是不用保证顺序的,但是有的时候,有的业务场景可能确实是需要严格的顺序来保证系统的准确性。
举个例子,分布式架构中的服务A调用服务B,发了两个请求,一个插入操作一个删除操作,本来是先插入再删除。但是很可能俩请求过去了,集群部署的情况下落在了不同机器上,可能插入请求因为某些原因执行慢了一些,导致删除请求先执行了,此时因为没数据所以没有啥效果没有啥影响;接着这个时候插入执行完了,好,把数据插入进去了,这不就完全错了嘛。
本来应该是插入 -> 删除,最终这条数据应该没了,结果现在是删除 -> 插入,导致最后数据还存在,然后你死都想不明白是怎么回事。你只能通过不同机器上的日志去看,费半天劲去查,最后比对俩操作的执行时间,可能最后也能查出来问题所在。
这,就是分布式系统中一个很常见的问题,那我们该如何保证接口的调用顺序呢?
首先,一般来说,我个人给你的建议是,你们从业务逻辑上最好设计的这个系统不需要这种顺序性的保证,因为一旦引入顺序性保障,我们就需要引入一些的别的、复杂的技术(如分布式锁)来保证,这样会导致系统的复杂度上升,而且会导致系统性能下降,吞吐量降低,热点数据压力过大等问题。
其次,如果不得不保证顺序性的话,下面给个我们用过的方案吧。
简单来说,首先你得用一致性hash负载均衡策略,将比如同一个订单id对应的请求都给分发到同一个机器上去。接着就是在那个机器上,因为可能还是多线程并发执行的,你就得将这个订单id对应的请求扔进一个内存队列里去,强制排队,这样来确保他们的顺序性。
如下图所示:
复杂点的,使用基于zookeeper的分布式锁来实现接口调用的强顺序性。
首先服务A发送的三个有序请求请求1、2、3,依次发送到消息对列,然后服务B的多个实例从消息对列消费。假如分别是三个实例拿到了1/2/3三个请求,那么当请求执行时需要小从zookeeper获取锁,才能执行。所以此时我们的服务A还要指明这三个请求的执行顺序,即seq=1/2/3,服务B才能知道执行顺序。
这时候三个请求都来获取锁,假如请求3先获取到锁,然后看Redis这个list是不是有比自己小的序号,有则释放锁。然后如果请求1拿到了锁,也去Redis判断是不是有比自己小的序号,一看没有,就执行请求1,然后从Redis的list里删掉这个序号。。。依次这样来获取锁->判断->删除redis里的序号。。。来保证接口的顺序性。
如下图所示:
你看看,上面为了保证接口调用的顺序性是不是又引入了很多复杂的技术,所以这样后续就会引发很多问题。比如说要是某个订单对应的请求特别多,造成某台机器成热点怎么办?这样系统的增加了系统的复杂度,同时系统的性能、吞吐量也会明显降低,引入zookeeper分布式锁这种很重的机制,势必会带来这些副作用。而解决这些问题又要开启后续一连串的复杂技术方案。。。
曾经这类问题弄的我们头疼不已,所以,还是建议什么呢?建议尽量避免引入顺序性,在系统设计时就避免这个问题,或者最好是像刚才那样一个订单的插入和删除操作,能不能合并成一个操作,就是一个删除,或者是什么,避免这种问题的产生。
欢迎一起学习交流。