一、现象
在一次访问请求nginx中,通常只需要几毫秒的RT,但当请求数据达到某一个数值时,rt明显提高,甚至超过了300毫秒。
二、问题的原因
大家都知道,TCP为了提高带宽利用率和吞吐量,做了各种优化。比如delay ack和Nagle算法。就是这样的一些优化使用不慎,导致陷入性能问题。接下来就先分别说说delay ack和Nagle算法。
- 什么是delay ack
就以我们的Nginx为例吧。nginx收到请求后,立即返回一个ack收到确认包。这个包没有只有消息头没有任何数据内容。这就导致一个明显的问题:带宽利用率比较低效。那有没有办法可以优化呢?有,就是delay ack。怎么做呢?就是Nginx收到数据不要着急发送ack包,而是等一段时间比如40毫秒,如果这40毫秒内有数据要发送给client。那么这个ack就可以打这趟顺风车了,从而节省资源。如果40毫秒内没数据发送给client呢,没办法,到了40毫秒也必须发送ack包。因为client以为丢包重传的代价更大。
还有一种情况就是,nginx收到数据在延缓等待发送ack包时,又收到client的一个数据包。这时nginx会把两个ack包合并为一个ack包回复给client。
- 什么是Nagle算法
在发送数据包时,如果数据包小于MSS(最大分段大小),则会去判断是否有已发出去的包还没有ack,如果有则不着急发送,等等前面的包收到回复再发送。
- 性能问题关键点,client端启动Nagle,server端启动delay ack
假如client发送一个http请求个server。这个请求时1600byte,MSS是1460byte。那么就会分成两个tcp包,第一个1460byte,剩下的140byte放在第二个包。第一个包发送到server时,由于server开启了delay ack,所以没有立即ack,又因为server没有收到完整的http请求包,所以也没有立即进行http response,这就导致ack会一直等到40毫秒的delay时间。其实如果client立即发送第二个包,server收到后立即做出http response也不会有问题。问题时client启动了Nagle算法,第一个包没有收到ack,第二个包就不会立即发送出去。两边相互等。这就是性能问题的核心原因。