问题
微服务架构中调用关系异常复杂,容易发生时序问题,而导致系统漏洞
比如,下面的时序图,玩家先买一个道具,然后进副本:
考虑到外挂、玩家点快了、网络异常等,有可能会导致 2.1 比 1.3 先访问 MoneyService,导致系统漏洞
解决思路
把玩家的每次操作,均视为一次事务
玩家当前事务正在执行过程中时,新请求挂起,而不是并发去执行
因此涉及 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
自己实现大致思路如下:
- 网络层接口支持 context 参数
- 网络层接口内部自动序列化 context 参数,并底层自动封包进网络数据
- 网络层接口内部自动解析网络数据,并重新生成 context 传递给上层接口
- 入口服务(如 gateway)给 context 设置事务ID
其他问题
微服务应用,通常都是针对用户的,比如游戏,绝大多数都是针对具体玩家的操作
这些范畴内,把微服务全链路看成一个事务都是 OK 的
但也有些例外的操作,比如全服型的,某服务自动触发的更新某玩家数据的操作。这种都无法通过入口服务(如 gateway)来让事务串行化
在微服务架构中,通常建议的做法都是服务被动处理事务
像某服务自动触发的更新某玩家数据的操作
基本上都会有代替做法,把它变成通过入口服务(如 gateway)来让事务串行化
这部分内容有待深究
以上