组内现有通讯协议使用 steady_clock
作为时间戳,计算单个节点的耗时,某个特殊场景,用到了消息包自带的时间戳,自带的时间戳来自于其他机器,导致计算出来的耗时异常。
题话外:Gemini2.5 Pro 有希望彻底超越 GPT-4
问题排查
开始也没注意到底层时间戳计算的问题,就想着停掉所有服务,仅从本地接入,分析下日志。发现有个服务一直停不掉,持续的在发送业务数据,没辙,按照通讯端口抓包定位机器的位置。
sudo tcpdump -nni any -B 4096 -s 0 -w tmp.pcap port 13100
内部网络情况复杂,消息经过了代理转发,先从服务本机抓包,使用 tcpdump
抓取端口 13100 的数据包。然后切换到代理服务器上,抓取端口 13100 的数据包。
分析发现,耗时异常的请求,都来自深圳办公室,排查问题的服务,都部署在上海办公室。
steady_clock 和 system_clock 的区别
std::steady_clock
和 std::system_clock
是 C++ 中用于处理时间的两种主要时钟。它们有以下关键区别:
std::system_clock
- 代表“墙上时间” (Wall Clock Time):它表示的是系统范围内的、现实世界的时间。这个时间与操作系统显示的时间是一致的。
- 可以被调整:这个时钟的时间可以被用户或系统服务(如 NTP 网络时间协议)向前或向后调整。例如,如果你手动修改系统时间,或者系统与时间服务器同步,
system_clock
的值会发生跳变。 - 不适合测量时间间隔:由于它可能向后跳变,用它来计算两个时间点之间的时间差可能会得到负数或不准确的结果。
- 主要用途:获取当前的日历时间,用于需要与现实世界时间对应的场景(例如,日志记录时间戳)。
std::steady_clock
- 单调递增时钟 (Monotonic Clock):这个时钟从某个起点开始,只会稳定地向前移动,永远不会减少。它的速率可能是固定的,也可能不是(尽管通常是)。
- 不可被调整:
steady_clock
不受系统时间变化的影响。即使用户修改了系统时间,它也会继续稳定地向前走。 - 最适合测量时间间隔:由于其单调性,它是测量代码执行时间、超时等待等场景的最佳选择,可以保证结果的准确性。
- 起点不确定:它的起始时间点(epoch)通常是系统启动时,但这并没有被标准所保证。
不同机器上的 steady_clock
一样吗?
不一样。
steady_clock
的值在不同机器之间是没有可比性的。甚至在同一台机器的两次不同启动之间,它的值也是不可比的。
因为它的设计初衷就是为了在单次程序运行中精确地测量时间段,而不是为了表示一个绝对的时间点。它的起点(epoch)是未定义的,并且在不同的系统或不同的启动会话中几乎肯定是不同的。
总结
特性 | system_clock |
steady_clock |
---|---|---|
类型 | 墙上时钟 | 单调时钟 |
是否可调整 | 是,可以向前或向后跳变 | 否,只会向前移动 |
主要用途 | 获取当前日历时间 | 测量时间间隔、超时 |
跨机器/重启比较 | 可以(同步后) | 不可以 |
简单来说:
- 需要知道“现在几点了?”,用
system_clock
。 - 需要知道“这段代码运行了多久?”,用
steady_clock
。