因为SO_SNDTIMEO/SO_RCVTIMEO对connect()无效,其超时由TCP重传机制决定(约75秒),且用户态无法观测三次握手真实延迟;eBPF可钩住tcp_v4_connect和tcp_finish_connect精确捕获SYN发出与连接完成时间点。

为什么 getsockopt + SO_SNDTIMEO/SO_RCVTIMEO 不能统计真实连接延迟
很多人第一反应是用 socket 选项控制超时,再测 connect() 耗时——这只能反映「本机发起连接的耗时」,且受本地路由、防火墙、SYN重传策略干扰极大。真实服务端响应延迟(比如三次握手最后一个 ACK 到达时间)根本不在用户态可见范围内。eBPF 的优势就在这里:它能钩住内核协议栈中 tcp_connect、tcp_finish_connect 等关键函数,精确捕获 SYN 发出和连接建立完成两个时间点。
如何用 bpftrace 快速验证连接延迟采集逻辑
不用写 Go,先用 bpftrace 确认时间戳采集是否可靠:
sudo bpftrace -e '
kprobe:tcp_v4_connect {
@start[tid] = nsecs;
}
kretprobe:tcp_finish_connect /@start[tid]/ {
$delta = nsecs - @start[tid];
printf("pid=%d, delta=%d us\n", pid, $delta / 1000);
delete(@start[tid]);
}'注意几点:
-
kretprobe:tcp_finish_connect是更稳的选择,比tcp_rcv_state_process或inet_csk_accept更贴近连接真正完成的时机 - 必须用
/@start[tid]/过滤,避免进程复用 tid 导致误匹配 - 输出单位是纳秒,除以 1000 得微秒,再除以 1000 才是毫秒——别在 Go 里重复除
Go 程序中用 libbpfgo 加载 eBPF 并读取延迟直方图
核心不是“怎么写 eBPF C”,而是怎么让 Go 安全读取 map 数据。推荐用 libbpfgo(不是 ebpf-go),因为它直接绑定 libbpf,对 perf event array 和 hash map 的支持更成熟:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- eBPF 端用
BPF_MAP_TYPE_HASH存struct { pid_t pid; __u64 start_ns; },键为pid_t;Go 端用Map.Lookup()按 PID 查起始时间 - 用
BPF_MAP_TYPE_PERF_EVENT_ARRAY向用户态推送完成事件(含 PID + end_ns),比轮询 hash map 更低开销 - Go 中处理 perf event 时,必须用
PerfEventArray.Read()配合unsafe.Pointer解析结构体,字段顺序和对齐要和 C 端严格一致 - 别忘了在 Go 里调用
Map.DeleteKey()清理已处理项,否则 map 会涨满
实际部署时 tcp_connect 钩子被跳过的常见原因
不是所有 connect 都走 tcp_v4_connect:
- AF_UNIX 或 AF_INET6 连接不会触发该 kprobe;需同时挂
tcp_v6_connect - 容器内 netns 切换后,kprobe 默认只作用于 init_net;得用
bpf_program__set_autoload(prog, false)+ 手动 attach 到每个 netns(libbpfgo目前不自动支持) - 某些发行版内核(如 RHEL 8.6+)默认禁用
kprobes,需确认/proc/sys/kernel/kptr_restrict值为 0 或 1,且/proc/sys/kernel/perf_event_paranoid≤ 2 - 如果目标进程用
socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0),connect()可能立即返回EINPROGRESS,此时tcp_finish_connect仍会被调用,但时间点延后——这反而是你想要的真实建连延迟
最易被忽略的是:eBPF 程序里没做 if (sk->sk_family != AF_INET) return 0; 过滤,导致 IPv6 或 AF_UNIX 流量污染数据,而 Go 侧又没校验 key 类型,最终读到全零值还当真延迟处理。

















