公益服梦幻诛仙上线送1.5亿:線程池機制使nginx性能提高9倍

  • A+
所屬分類:Nginx
摘要

一般情況下,nginx 是一個事件處理器,一個從內核獲取連接事件并告訴系統如何處理的控制器。
實際上,在操作系統做讀寫數據調度的時候,nginx是協同系統工作的,所以nginx能越快響應越好。

nginx task pool

一、問題

一般情況下,nginx 是一個事件處理器,一個從內核獲取連接事件并告訴系統如何處理的控制器。實際上,在操作系統做讀寫數據調度的時候,nginx是協同系統工作的,所以nginx能越快響應越好。

 

nginx處理的事件可以是 超時通知、socket可讀寫的通知 或 錯誤通知。nginx 接收到這些消息后,會逐一進行處理。但是所有處理過程都是在一個簡單的線程循環中完成的。nginx 從消息隊列中取出一條event后執行,例如 讀寫socket的event。在大多數情況下這很快,Nginx瞬間就處理完了。

 

如果有耗時長的操作發生怎么辦?整個消息處理的循環都必須等待這個耗時長的操作完成,才能繼續處理其他消息。所以,我們說的“阻塞操作”其實意思是長時間占用消息循環的操作。操作系統可能被各種各樣的原因阻塞,或者等待資源的訪問,例如硬盤、互斥鎖、數據庫同步操作等。

 

例如,當nginx 想要讀取沒有緩存在內存中的文件時,則要從磁盤讀取。但磁盤是比較緩慢的,即使是其他后續的事件不需要訪問磁盤,他們也得等待本次事件的訪問磁盤結束。結果就是延遲增加和系統資源沒有被充分利用。

 

有些操作系統提供了異步讀寫文件接口,在nginx中可以使用這些接口(//nginx.org/en/docs/http/ngx_http_core_module.html?&&&_ga=1.197764335.1343221768.1436170723#aio)。例如FreeBSD就是一個較好的例子,但不幸的是,linux提供的一系列異步讀文件接口有不少缺陷。其中一個問題是:文件訪問和緩沖需要隊列,但是Nginx已經很好解決了。但是還有一個更嚴重的問題:使用異步接口需要對文件描述符設置O_DIRECT標識,這意味著任何對這個文件的訪問會跳過緩存直接訪問磁盤上的文件。在大多數情況下,這不是訪問文件的最佳方法。

二、線程池

為了解決這個問題,Nginx 1.7.11 引入了線程池概念。現在讓我們了解一下線程池是怎樣工作的。

 

在nginx中,線程池執行的是分發服務,他由一個任務隊列和一些執行任務的線程組成。當一個工作線程在執行一個可能會存在潛在長時間操作的任務時,這個任務會被”卸下“并重新放到任務隊列中去,這個被”卸下“的任務可能會被其他線程再執行。

 

現在,只有2個基礎操作會造成“卸下任務”到任務隊列:

  • 在大多操作系統上的read()系統調用
  • linux系統的sendfile()
    如果這個機制被證實是有益于nginx的,我們以后還會添加其他的操作。

三、線程池并非靈丹妙藥

大多數讀寫文件操作都需要通過緩慢的磁盤。如果有充足的內存來存儲數據,那么操作系統會緩存頻繁使用的文件,也就是“頁面緩存”(page cache)機制。

 

由于頁面緩存機制,nginx幾乎在所有情況下都能體現非常好的性能。通過頁面緩存讀取數據非???,并且不會阻塞。另一方面,卸下任務到任務池是有瓶頸的。所以在內存充足并且使用的數據不是非常大的時候,nginx即使不使用線程池也是幾乎工作在最佳狀態。

 

卸下寫操作到任務池中,是一個適用于特殊場景的處理方案,適用于大量無法使用VM緩存的請求操作。
例如一個高負荷的基于Nginx的視頻流服務器。另外FreeBSD的用戶不需要擔心這些,因為FreeBSD已經有很好的異步讀操作接口,無需使用線程池。

四、配置線程池

如果你確定在你的場景中適合使用線程池,那么一起看看如何配置線程池。準備工作步驟如下:

  • 使用 nginx 1.7.11 或更新的版本
  • 使用--with-threads參數編譯nginx

最簡單的例子,添加一個aio線程標識(可以添加到http、server 或 location段中):

這是一個最簡單的配置例子,等于以下的配置:

以上配置定義了一個叫 default 的線程池,有32個工作線程,任務隊列最大存放65536個任務。如果任務隊列滿了,nginx會拋棄任務并打印以下日志:

當出現了這個日志,這意為著你可以調大你的任務隊列,或者你的系統無法處理這么多任務。所以綜上所述,你可以配置你的 線程數,任務隊列長度,線程池名稱。你也可以設置多個線程池,用在不同的地方:

如果max_queue,也就是任務隊列長度未指定,那么長度默認為65536.max_queue也可以設置為0,這樣線程池只能處理和線程數一樣多的任務,不會有任務存儲在任務隊列中。

 

現在假設你有一臺有3個硬盤的服務器,你希望這臺服務器作為緩存代理使用,這是你CDN的一個緩存節點,緩存的數據已經超過了可用內存。在這個場景中最重要的事情就是提高磁盤讀寫的性能。一個方案就是使用RAID,另一個方案就是使用Nginx:

在配置中,thread_pool 指令給每個磁盤定義了獨立的線程池;proxy_cache_path指令給每個磁盤定義獨立的緩存路徑、參數;split_clients ??橛糜詼喔齷捍媯ㄒ簿褪嵌喔齟排蹋┑母涸鼐?,這個解決方案很符合該使用場景;proxy_cache_path中的use_temp_path=off參數讓nginx存儲臨時文件到緩存目錄,這可以避免更新緩存時的磁盤間數據拷貝。

 

以上的例子說明可以根據自身硬件靈活調整nginx,通過細微調整,可以讓你的軟件、操作系統、硬件協同工作在最佳狀態,盡可能的利用所有資源。

結論

線程池機制是一個非常好的機制,通過解決大量數據情況下導致的阻塞問題,使得nginx的性能達到一個新的高度。如之前提到的,接下來會有新的接口可能會實現在不損耗性能的情況下實現”卸下“任務機制。注:原文中的一些翻譯
(1)offloading 翻譯為 卸下,其實就是把一個任務塞回到任務池中;
(2)原文中有提到把任務offloading到thread pool中,但其實是task是存放在task pool中,所以我譯為”把任務卸下到任務池中“;
(3)性能測試階段譯文略過,可以參考原帖

投稿信息

原文標題:Thread Pools in NGINX Boost Performance 9x!
原文官方地址:https://www.nginx.com/blog/thread-pools-boost-performance-9x/

本文為譯文,非直譯。感謝"solohac"的投稿

weinxin
微信公眾號
掃一掃關注運維生存時間公眾號,獲取最新技術文章~

發表評論

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前評論:5   其中:訪客  5   博主  0

    • test啊 9

      test啊

      • 默北 梦幻诛仙论坛 6

        nginx的線程池主要是為了規避磁盤IO阻塞。 –with-threads選項可能對某些第三方??椴患嬡輊rror: unknown type name ‘ngx_thread_task_t’,如ngx_echo???,需要打上補丁。

        • 錕斤拷 9

          不錯的文章

          • 默北 梦幻诛仙论坛 6

            贊一個

              • 匿名 9

                @默北 去問問