开始前,会这些就够了
- 知道两个服务可以互发请求
假设服务甲记录“发送订单请求:100 毫秒”,服务乙记录“收到请求:90 毫秒”。合并日志按时间排序,接收竟跑到发送前面。最先要检查的是两台机器的时钟和记录方式,而不是宣布网络产生了负延迟。这是一组假设时间值,不是真实机器测量。
先分清你排的是什么时间
日志可以带事件发生时的机器时间,也可以带日志平台收到它的时间。前者可能受机器之间时钟偏差影响;后者还受到缓冲、批量发送和网络延迟影响。一条记录先出现在搜索结果里,只能说明某种排序或接收顺序,不能直接证明业务先发生。
同一条消息的发送必然先于它被接收,这是消息关系提供的证据,不依赖两台机器显示的数字是否一致。Lamport 的 happened-before 关系还包含同一顺序进程内部的先后,以及这些关系的传递。没有这样的关系时,不能硬给两个事件编造业务依赖。
逻辑时钟记录顺序,不记录毫秒
一种简单做法是给事件记递增计数。发送时携带自己的数,接收方把计数推进到比自己和消息中的数都大。下面让甲发送时记一,乙收到后记二;即使假设墙上时钟分别显示一百和九十,计数仍尊重这条消息的先后关系。
程序只有一条已知消息,整数值不表示经过了多少时间。甲到乙的逻辑计数增加一,并不能推出耗时一毫秒;用来算本机持续时间时,需要选择适合测量时长的时钟接口,不能拿这些逻辑计数相减当作延迟。
a_clock = 0
b_clock = 0
a_clock += 1
message_clock = a_clock
sent = {"event": "A send", "wall": 100, "logical": a_clock}
b_clock = max(b_clock, message_clock) + 1
received = {"event": "B receive", "wall": 90, "logical": b_clock}
events = [sent, received]
for key in ("wall", "logical"):
print(key, [e["event"] for e in sorted(events, key=lambda e: e[key])])
怎样运行:Python 3.10+;保存为 clocks_demo.py,运行 python clocks_demo.py。
运行后对照结果
wall ['B receive', 'A send']
logical ['A send', 'B receive']
数字较小,不一定就是另一个事件的原因
正确维护的 Lamport 时钟保证:如果甲事件在这种关系上先于乙,甲的计数就更小。反过来却不成立。一个完全没和甲通信的服务,也可能因为自己做了很多事而记到一百;不能据此说甲引起了它的事件。排序可以给出一种排列,却不自动补出因果证据。
并行执行的两个操作可能没有可比较的先后关系。为了展示,把它们按机器名或编号打破平局是合理的界面决定,但应该标明这只是显示顺序。把所有日志强制排成一列,有时反而会让读者误以为每一行都依赖上一行。
用请求关系把日志连起来
实际排查时,把请求标识、发送与接收的消息标识、处理片段的父子关系和机器时间一起看。W3C Trace Context 规定 traceparent 等字段,用于跨服务传递追踪身份。它帮助关联同一链路,但本身不是时钟同步协议,也不保证所有服务都记录了完整日志。
若一条链路缺少中间记录,要保留“不知道”的部分,不要用相邻时间强行补齐。先确认日志中的事件究竟代表发送前、发送完成还是业务提交,再画出能够证明的依赖线。这样的图可能没有唯一总顺序,却更接近系统实际发生的事情。
记住这三点
- 机器时间排序和日志收集顺序都不等于完整业务先后。
- Lamport 时钟保持已知先后关系,但逻辑数值不是耗时。
- 逻辑计数较小不能反向证明因果,需要消息或执行关系。
别把例子用过头
- 示例是两事件模型,未实现分布式时钟、消息重试或追踪系统。
- happened-before 表示可影响的顺序,不是对业务根因的自动证明。
- 没有日志不等于事件没发生,采样与丢失都需要考虑。
自己试一小步
加入一个没有和甲乙通信的服务丙,能否只凭逻辑时钟把它放进这条因果链?
- 为丙构造两个独立事件,并设置递增计数。
- 分别按机器时间与逻辑计数展示所有事件。
- 只为确有消息或本地先后证据的事件画箭头。
做到这些,就算完成
- 不把展示顺序当成因果关系。
- 能说明逻辑计数无法计算网络耗时。
- 明确哪些事件之间仍然无法确定先后。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- Time, Clocks, and the Ordering of Events in a Distributed System (新标签页)↗
1978 年论文第 2–3 页支持 happened-before 定义、消息先后、逻辑时钟条件,以及反向推断不成立。
- Trace Context (新标签页)↗
支持 traceparent 的 trace-id、parent-id 与上下文传播;用于说明链路关联而非时钟同步。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
OpenTelemetry 官方:Java 自动插桩与遥测 (新标签页)↗
只追踪一条请求的父子调用与日志对应关系,先用关联信息连接事件,再检查各机器时间是否适合直接比较。
查看学习准备与访问说明 →