2.6.29 – 2.6.39 之间的 kernel 不要在 LVS Director 的网卡开启 GRO

我之前开发了一个传图系统,直接使用了 @agentzh 的 lua_resty_upload (其实这里不应该用 nginx_lua 做的),但发现其在 LVS 下,POST 请求总是 socket timeout ,而直接 POST 到 real server 没有问题。无奈我当时只好用 DNS 轮训的方式,终于在最近找到原因了。

Linux 在 2.6.29 的时候,引入了一个 GRO (Generic receive offload) 。

MTU 一般都是 1500 字节,如果一个包超过了 MTU ,就会被分片。1500 这个数字,估计是基于当时的网络环境制定的,而现在,10Gbps 的网卡都普遍使用了,可能就不太适用了。如果 10Gbps 的网卡满载地来跑,一个完整的数据包会被分片 800w 片。我们可以通过调整 client 和 server 端的 MTU 令到分片尽可能减少,提高吞吐量。但是,如果 client 端(例如用户)不受我们控制呢,那我们就无法提高性能了。于是有人想到通过网卡的行为来间接实现相当于提高 MTU 的作用,这就是 GRO [1] 。

GRO 就是在网卡中将满足一定的条件(比较严格)的包,将分片的包组装合并了,才一次性交给上面的协议栈。现在的网卡一般都支持了,除了网卡支持,还要驱动也支持才可以。如果网卡和驱动都支持,那么在 2.6.29 以后的 kernel ,都会默认开启。

ethtool -k eth0 ,来查看是否有 generic-receive-offload: on ,如果是 off ,也不一定是不支持,可以通过 ethtool -K eth0 gro on 来尝试开启。

但是 GRO 和 LVS 协作得并不好,具体表现就是,POST 数据到 LVS 很慢。

抓包看我的 POST 请求,握手阶段用了较长时间,出现了数次 incorrect 后才真正开始传输。POST 小于 MTU 的数据,并不会触发这个问题,而 POST 大于 MTU 的数据,就会。证明了这里肯定是 GRO 惹的祸。

没有 google 到最根本的原因,但也有一些说法 [2] ,就是 GRO 和 LVS 之间的兼容没有做好,知道 2.6.39 已经修复了这个问题。

经验主义一点,就是 LVS 的 director 一律关闭网卡的 GRO 。

ethtool -K eth0 gro off

这个情况我遇到的,一般发生在内网 POST 的时候,而用户 POST 给我的情况太慢的不太多,也有可能是他们网络根本就慢,手机的网络环境变数太多,而我暂时也没有办法把他们过滤出来一一查看。我猜想,是因为内网的网络环境正好符合了 GRO 的 merge 的条件,GRO 起作用了,所以我的 POST 很慢。而用户的请求不符合条件,所以还是比较正常的。

 

 

Reference:

[1] http://lwn.net/Articles/358910/

[2] http://archive.linuxvirtualserver.org/html/lvs-users/2011-05/msg00004.html

Comments

  1. gavin says:

    我碰到过登录了网站,带了太多cookie,导致lvs很卡的问题。

Submit a Comment