Nginx 教程(2):性能

tcp_nodelay, tcp_nopush 和 sendfile

 

tcp_nodelay

在 TCP 發展早期,工程師須要面對流量衝突和堵塞的問題,其中涌現了大批的解決方案,其中之一是由 John Nagle 提出的算法。node

Nagle 的算法旨在防止通信被大量的小包淹沒。該理論不涉及全尺寸 tcp 包(最大報文長度,簡稱 MSS)的處理。只針對比 MSS 小的包,只有當接收方成功地將之前的包(ACK)的全部確認發送回來時,這些包纔會被髮送。在等待期間,發送方能夠緩衝更多的數據以後再發送。nginx

if package.size >= MSS.size算法

  send(package)緩存

elsif acks.all_received?安全

  send(package)服務器

else網絡

  # acumulate data併發

endsocket

 

與此同時,誕生了另外一個理論,延時 ACKtcp

 

在 TCP 通信中,在發送數據後,須要接收回應包(ACK)來確認數據被成功傳達。

 

延時 ACK 旨在解決線路被大量的 ACK 包擁堵的情況。爲了減小 ACK 包的數量,接收者等待須要回傳的數據加上 ACK 包回傳給發送方,若是沒有數據須要回傳,必須在至少每 2 個 MSS,或每 200 至 500 毫秒內發送 ACK(以防咱們再也不收到包)。

 

if packages.any?

  send

elsif last_ack_send_more_than_2MSS_ago? || 200_ms_timer.finished?

  send

else

  # wait

end

 

正如你可能在一開始就注意到的那樣 —— 這可能會致使在持久鏈接上的一些暫時的死鎖。讓咱們重現它!

 

假設:

 

  • 初始擁塞窗口等於 2。擁塞窗口是另外一個 TCP 機制的一部分,稱爲慢啓動。細節如今並不重要,只要記住它限制了一次能夠發送多少個包。在第一次往返中,咱們能夠發送 2 個 MSS 包。在第二次發送中:4 個 MSS 包,第三次發送中:8 個MSS,依此類推。

  • 4 個已緩存的等待發送的數據包:A, B, C, D

  • A, B, C是 MSS 包

  • D 是一個小包

 

場景:

 

  • 因爲是初始的擁塞窗口,發送端被容許傳送兩個包:A 和 B

  • 接收端在成功得到這兩個包以後,發送一個 ACK

  • 發件端發送 C 包。然而,Nagle 卻阻止它發送 D 包(包長度過小,等待 C 的ACK)

  • 在接收端,延遲 ACK 使他沒法發送 ACK(每隔 2 個包或每隔 200 毫秒發送一次)

  • 在 200ms 以後,接收器發送 C 包的 ACK

  • 發送端收到 ACK 併發送 D 包

  • 在這個數據交換過程當中,因爲 Nagel 和延遲 ACK 之間的死鎖,引入了 200ms 的延遲。

     

    Nagle 算法是當時真正的救世主,並且目前仍然具備極大的價值。但在大多數狀況下,咱們不會在咱們的網站上使用它,所以能夠經過添加 TCP_NODELAY 標誌來安全地關閉它。

     

    tcp_nodelay on;     # sets TCP_NODELAY flag, used on keep-alive connections

     

    享受這200ms提速吧!

     

    sendfile

     

    正常來講,當要發送一個文件時須要下面的步驟:

     

  • malloc(3) – 分配一個本地緩衝區,儲存對象數據。

  • read(2) – 檢索和複製對象到本地緩衝區。

  • write(2) – 從本地緩衝區複製對象到 socket 緩衝區。

  •  

    這涉及到兩個上下文切換(讀,寫),並使相同對象的第二個副本成爲沒必要要的。正如你所看到的,這不是最佳的方式。值得慶幸的是還有另外一個系統調用,提高了發送文件(的效率),它被稱爲:sendfile(2)(想不到吧!竟然是這名字)。這個調用在文件 cache 中檢索一個對象,並傳遞指針(不須要複製整個對象),直接傳遞到 socket 描述符,Netflix 表示,使用 sendfile(2) 將網絡吞吐量從 6Gbps 提升到了 30Gbps。

     

    然而,sendfile(2) 有一些注意事項:

     

  • 不可用於 UNIX sockets(例如:當經過你的上游服務器發送靜態文件時)

  • 可否執行不一樣的操做,取決於操做系統

  • 在 nginx 中打開它

     

    sendfile on;

    tcp_nopush

     

    tcp_nopush 與 tcp_nodelay 相反。不是爲了儘量快地推送數據包,它的目標是一次性優化數據的發送量。

     

    在發送給客戶端以前,它將強制等待包達到最大長度(MSS)。並且這個指令只有在 sendfile 開啓時才起做用。

     

    sendfile on;

    tcp_nopush on;

     

    看起來 tcp_nopush 和 tcp_nodelay 是互斥的。可是,若是全部 3 個指令都開啓了,nginx 會:

     

  • 確保數據包在發送給客戶以前是已滿的

  • 對於最後一個數據包,tcp_nopush 將被刪除 —— 容許 TCP 當即發送,沒有 200ms 的延遲

  • 在 nginx 和上游服務器之間 keep-alive

     

    upstream backend {

        # The number of idle keepalive connections to an upstream server that remain open for each worker process

        keepalive 16;

    }

     

    server {

      location /http/ {

        proxy_pass http://http_backend;

        proxy_http_version 1.1;

        proxy_set_header Connection "";

      }

    }

相關文章
相關標籤/搜索