1. 解決方式
每個worker進程被建立的時候,都會呼叫ngx_worker_process_init()方法初始化目前worker進程,這個過程中有一個非常重要的步驟,也就是每個worker程序都會呼叫epoll_create()方法為自己建立一個獨特的epoll句柄。對於每一個需要監聽的端口,都有一個文件描述符與之對應,而worker進程只有將該文件描述符通過epoll_ctl()方法添加到當前進程的epoll句柄中,並且監聽accept事件,此時才會被客戶端的連線建立事件觸發,從而處理該事件。從這裡也可以看出,worker進程如果沒有將所需要監聽的連接埠對應的檔案描述子加入到該進程的epoll句柄中,那麼其是無法被觸發對應的事件的。基於這個原理,nginx就使用了一個共享鎖定來控制當前進程是否有權限將需要監聽的連接埠加入到目前進程的epoll句柄中,也就是說,只有取得鎖定的進程才會監聽目標連接埠。透過這種方式,就確保了每次事件發生時,只有一個worker進程會被觸發。如下圖所示為worker進程工作循環的示意圖:
這裡關於圖中的流程,需要說明的一點是,每個worker進程在進入循環之後就會嘗試取得共享鎖,如果沒有取得到,就會將所監聽的連接埠的檔案描述符從目前進程的epoll句柄中移除(即使不存在也會移除),這麼做的主要目的是防止遺失客戶端連線事件,即使這可能造成少量的驚群問題,但並不嚴重。試想一下,如果按照理論,在當前進程釋放鎖的時候就將監聽的端口的文件描述符從epoll句柄中移除,那麼在下一個worker進程獲取鎖之前,這段時間各個端口對應的文件描述符是沒有任何epoll句柄進行監聽的,此時就會造成事件的遺失。如果反過來,按照圖中的在獲取鎖失敗的時候才移除監聽的文件描述符,由於獲取鎖失敗,則說明當前一定有一個進程已經監聽了這些文件描述符,因而此時移除是安全的。但是這樣會造成的一個問題是,按照上圖,當前進程在一個循環執行完畢的時候,會釋放鎖,然後處理其他的事件,注意這個過程中其是沒有釋放所監聽的文件描述符的。此時,如果另一個進程獲取到了鎖,並且監聽了文件描述符,那麼這個時候就有兩個進程監聽了文件描述符,因而此時如果客戶端發生連接建立事件,那麼就會觸發兩個worker進程。這個問題是可以容忍的,主要原因有兩點:
這種驚群現像只會觸發較少數量的工作進程,相比每次都驚醒所有工作進程要好得多
會發生這種驚群問題的主要原因是,當前進程釋放了鎖,但是沒有釋放所監聽的檔案描述符,但是worker進程在釋放鎖之後主要是處理客戶端連線的讀寫事件和檢查標誌位,這個過程是非常短的,在處理完之後,其就會嘗試獲取鎖,這個時候就會釋放所監聽的文件描述符了,而相較而言,取得鎖的worker進程在等待處理客戶端的連線建立事件的事件就更長了,因而會發生驚群問題的機率還是比較小的。
2. 原始碼講解
worker進程初始事件的方法主要是在ngx_process_events_and_timers()方法中進行的,下面我們就來看看該方法是如何處理整個流程的,如下是該方法的源碼:
void ngx_process_events_and_timers(ngx_cycle_t *cycle) { ngx_uint_t flags; ngx_msec_t timer, delta; if (ngx_trylock_accept_mutex(cycle) == ngx_error) { return; } // 这里开始处理事件,对于kqueue模型,其指向的是ngx_kqueue_process_events()方法, // 而对于epoll模型,其指向的是ngx_epoll_process_events()方法 // 这个方法的主要作用是,在对应的事件模型中获取事件列表,然后将事件添加到ngx_posted_accept_events // 队列或者ngx_posted_events队列中 (void) ngx_process_events(cycle, timer, flags); // 这里开始处理accept事件,将其交由ngx_event_accept.c的ngx_event_accept()方法处理; ngx_event_process_posted(cycle, &ngx_posted_accept_events); // 开始释放锁 if (ngx_accept_mutex_held) { ngx_shmtx_unlock(&ngx_accept_mutex); } // 如果不需要在事件队列中进行处理,则直接处理该事件 // 对于事件的处理,如果是accept事件,则将其交由ngx_event_accept.c的ngx_event_accept()方法处理; // 如果是读事件,则将其交由ngx_http_request.c的ngx_http_wait_request_handler()方法处理; // 对于处理完成的事件,最后会交由ngx_http_request.c的ngx_http_keepalive_handler()方法处理。 // 这里开始处理除accept事件外的其他事件 ngx_event_process_posted(cycle, &ngx_posted_events); }
上面的程式碼中,我們省略了大部分的檢查工作,只留下了骨架程式碼。首先,worker程序會呼叫ngx_trylock_accept_mutex()方法取得鎖,這其中如果取得到了鎖就會監聽各個連接埠對應的檔案描述子。然後會呼叫ngx_process_events()方法處理epoll句柄中監聽到的事件。接著會釋放共享鎖,最後就是處理已建立連線的客戶端的讀寫事件。下面我們來看看ngx_trylock_accept_mutex()方法是如何取得共享鎖的:
ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) { // 尝试使用cas算法获取共享锁 if (ngx_shmtx_trylock(&ngx_accept_mutex)) { // ngx_accept_mutex_held为1表示当前进程已经获取到了锁 if (ngx_accept_mutex_held && ngx_accept_events == 0) { return ngx_ok; } // 这里主要是将当前连接的文件描述符注册到对应事件的队列中,比如kqueue模型的change_list数组 // nginx在启用各个worker进程的时候,默认情况下,worker进程是会继承master进程所监听的socket句柄的, // 这就导致一个问题,就是当某个端口有客户端事件时,就会把监听该端口的进程都给唤醒, // 但是只有一个worker进程能够成功处理该事件,而其他的进程被唤醒之后发现事件已经过期, // 因而会继续进入等待状态,这种现象称为"惊群"现象。 // nginx解决惊群现象的方式一方面是通过这里的共享锁的方式,即只有获取到锁的worker进程才能处理 // 客户端事件,但实际上,worker进程是通过在获取锁的过程中,为当前worker进程重新添加各个端口的监听事件, // 而其他worker进程则不会监听。也就是说同一时间只有一个worker进程会监听各个端口, // 这样就避免了"惊群"问题。 // 这里的ngx_enable_accept_events()方法就是为当前进程重新添加各个端口的监听事件的。 if (ngx_enable_accept_events(cycle) == ngx_error) { ngx_shmtx_unlock(&ngx_accept_mutex); return ngx_error; } // 标志当前已经成功获取到了锁 ngx_accept_events = 0; ngx_accept_mutex_held = 1; return ngx_ok; } // 前面获取锁失败了,因而这里需要重置ngx_accept_mutex_held的状态,并且将当前连接的事件给清除掉 if (ngx_accept_mutex_held) { // 如果当前进程的ngx_accept_mutex_held为1,则将其重置为0,并且将当前进程在各个端口上的监听 // 事件给删除掉 if (ngx_disable_accept_events(cycle, 0) == ngx_error) { return ngx_error; } ngx_accept_mutex_held = 0; } return ngx_ok; }
上面的程式碼中,本質上主要做了三件事:
透過ngx_shmtx_trylock()方法嘗試使用cas方法取得共用鎖定;
#取得鎖定之後則呼叫ngx_enable_accept_events()方法監聽目標連接埠對應的檔案描述子;
- ##如果沒有取得到鎖,則呼叫ngx_disable_accept_events()方法釋放所監聽的檔案描述子。
以上是nginx驚群問題如何解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

NGINX可用於提升網站性能、安全性和可擴展性。 1)作為反向代理和負載均衡器,NGINX可優化後端服務和分擔流量。 2)通過事件驅動和異步架構,NGINX高效處理高並發連接。 3)配置文件允許靈活定義規則,如靜態文件服務和負載均衡。 4)優化建議包括啟用Gzip壓縮、使用緩存和調整worker進程。

NGINXUnit支持多種編程語言,通過模塊化設計實現。 1.加載語言模塊:根據配置文件加載相應模塊。 2.應用啟動:調用語言運行時執行應用代碼。 3.請求處理:將請求轉發給應用實例。 4.響應返回:將處理後的響應返回給客戶端。

NGINX和Apache各有優劣,適合不同場景。 1.NGINX適合高並發和低資源消耗場景。 2.Apache適合需要復雜配置和豐富模塊的場景。通過比較它們的核心特性、性能差異和最佳實踐,可以幫助你選擇最適合需求的服務器軟件。

確認 Nginx 是否啟動的方法:1. 使用命令行:systemctl status nginx(Linux/Unix)、netstat -ano | findstr 80(Windows);2. 檢查端口 80 是否開放;3. 查看系統日誌中 Nginx 啟動消息;4. 使用第三方工具,如 Nagios、Zabbix、Icinga。

要關閉 Nginx 服務,請按以下步驟操作:確定安裝類型:Red Hat/CentOS(systemctl status nginx)或 Debian/Ubuntu(service nginx status)停止服務:Red Hat/CentOS(systemctl stop nginx)或 Debian/Ubuntu(service nginx stop)禁用自動啟動(可選):Red Hat/CentOS(systemctl disable nginx)或 Debian/Ubuntu(syst

如何在 Windows 中配置 Nginx?安裝 Nginx 並創建虛擬主機配置。修改主配置文件並包含虛擬主機配置。啟動或重新加載 Nginx。測試配置並查看網站。選擇性啟用 SSL 並配置 SSL 證書。選擇性設置防火牆允許 80 和 443 端口流量。

服務器無權訪問所請求的資源,導致 nginx 403 錯誤。解決方法包括:檢查文件權限。檢查 .htaccess 配置。檢查 nginx 配置。配置 SELinux 權限。檢查防火牆規則。排除其他原因,如瀏覽器問題、服務器故障或其他可能的錯誤。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

禪工作室 13.0.1
強大的PHP整合開發環境

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Dreamweaver CS6
視覺化網頁開發工具