eBPF 零侵扰分布式追踪 3 分钟锁定 Java 程序 I/O 线程阻塞

原创
09/25 10:09
阅读数 1.6K

摘要: I/O 线程阻塞是Java 程序经常出现的问题之一,此类故障发生时 Java 程序的请求、响应在 I/O 线程向操作系统 Socket Buffer 读/写过程中发生阻塞,由于在业务代码插桩无法观测到 I/O 线程的工作情况和性能表现,因而导致故障非常隐蔽和难以诊断定位。通过本篇案例您将了解到,某银行的开发工程师如何使用 eBPF 技术带来的零侵扰追踪能力,在某次分布式核心交易系统上线信创平台的非功能测试(性能压测)故障诊断中,用 3 分钟时间锁定 Java 程序 I/O 线程阻塞。

0x0: 故障背景

近期,某银行分布式核心交易系统 XX 中心业务按计划即将上线华为信创云 ,但在上线前的非功能测试(性能压测)过程中,业务响应时延不定时劣化,原因不明。

XX 中心的业务程序使用 Java 语言开发(SOFARPC 框架),采用 Netty 作为网络框架,该业务流程涉及 ClientGatewayApp-1App-2,端到端业务访问路径如下:

  • 1)Client 使用 SOFARPC 协议访问 Gateway 的接口;
  • 2)Gateway 经过七层负载均衡将 SOFARPC 协议的 Request 转发至某个 App-1
  • 3)App-1 访问 App-2

业务流程及 DeepFlow 观测点

DeepFlow 通过 eBPF 以零侵扰 的方式对每一次请求 经过每一个位置 时( 观测点 1~8 )的性能进行全链路 采集和观测,整个过程对应用程序无需搬运或注入代码 ,也无需任何重启操作

0x1: 故障诊断

在 DeepFlow 可观测性平台使用 "5W 方法" 对故障快速诊断。

故障诊断的 “5W 方法”

什么是 "5W 方法"?

从宏观到微观有序调阅可观测性数据,逐步回答如下 5 个问题,可以快速、有效诊断出问题根因,称之为"5W 方法":

  • Who is in trouble?
  • When is it in trouble?
  • Which request is in trouble?
  • Where is the root position?
  • What is the root cause?
  1. 确定首个观测对象

故障诊断的第一步 ,首先要回答 "Who is in trouble" 的问题,从而确定首个观测的对象。

此次性能压测的故障诊断场景中,已明确知道 Gateway ------> App-1 路径的响应时延存在异常,因此将其作为首个观测对象,在 DeepFlow 的「追踪」-「路径分析」功能入口检索该路径。

Who is in trouble?

  1. RED 指标监控,找出时间点

故障诊断的第二步 ,要回答 "When is it in trouble" 的问题。

打开 Gateway ------> App-1 路径的「右滑窗」,在其中观测 RED 指标(请求量、错误率、响应时延)。

由于分布式核心系统的业务等级非常重要,因此选择 观测响应时延指标中的"最大时延 ",并找出问题发生时间点

通过分析该路径的最大时延,我们发现在压测阶段周期性出现慢响应(大于 2 秒),于是我们可以将右滑窗的观测时间范围缩小到问题时段(20:20~21:00)。

When is it in trouble?

  1. 调用日志检索,找出慢响应

当确定问题发生时间段之后,第三步 便要回答 "Which request is in trouble" 的问题。

在「右滑窗」的「调用日志」中过滤或排序,在问题时段找到十余次慢响应请求。

Which request is in trouble?

  1. 调用链追踪,找出根因位置

找到慢响应的请求列表后,第四步 便可以通过调用链追踪,回答 "Where is the root position" 的问题。

选取其中一次约 2s 的慢响应请求做「调用链追踪」,经过追踪发现在观测点 8 的左侧存在约 2s 的空缺,说明:

  • 观测点 7App-2 的 Pod 网卡)已及时收到 Request;
  • 观测点 8App-2 的应用进程)等待约 2s 才发起 read() 读取该次 Request。

因此,根因位置是 App-2 的应用进程。

Where is the root position?

  1. 根因分析

第五步 ,对根因位置进行分析,回答 "What is the root cause" 的问题。

为什么 App-2 在网卡已收到 Request 后约 2s 才发起 read() 的 Syscall?

答案很简单:Java 程序 Netty 的 I/O 线程繁忙

What is the root cause?

Java 程序使用 Netty 网络框架时,当多路 Client 发起连接(TCP Connection)注册,Netty 的 BossGroup 会在 I/O 线程池(即 Woker Group)中选择一个 I/O 线程(Worker EventLoop)负责 TCP 建连处理(三次握手),接收应用请求,发送应用响应,因此 I/O 线程池中的一个 I/O 线程会被多个 TCP 连接所复用。

因此,虽然 Netty 的 NIO 通信模型很好的解决了高并发场景下的业务处理性能,并通过 I/O 线程池很好的控制了高并发量对线程资源的消耗,但 Netty 需要显式配置明确限制 I/O 线程数量(默认取值:2 * CPU 核数),在瞬时请求量大的场景下 I/O 线程仍然存在拥塞风险,当某个 I/O 线程负责的 TCP 连接出现大并发时,有可能发生 Request 到达 Socket 的 recv buffer 后不能被 I/O 线程及时 read 的情况。

  1. 修复方案及验证

锁定故障根因是"Netty 的 I/O 线程阻塞 "后,开发人员在 JVM 中修改应用程序启动参数:java -XX:ActiveProcessorCount,将取值调整为 实际 CPU 核数 的 2 倍,达到 I/O 线程超分 的效果(Netty 中 I/O 线程数量默认取值 2 * ActiveProcessorCount,此时等于 4 * 实际 CPU 核数)。

通过调整 ActiveProcessorCount 参数,将应用程序 I/O 线程数量提升到原来的 2 倍之后,经过持续压测验证,问题解决。

实际生产中,可以根据应用程序的特性(I/O 密集型、CPU 密集型)来自定义线程数量,以达到更好的应用程序性能。对高度 I/O 密集型应用程序,可以增加 I/O 线程数量;对高度计算密集型应用程序,可减少 I/O 线程数量。

0x2: 总结

DeepFlow Observability vs Monitoring

从该案例中我们不难发现,在传统 NPM 监控和 APM 监控体系下,操作系统内核、应用程序均存在大范围的盲区 ,导致故障定位极其困难 ,经常发生 APM 、NPM 呈现的服务性能不一致的情况,从 NPM 监控明显看到服务端应用程序响应慢,但从 APM 监控却认为所有请求的响应性能指标均正常,因而故障诊断中的边界不清来回拉锯甚至互相指责非常普遍。

DeepFlow 通过 eBPF 技术实现的零侵扰(Zero-Code )的分布式追踪,实现了任意一次应用请求(Any Request )从物理网络到容器网络、操作系统、应用进程的全技术栈(Full-Stack )、端到端(Real End-End)追踪能力,打通了应用运维、系统运维、网络运维的技术栈边界,构建统一的运维观测数据湖,从而将故障诊断效率提升到 3 分钟以内。

0x3: 技术 Tip------Netty 网络框架数据包读取过程

Netty 网络框架使用 OS Kernel 的 epoll 机制在内核空间、用户空间之间读/写数据。

那么 Netty I/O 线程如何与 OS Kernel 交互并获取通信对端发来的 Request 数据包呢?

Workflow of Netty

可以通俗的描述为如下过程:

  1. 首先,Netty 的 Worker EventLoopI/O 线程)调用 Kernel 中 epollepoll_wait() 方法,并进入阻塞状态,等待 epoll 的通知;
  2. 当网卡接收到 Request 数据包后,通过硬中断通知 Kernel,Kernel 处理数据包并放入 Socket 的 recv buffer;
  3. 然后,epoll 监测到 Socket 的 recv buffer 有数据包到达后,将可读的 Socket 列入就绪的事件列表;
  4. 再然后,Worker EventLoopepoll_wait 上被唤醒,并返回就绪的事件列表;
  5. 最后,Worker EventLoop 调用 read() 方法从操作系统的 recv buffer 中读取 Request 数据包。

至此,Netty 完成一次从 Kernel 的 Socket 读取数据的完整过程,如果 Worker EventLoop 没有其他需要读/写的数据,则再次回到第 1 步,调用 epollepoll_wait() 方法,进入阻塞状态,等待下一次数据包到达后的 epoll 通知,循环往复。

0x4: 什么是 DeepFlow

DeepFlow 是云杉网络开发的一款可观测性产品,旨在为复杂的云原生AI 应用提供深度可观测性。DeepFlow 基于 eBPF 实现了应用性能指标、分布式追踪、持续性能剖析等观测信号的零侵扰Zero Code)采集,并结合智能标签SmartEncoding)技术实现了所有观测信号的全栈Full Stack)关联和高效存取。使用 DeepFlow,可以让云原生及 AI 应用自动具有深度可观测性,从而消除开发者不断插桩的沉重负担,并为 DevOps/SRE 团队提供从代码到基础设施的监控及诊断能力。

GitHub 地址:https://github.com/deepflowio/deepflow

访问 DeepFlow Demo,体验零侵扰、全栈的可观测性。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
1 收藏
0
分享
返回顶部
顶部