注冊用戶即可下載全站資源 關注Java幫幫微信公眾號
 

Linux 驚群效應之 Nginx 解決方案

34
發表時間:2018-12-22 12:54

前言

因為項目涉及到 Nginx 一些公共模塊的使用,而且也想對驚群效應有個深入的了解,在整理了網上資料以及實踐后,記錄成文章以便大家復習鞏固。

結論

  • 不管還是多進程還是多線程,都存在驚群效應,本篇文章使用多進程分析。

  • 日本韩国三级aⅴ在线观看在 Linux2.6 版本之后,已經解決了系統調用 accept 的驚群效應(前提是沒有使用 select、poll、epoll 等事件機制)。

  • 目前 Linux日本韩国三级aⅴ在线观看 已經部分解決了 epoll 的驚群效應(epoll 在 fork 之前),Linux2.6 是沒有解決的。

  • Epoll 在 fork 之后創建仍然存在驚群效應,Nginx日本韩国三级aⅴ在线观看 使用自己實現的互斥鎖解決驚群效應。

驚群效應是什么

驚群效應(thundering herd)是指多進程(多線程)在同時阻塞等待同一個事件的時候(休眠狀態),如果等待的這個事件發生,那么他就會喚醒等待的所有進程(或者線程),但是最終卻只能有一個進程(線程)獲得這個時間的“控制權”,對該事件進行處理,而其他進程(線程)獲取“控制權”失敗,只能重新進入休眠狀態,這種現象和性能浪費就叫做驚群效應。

驚群效應消耗了什么

  • Linux 內核對用戶進程(線程)頻繁地做無效的調度、上下文切換等使系統性能大打折扣。上下文切換(context switch)過高會導致 CPU 像個搬運工,頻繁地在寄存器和運行隊列之間奔波,更多的時間花在了進程(線程)切換,而不是在真正工作的進程(線程)上面。直接的消耗包括 CPU 寄存器要保存和加載(例如程序計數器)、系統調度器的代碼需要執行。間接的消耗在于多核 cache 之間的共享數據。

  • 為了確保只有一個進程(線程)得到資源,需要對資源操作進行加鎖保護,加大了系統的開銷。目前一些常見的服務器軟件有的是通過鎖機制解決的,比如 Nginx(它的鎖機制是默認開啟的,可以關閉);還有些認為驚群對系統性能影響不大,沒有去處理,比如 Lighttpd

Linux 解決方案之 Accept

Linux 2.6 版本之前,監聽同一個 socket日本韩国三级aⅴ在线观看 的進程會掛在同一個等待隊列上,當請求到來時,會喚醒所有等待的進程。

日本韩国三级aⅴ在线观看Linux 2.6 版本之后,通過引入一個標記位 WQ_FLAG_EXCLUSIVE,解決掉了 accept 驚群效應。

具體分析會在代碼注釋里面,accept代碼實現片段如下:

喚醒阻塞的 accept 代碼片段如下:

Linux 解決方案之 Epoll

在使用 select、poll、epoll、kqueue 等 IO 復用時,多進程(線程)處理鏈接更加復雜。
在討論 epoll 的驚群效應時候,需要分為兩種情況:

  • 日本韩国三级aⅴ在线观看epoll_create 在 fork 之前創建

  • epoll_create 在 fork 之后創建

epoll_create 在 fork 之前創建

與 accept 驚群的原因類似,當有事件發生時,等待同一個文件描述符的所有進程(線程)都將被喚醒,而且解決思路和 accept 一致。

日本韩国三级aⅴ在线观看為什么需要全部喚醒?因為內核不知道,你是否在等待文件描述符來調用 accept() 函數,還是做其他事情(信號處理,定時事件)。

此種情況驚群效應已經被解決。

epoll_create 在 fork 之后創建

epoll_create 在 fork 之前創建的話,所有進程共享一個 epoll 紅黑數。
日本韩国三级aⅴ在线观看如果我們只需要處理 accept 事件的話,貌似世界一片美好了。但是 epoll 并不是只處理 accept 事件,accept 后續的讀寫事件都需要處理,還有定時或者信號事件。

日本韩国三级aⅴ在线观看當連接到來時,我們需要選擇一個進程來 accept,這個時候,任何一個 accept 都是可以的。當連接建立以后,后續的讀寫事件,卻與進程有了關聯。一個請求與 a 進程建立連接后,后續的讀寫也應該由 a 進程來做。

當讀寫事件發生時,應該通知哪個進程呢?Epoll 并不知道,因此,事件有可能錯誤通知另一個進程,這是不對的。所以一般在每個進程(線程)里面會再次創建一個 epoll 事件循環機制,每個進程的讀寫事件只注冊在自己進程的 epoll 種。

日本韩国三级aⅴ在线观看我們知道 epoll 對驚群效應的修復,是建立在共享在同一個 epoll 結構上的。epoll_create 在 fork 之后執行,每個進程有單獨的 epoll 紅黑樹,等待隊列,ready 事件列表。因此,驚群效應再次出現了。有時候喚醒所有進程,有時候喚醒部分進程,可能是因為事件已經被某些進程處理掉了,因此不用在通知另外還未通知到的進程了。

Nginx 解決方案之鎖的設計

日本韩国三级aⅴ在线观看首先我們要知道在用戶空間進程間鎖實現的原理,起始原理很簡單,就是能弄一個讓所有進程共享的東西,比如 mmap 的內存,比如文件,然后通過這個東西來控制進程的互斥。

Nginx 中使用的鎖是自己來實現的,這里鎖的實現分為兩種情況,一種是支持原子操作的情況,也就是由 NGX_HAVE_ATOMIC_OPS 這個宏來進行控制的,一種是不支持原子操作,這是是使用文件鎖來實現。

鎖結構體

如果支持原子操作,則我們可以直接使用 mmap,然后 lock 就保存 mmap 的內存區域的地址
日本韩国三级aⅴ在线观看如果不支持原子操作,則我們使用文件鎖來實現,這里 fd 表示進程間共享的文件句柄,name 表示文件名

原子鎖創建

原子鎖獲取

TryLock,它是非阻塞的,也就是說它會嘗試的獲得鎖,如果沒有獲得的話,它會直接返回錯誤。
Lock,它也會嘗試獲得鎖,而當沒有獲得他不會立即返回,而是開始進入循環然后不停的去獲得鎖,知道獲得。不過 Nginx 這里還有用到一個技巧,就是每次都會讓當前的進程放到 CPU 的運行隊列的最后一位,也就是自動放棄 CPU。

原子鎖實現
如果系統庫支持的情況,此時直接調用OSAtomicCompareAndSwap32Barrier,即 CAS。

如果系統庫不支持這個指令的話,Nginx 自己還用匯編實現了一個。

原子鎖釋放

Unlock 比較簡單,和當前進程 id 比較,如果相等,就把 lock 改為 0,說明放棄這個鎖。

#define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0)  

Nginx 解決方案之驚群效應

變量分析

ngx_use_accept_mutex 這個變量,如果有這個變量,說明 Nginx 有必要使用 accept 互斥體,這個變量的初始化在 ngx_event_process_init 中。
ngx_accept_mutex_held 表示當前是否已經持有鎖。
日本韩国三级aⅴ在线观看ngx_accept_mutex_delay 表示當獲得鎖失敗后,再次去請求鎖的間隔時間,這個時間可以在配置文件中設置的。

ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;

日本韩国三级aⅴ在线观看ngx_accept_disabled,這個變量是一個閾值,如果大于 0,說明當前的進程處理的連接過多。

是否使用鎖

日本韩国三级aⅴ在线观看NGX_POST_EVENTS 標記,設置了這個標記就說明當 socket 有數據被喚醒時,我們并不會馬上 accept 或者說讀取,而是將這個事件保存起來,然后當我們釋放鎖之后,才會進行 accept 或者讀取這個句柄。

如果沒有設置 NGX_POST_EVENTS 標記的話,Nginx 會立即 Accept 或者讀取句柄

日本韩国三级aⅴ在线观看定時器,這里如果 Nginx 沒有獲得鎖,并不會馬上再去獲得鎖,而是設置定時器,然后在 epoll 休眠(如果沒有其他的東西喚醒)。此時如果有連接到達,當前休眠進程會被提前喚醒,然后立即 accept。否則,休眠 ngx_accept_mutex_delay時間,然后繼續 tryLock。

獲取鎖來解決驚群

日本韩国三级aⅴ在线观看如上代碼,當一個連接來的時候,此時每個進程的 epoll 事件列表里面都是有該 fd 的。搶到該連接的進程先釋放鎖,在 accept。沒有搶到的進程把該 fd 從事件列表里面移除,不必再調用 accept,造成資源浪費。

同時由于鎖的控制(以及獲得鎖的定時器),每個進程都能相對公平的 accept 句柄,也就是比較好的解決了子進程負載均衡。


文章分類: LinuxNginx
分享到:
支付寶贊助-Java幫幫社區
微信贊助-Java幫幫社區
Java幫幫公眾號生態

Java幫幫公眾號生態

總有一款適合你

Java幫幫-微信公眾號

Java幫幫-微信公眾號

將分享做到極致

Python幫幫-公眾號

Python幫幫-公眾號

人工智能,爬蟲,學習教程

大數據驛站-微信公眾號

大數據驛站-微信公眾號

一起在數據中成長

九點編程-公眾號

九點編程-公眾號

深夜九點學編程

程序員生活志-公眾號

程序員生活志-公眾號

互聯網,職場,程序員那些事兒

Java幫幫學習群生態

Java幫幫學習群生態

總有一款能幫到你

Java學習群

Java學習群

與大牛一起交流

大數據學習群

大數據學習群

在數據中成長

九點編程學習群

九點編程學習群

深夜九點學編程

python學習群

python學習群

人工智能,爬蟲

測試學習群

測試學習群

感受測試的魅力

Java幫幫生態承諾

Java幫幫生態承諾

一直堅守,不負重望

初心
勤儉
誠信
正義
分享
合作品牌 非盈利生態-優質內容分享傳播者
關于我們
友鏈申請
友鏈交換:加幫主QQ2524138991 留言即可 24小時內答復  
全站內容非商業用途,內容來源于網友,并遵循 許可,如有異議請聯系客服。
會員登錄
獲取驗證碼
登錄
登錄
我的資料
留言
回到頂部