xdp-tutorial

xdp-tutorial 是 XDP 官方提供的入门指南,还是比较权威的。

但是,有一说一,这个仓库的更新并不是很及时,不太能跟得上 XDP 自身的迭代速度。其中存在的一些问题,虽然可以在 issue 列表里搜到,但都是说上游已经解决,就是还没来得及更新这个 xdp-tutorial 仓库依赖的 libxdp 或者 libbpf 版本。

lesson 回顾

xdp-tutorial 包含了三大类 lesson:

  • basic
  • packet
  • advanced

看似循序渐进,实则不然;前两个部分 basic 和 packet 还是比较正常的,第三个部分 advanced 基本没啥可实操的,建议看看就好,不要逗留。另外,repo readme 里忽略了 tracing 系列的 lesson,算是第四个部分。讲道理,tracing 部分比 advanced 有用多了,建议上点心。

Read more »

背景

缓存服务的吞吐出现明显下降,更具体一点,命中状态下的吐出带宽 偏低。

网络瓶颈

因为数据是从本地吐出的,所以最开始怀疑是网络的因素,通过小米监控查看了 tcp 相关的指标,比如:

  • TcpExt.PruneCalled:TCP 协议层主动丢包
  • TcpExt.TCPTimeouts:TCP 传输超时

相比其他负载差不多的机器,这些指标都要显著偏高。结合以往经验,TCP 的这些指标异常也有可能只是表象,是其他原因导致了这些异常,我们不能简单的将这些异常直接归为根本原因。

Read more »

起因

在使用 linux kernel aio 接口的过程中,发现一个诡异的问题:如果单次 aio 请求的数据大小超过了某一个阈值,kernel aio 就总是处理失败:不管请求数据多大,aio 返回的结果一直都是 2147479552 !

AIO

一般情况下我们使用的 IO 接口都是同步类型的,这样的接口会同步阻塞调用程序,直到底层数据准备好,才会返回。比如调用 read 函数读取磁盘文件的数据,因为涉及到数据从物理磁盘传输到内存,这个过程一般耗时在毫秒级别,期间调用程序无事可做,就会被 OS 阻塞挂起,等待直到数据读取完成之后,read 函数返回,控制权才会重新回到调用程序。

AIO 则是采用异步的方式,发起 IO 请求之后不再阻塞,而是立即返回,这样调用程序就可以在 IO 执行期间做更多的其他事情,从而实现并行,提升处理效率。

具体来说,在 linux 上实现 AIO 主要有两种方式:Posix 和 kernel

Read more »

在一般的网络调优中,调整 TCP 协议栈的内存使用是比较常见的一种方式,可以在两个层次进行:

  • 内核层
  • 应用层

内核层

内核层次对于 TCP 内存的使用主要涉及以下两类:

man 7 tcp

1
2
3
tcp_mem
tcp_rmem
tcp_wmem

可以通过 sysctl 接口进行查看和更改:

1
2
3
net.ipv4.tcp_mem = 22152        29538   44304
net.ipv4.tcp_rmem = 4096 131072 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
Read more »

nginx 缓冲

Module ngx_http_proxy_module

nginx 转发 upstream 数据到 client,如果上下游网速不匹配,典型的,upstream 发送数据的速度,一般远大于 client 接收数据的速度,就会导致数据在 nginx 积压,我们可以通过配置指令

1
proxy_buffering on|off

选择性的开启或者关闭 nginx 对 upstream 数据的缓冲功能

缓存和缓冲,概念是有所区分的:缓存表示数据被存了下来,存了起来,以备后续重复使用;缓冲则是表示对数据进行临时性的存储,一般就是因为上下游速度不匹配的时候,需要引入缓冲的机制,比如 nginx 将 upstream 数据临时性的缓冲在 nginx 本地,只是为了接下来把这些数据以匹配下游的速度发送出去,而并没有“永久性”存储这些数据。换个角度理解,缓冲是一次性的行为,而缓存就是为了复用。相应的,nginx 也明确区分了 proxy_cacheproxy_buffering 两个独立的功能。

Read more »

os.popen 的若干问题

背景

os.popen 是 Python 标准库 os 的一个函数,一般用来在程序中 调用 shell 命令,并需要读取输出 时使用。

1
os.popen(command[, mode[, bufsize]])

Open a pipe to or from command. The return value is an open file object connected to the pipe, which can be read or written depending on whether mode is ‘r’ (default) or ‘w’. The bufsize argument has the same meaning as the corresponding argument to the built-in open() function. The exit status of the command (encoded in the format specified for wait()) is available as the return value of the close() method of the file object, except that when the exit status is zero (termination without errors), None is returned.

理论上,除了读取输出,我们也可以写入,因为 os.popen 本质上是提供了一个管道,允许双向通信。

问题

这个函数在 Python 2.6 之后已经被标记为 deprecated,但是因为 使用上的便利,还是有很多 Python 程序在需要调用 shell 命令时使用该函数。比如:

1
2
now = os.popen('date').read()
# do something with @now ...

我们一般都只会关心 shell 命令的输出,也就是上述代码中的 now

容易掉坑的地方就在这里,我们忽略了对 os.popen() 返回值的处理!

Read more »

原文请移步

主题

  • 基于 linux 内核,机器是如何接受数据包的?
  • 数据包从网卡到应用层会经过若干个组件,如何针对性的进行监控和调优?

基本原则

  • 理论上,只需要在网络栈的各个层次监控 丢包率,就可以很快的定位系统当前的瓶颈;
  • 这个时候如果只是参考网上的一份”最优“ sysctl 配置,往往不能达到很好的效果;
  • 调优的前提是,我们需要有清晰的可监控指标来验证实际的效果。

概述

数据包从抵达网卡开始,一路到达套接字的 receive buffer:

  • 驱动加载和初始化
  • 数据包到达网卡控制器(NIC)
  • 数据包被复制到内核空间( DMA -> ring buffer )
  • 产生硬件中断,通知系统数据可读
  • 驱动调用 NAPI 激活 poll 循环(如果该循环处于休眠状态)
  • ksoftirpd 调用驱动注册的 poll 函数,读取 ring buffer 中的数据包
  • ring buffer 对应的内存区域被解除映射( memory region unmapped )
  • 数据包被封装为 skb 结构体,准备传递到上层协议栈
  • 如果开启网卡多队列,数据帧会被负载均衡到多个 CPU 进行处理
  • 数据帧经由队列,递交上层协议栈
  • 协议栈处理( IP -> UDP/TCP )
  • 数据被填充到套接字的 receive buffer
Read more »
0%