思考(六十三):微服务架构中的时序问题

问题

微服务架构中调用关系异常复杂,容易发生时序问题,而导致系统漏洞

比如,下面的时序图,玩家先买一个道具,然后进副本:

ShopService BattleService MoneyService 1.1 查询 Gold 1.2 购物处理 1.3 扣除 Gold 2.1 查询 Gold 2.2 进入条件检查 ShopService BattleService MoneyService

考虑到外挂、玩家点快了、网络异常等,有可能会导致 2.1 比 1.3 先访问 MoneyService,导致系统漏洞

解决思路

把玩家的每次操作,均视为一次事务

玩家当前事务正在执行过程中时,新请求挂起,而不是并发去执行

因此涉及 2 块编码:

  1. 如何追踪微服务调用链,并做成事务
  2. 入口服务(如 gateway)对事务做处理

第 2 个问题简单

第 1 个问题也有现成的参考或直接拿来用。可以搜索微服务全链路跟踪

微服务全链路跟踪

下面引用下 opentracing 中的一些图解,展示微服务全链路跟踪的过程
摘自 https://opentracing.io/specification/

Causal relationships between Spans in a single Trace


        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C is a `ChildOf` Span A)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)

Sometimes it’s easier to visualize Traces with a time axis as in the diagram below:

Temporal relationships between Spans in a single Trace


––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|> time

 [Span A···················································]
   [Span B··············································]
      [Span D··········································]
    [Span C········································]
         [Span E·······]        [Span F··] [Span G··] [Span H··]

上面 2 副图,解释了微服务全链路调用实际上是个directed acyclic graph (DAG)

directed acyclic graph (DAG) 单向无环图。用程序员的话来说就是,DAG 这丫不会死循环,且可以回溯回 root 节点

因此可以放心做全链路跟踪啦

golang 微服务全链路编码思路

如果微服务间通信都是用 grpc 这种,那么直接拿 opentracing 做下应用就可以了

如果微服务间基础通信模块写的比较奔放,结合 golang 特点 ,最优雅的实现,就用 context 来传递事务ID

自己实现大致思路如下:

  1. 网络层接口支持 context 参数
  2. 网络层接口内部自动序列化 context 参数,并底层自动封包进网络数据
  3. 网络层接口内部自动解析网络数据,并重新生成 context 传递给上层接口
  4. 入口服务(如 gateway)给 context 设置事务ID

其他问题

微服务应用,通常都是针对用户的,比如游戏,绝大多数都是针对具体玩家的操作

这些范畴内,把微服务全链路看成一个事务都是 OK 的

但也有些例外的操作,比如全服型的,某服务自动触发的更新某玩家数据的操作。这种都无法通过入口服务(如 gateway)来让事务串行化

在微服务架构中,通常建议的做法都是服务被动处理事务

某服务自动触发的更新某玩家数据的操作基本上都会有代替做法,把它变成通过入口服务(如 gateway)来让事务串行化

这部分内容有待深究

以上

猜你喜欢

转载自blog.csdn.net/u013272009/article/details/104525447