一般分布式系统接口的调用,不用考虑顺序性,但是某些特殊场景可能确实是需要严格的顺序保证。
比如,你的系统访问系统A,先插入数据再删除。假设现在俩个请求请求A系统时,落在不同机器上,可能插入请求因为某些原因执行慢了一些,导致删除请求先执行了,此时因为没数据所以没有起作用,结果这个时候插入请求过来了,数据插入进去了,那就造成数据不一致了。
本来应该是 “先插入 -> 再删除”,这条数据应该没了,结果现在变成 “先删除 -> 再插入”,数据还存在。
这就是分布式系统一些很常见的问题。
图1 分布式系统用户多个请求
解决方案
首先呢,从你系统设计来看,能不要这种接口请求顺序性的保证要求就不要,因为一旦引入顺序性保障,比如就会导致系统复杂度上升,带来系统性能降低,热点数据压力过大等问题。
如果你一定要引入这样的功能,也是有解决方案的。
如图1所示,我们可以在系统A之前加入一个接入服务,来自同一个用户的每个请求都带一个id,比如orderId,接入服务可以根据orderId做hash取模,把相同取模结果的请求分发到同一台机器上。
图2 接入服务分发请求
用户发起了3个请求,接入服务对orderId取模后,请求按请求1,请求2,请求3的顺序分发到了同一台机器上。
但是这里有一个问题,假如系统A里是多线程的处理请求,那么还是会导致乱序,所以系统A内部也需要搞一个内存队列,使请求有序化。
图3 系统A的内存队列
系统A内部创建多个内存队列,基于同样的机制,对orderId取模,取模后值相同的请求分发到同一个内存队列。如图3中的3个请求分发到了同一个内存队列,每个内存队列有一个线程去消费。
但是这样引发的后续问题就很多,比如说要是某个订单对应的请求特别多,造成某台机器成热点怎么办?解决这些问题又要开启一连串的复杂技术方案。
所以能不能从其他角度考虑一些解决方案,比如一个订单的插入和删除操作,能不能合并成一个操作,或者是其它的方法,避免这种问题的产生。
总结:
上面的解决方案并不能100%保证接口请求的顺序性,用户发起的3次请求,可能由于网络的原因,不是顺序性的到达接入服务。比如用户发起的是请求1->请求2->请求3,到达接入服务的顺序可能是请求2->请求1->请求3。
同样的道理,接入服务转发请求到系统A的请求,也可能会乱序。
所以上述方案只能99.99%的保证接口请求顺序性。
如果想100%的保证接口请求顺序性,那么就需要使用分布式锁了,有兴趣的读者,可以自行研究下如何落地。
此时用户发起的请求,必须要带上请求的顺序号,如图:
图3 请求带顺序号
系统中使用分布式锁这种很重的技术方案,对性能影响非常大,也会大大增加系统的复杂度,如果可以,尽量不要使用。
END
点赞和在看就是最大的支持❤️