Linux TCP 队列:从 listen backlog 追到应用读取

背景 监听端口存在,却仍有客户端连接超时,问题可能出在握手或应用接收速度。不同 TCP 队列反映不同阶段,不能混为一谈。 核心判断 区分握手中的请求队列与等待 accept 的连接队列。 把 ss 的监听队列与 ListenOverflows、ListenDrops 对照。 同时检查应用 accept 速度、FD 限额和调度延迟。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 ss -lntp; ss -s; nstat -az | grep -E 'Listen|Syncookies|Retrans'; cat /proc/net/netstat | grep Listen 边界与误区 提高 backlog 只是增加缓冲,不能解决长期接收能力不足。Syncookies 也不是业务容量扩展机制,队列含义要按 socket 状态解读。 验证方式 在受控环境制造连接突发,观察计数器增量和客户端连接耗时;若队列稳定增长,先查服务线程和资源限额,再逐项调参。 小结 把 SYN 队列、accept 队列、socket 状态和应用 accept 速率放在同一时间轴。

2026年10月7日 · 1 分钟 · BvBeJ

Linux I/O wait:别把等待磁盘误判成 CPU 不够

背景 CPU 利用率不高但请求很慢,可能是线程在等待 I/O,也可能是锁或远程依赖。iowait 只是入口,不是瓶颈的最终解释。 核心判断 把设备延迟、队列、吞吐与进程读写速率对齐到同一时间段。 结合 PSI 和 D 状态线程判断是否存在实际阻塞。 区分同步写、刷盘、随机读和后台维护流量。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 iostat -xz 1 5; pidstat -d -w 1 5; vmstat 1 5; cat /proc/pressure/io 边界与误区 sysstat 工具需要预先安装;PSI 支持取决于内核配置。单看 util 或 iowait 无法解释多队列设备的饱和程度。 验证方式 在已知顺序读和同步写场景下采样建立对照,再关联业务 p99。只有确认瓶颈来自存储路径,才讨论更换磁盘或增加并发。 小结 同时观察 PSI、磁盘延迟、队列深度、进程状态和请求耗时再定位瓶颈。

2026年9月27日 · 1 分钟 · BvBeJ

Linux 文件描述符泄漏:从增长曲线定位资源所有权

背景 FD 用量不断上升,最终表现可能是无法建立连接,也可能是日志写入失败。需要先分清增长的是文件、socket 还是管道。 核心判断 按时间采集数量和符号链接目标,区分正常连接池与泄漏。 沿着打开、移交、关闭三个动作检查资源所有者。 重点覆盖错误分支、重试循环和提前返回。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 pid=$(pidof app); printf 'total='; find "/proc/$pid/fd" -mindepth 1 -maxdepth 1 | wc -l; ls -l "/proc/$pid/fd" | head 边界与误区 提高 ulimit 只延迟触顶,不能修复泄漏。短暂连接峰值也不是泄漏证据,必须与请求量和回收过程对照。 验证方式 固定负载运行一段时间再停止,确认 FD 回落。采集 /proc 需要相应权限,示例的 pidof 单实例假设在多进程环境应改成逐 PID 处理。 小结 按进程统计 FD 总数和目标类型,沿着增长曲线寻找没有闭合的资源生命周期。

2026年9月17日 · 1 分钟 · BvBeJ

Linux 进程基线:先建立正常画像,再判断异常

背景 线程数从几十变成几百是否异常,取决于请求量和运行阶段。没有正常基线,排障很容易只盯着某个绝对值。 核心判断 按同一进程版本记录 RSS、线程、FD、连接数与请求负载。 区分预热增长、稳定平台和持续线性增长。 采样时记录 PID 与启动时间,进程重启后不要拼接成一条曲线。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 pid=$(pidof app); ps -o pid,ppid,stat,%cpu,%mem,rss,nlwp,etime -p "$pid"; ls "/proc/$pid/fd" | wc -l 边界与误区 pidof 可能返回多个 PID,示例适用于单实例进程;多实例请逐个采样。RSS 增加也可能是缓存增长,不能单凭它认定泄漏。 验证方式 保留空载、稳定负载和峰值三个阶段的快照。停止压测后观察资源是否回到可解释的平台,再决定是否进一步抓堆或分析 FD。 小结 围绕进程树、线程数、文件描述符、RSS 和连接数建立可比较的时间序列基线。

2026年9月7日 · 1 分钟 · BvBeJ

Linux 容器运行时对比:containerd vs CRI-O

背景与问题界定 2023 年 Kubernetes 正式宣布废弃 Docker Shim 后,核心容器运行时可选方案聚焦在 containerd 和 CRI-O 之间。某团队在进行容器运行时切换时遇到了两类截然不同的问题:选择 containerd 的测试集群发现镜像拉取非常快但内存占用偏高;选择 CRI-O 的集群在 RHEL 上运行得很稳定,但在 Ubuntu 上遇到了一些 cgroup v2 兼容性问题。更深层次的分歧在于技术理念——containerd 由 Docker 捐赠给 CNCF,继承了 Docker 的镜像管理和 API 设计哲学,强调"兼容并包";而 CRI-O 由 Red Hat 主导开发,遵循"专为 K8s 设计"的极简路线,去除了一切 K8s 不需要的功能。选择哪一个不仅仅是技术比对,更关乎团队的运维习惯和长期生态走向。 目标拆解与工程约束 OCI 规范兼容性:运行时必须严格遵循 OCI Runtime Spec 和 OCI Image Spec,支持所有标准 OCI 镜像格式和 runtime handler(runc、kata、gVisor),不能出现某个镜像在 dockerd 能跑但在新运行时无法运行的情况。 镜像拉取与管理效率:在大规模集群中(500+ 节点同时拉取镜像),运行时的镜像层缓存机制、并发拉取策略和 GC 效率直接影响 Pod 启动速度,目标是将 Pod 启动 P99 延迟保持在 30 秒以内。 安全特性支持:必须原生支持 Rootless 容器、User Namespace Remapping、Seccomp 和 AppArmor 等安全机制,同时不能因为安全加固而显著增加容器启动延迟和运行时开销。 运维可观测性:运行时需要提供清晰的 Metrics 指标(容器启动时间、镜像缓存命中率、GRPC 调用延迟等),方便通过 Prometheus 统一监控运行时健康状态,不能成为运维的"黑盒"。 方案设计 我们从架构、性能、生态三个维度对比。containerd 的架构分为三个主要组件:containerd(守护进程,管理镜像和容器生命周期)、containerd-shim(每个容器一个 shim 进程,负责容器运行时状态管理)、ctr/nerdctl(CLI 工具)。CRI-O 则采用单一守护进程 crio + conmon(容器监控进程)+ crictl CLI 的简化结构,不需要额外的 shim 管理。 ...

2026年8月28日 · 2 分钟 · BvBeJ

Linux 内核调优实战:从 sysctl 到 cgroup v2

背景与问题界定 某在线广告平台的实时竞价系统(RTB)运行在 100+ 台物理服务器上,Linux 内核版本已升级到 5.15 LTS,但性能表现始终达不到预期。一方面,netstat -s 显示大量的 TCPLoss 和 TCPTimeouts,网络协议栈参数均为操作系统默认值——这些默认值针对通用桌面场景设计,不适合高并发、低延迟的广告竞价场景。另一方面,容器技术从 Docker 切换到 containerd 后,cgroup v1 的层级复杂导致资源统计不准确,同一个 Pod 内的 Java 进程和 Sidecar 代理在 CPU 资源上相互争抢,缺乏精细化的限制能力。需要一套系统性的内核参数调优和 cgroup v2 迁移方案。 目标拆解与工程约束 网络协议栈极致优化:RTB 系统对网络延迟苛刻到微秒级,TCP 连接的延迟确认(delayed ack)、Nagle 算法、TIME_WAIT 累积等默认行为必须被调整,目标是单次请求的 TCP 握手延迟降低 30%。 内存管理精细化:广告竞价算法使用大量 mmap 文件映射和共享内存,需要调整 vm.swappiness、vm.dirty_ratio 和 NUMA 内存分配策略(numa_balancing)来减少内存回收抖动和跨 NUMA 节点访问延迟。 cgroup v2 统一资源管控:从 cgroup v1 迁移到 v2,利用其单一层级树(Single Hierarchy)消除 v1 中不同子系统可能挂在不同控制器下的混乱,实现 CPU、内存、IO 的统一 resource controller。 IO 优先级隔离:容器内的日志写入、监控数据采集和业务读写共享同一块 SSD,需要通过 cgroup v2 的 IO Controller(io.weight 和 io.max)实现 IO 带宽的 QoS 保障,避免日志洪峰冲垮数据库 IO。 方案设计 网络协议栈调优是我们投入最多的领域。根据 RTB 业务的流量特征(短连接、高并发、单次数据量小),我们将以下 sysctl 参数纳入集群基线配置: ...

2026年8月22日 · 2 分钟 · BvBeJ

Linux 性能诊断实战:从 perf 到 eBPF 追踪

背景与问题界定 某电商平台大促期间,核心下单接口的 P99 延迟从 200ms 飙升到 3.2s,CPU 饥渴地跑在 95% 但业务层看不出任何明显瓶颈。线上排查时,top 和 htop 只能看到进程级 CPU 使用率,无法定位到具体函数或内核调用路径。传统 strace 在生产环境会带来 10 倍以上的性能开销,无法直接使用。这暴露了一个典型问题:现代高并发系统中,性能瓶颈往往埋藏在系统调用层、内核协议栈或内存分配的微妙竞争点上,需要一套低开销、“显微镜级"的诊断工具组合。 目标拆解与工程约束 零入侵采样:诊断工具不能对生产业务进程造成显著的性能影响,采样开销必须控制在 1% CPU 以内,不能让诊断本身成为新的性能瓶颈。 全栈追踪能力:工具链必须覆盖从应用层函数、库调用、系统调用到内核函数、硬件 PMU 事件的全栈追踪,能在一个时间线上串联用户态和内核态的执行路径。 动态插桩:不能在业务代码中预先埋点,必须支持对运行中的进程进行动态插桩,且插桩点和追踪逻辑可以在不重启进程的情况下热插拔和调整。 安全与权限约束:生产环境上使用 eBPF 需要保证内核版本 >= 4.19(基础功能)或 >= 5.4(CO-RE 支持),同时需要控制 BPF 程序的复杂度(指令数 <= 4096),防止 BPF JIT 引发内核 panic。 方案设计 故障排查采用分层诊断策略。第一层使用 perf 进行快速的 CPU 热点定位,通过 perf top -p <pid> 查看进程级别热点函数,利用 CPU 硬件 PMU 中的 cycles 事件采样统计函数调用频率。当 perf 显示热点集中在内核态时(如 _raw_spin_lock 或 tcp_v4_rcv),使用 perf record -e sched:sched_switch -g 记录上下文切换的调用栈,确认是否为频繁的锁竞争或调度延迟。 ...

2026年8月18日 · 2 分钟 · BvBeJ

Linux CPU 隔离在低延迟服务中的实践

典型问题 同机混部下,关键服务线程会被后台任务、ksoftirqd 和内核 housekeeping 干扰,导致 p99 抖动长期无法收敛。 隔离手段 通过 cpuset 为关键进程绑定独占核心。 将中断亲和性避开关键核心。 把系统 housekeeping 任务集中到专用核。 验证方法 对比隔离前后 p99/p999。 观察调度切换次数与 run queue 长度。 结合 perf 看 cache miss 与上下文切换变化。 小结 CPU 隔离不是“调个参数”,而是资源编排策略。把关键路径从系统噪声里解耦,低延迟目标才有实现基础。

2026年5月26日 · 1 分钟 · BvBeJ

eBPF 持续 Profiling 在生产环境的落地边界

为什么要持续 Profiling 离线抓火焰图只能解释“当下问题”,无法覆盖版本演进中的渐进回归。持续 profiling 能提供趋势视角。 落地关键 采样频率按服务等级分层,避免全局高频。 只保留聚合后的符号栈,降低存储压力。 结合版本号维度做回归对比。 噪声治理 排除短命批处理进程。 对 JIT 语言补齐符号映射。 过滤启动期冷缓存阶段样本。 常见风险 盲目提高采样率导致 CPU 额外开销。 内核版本不一致引发采样偏差。 只看 Top 函数,忽视调用链变化。 小结 持续 profiling 不是“多收集”,而是“低扰动、可对比、可解释”的长期性能体检体系。

2026年5月10日 · 1 分钟 · BvBeJ

C++ 服务内存碎片治理:从分配器选择到线上观测

症状识别 业务负载稳定,但 RSS 只涨不降。 堆分析看不到明显泄漏对象。 进程重启后内存瞬间回落。 为什么会碎片化 多线程下不同 size class 高频分配释放。 短命与长命对象混放。 大量临时 buffer 导致 arena 难回收。 治理路径 分离对象生命周期:短命池与长命池分开。 统一热点对象尺寸,减少跨 class 抖动。 评估 jemalloc/tcmalloc 与默认 allocator 差异。 线上指标建议 allocated_bytes active_bytes resident_bytes fragmentation_ratio = resident / active 风险点 只看进程总内存,不看分配器内部统计。 盲目手写内存池,忽略线程本地缓存竞争。 回收策略和 NUMA 绑定冲突。 小结 碎片治理是系统工程:对象模型、分配器、线程调度、NUMA 拓扑都要协同。先观测后改造,收益才稳定。

2026年5月6日 · 1 分钟 · BvBeJ