无码av一区二区三区无码,在线观看老湿视频福利,日韩经典三级片,成 人色 网 站 欧美大片在线观看

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

一文淺析Nginx線程池!

2023-03-23 21:32 作者:補給站Linux內核  | 我要投稿

Nginx通過使用多路復用IO(如Linux的epoll、FreeBSD的kqueue等)技術很好的解決了c10k問題,但前提是Nginx的請求不能有阻塞操作,否則將會導致整個Nginx進程停止服務。

但很多時候阻塞操作是不可避免的,例如客戶端請求靜態(tài)文件時,由于磁盤IO可能會導致進程阻塞,所以將會導致Nginx的性能下降。為了解決這個問題,Nginx在1.7.11版本中實現(xiàn)了線程池機制。

下面我們將會分析Nginx是怎么通過線程池來解決阻塞操作問題。

啟用線程池功能

要使用線程池功能,首先需要在配置文件中添加如下配置項:

上面定義了一個名為“default”,包含32個線程,任務隊列最多支持65536個請求的線程池。如果任務隊列過載,Nginx將輸出如下錯誤日志并拒絕請求:

如果出現(xiàn)上面的錯誤,說明線程池的負載很高,這是可以通過添加線程數(shù)來解決這個問題。當達到機器的最高處理能力之后,增加線程數(shù)并不能改善這個問題。

一切從“源”開始

下面主要通過剖析Nginx的源碼來了解線程池機制實現(xiàn)原理。現(xiàn)在先來了解Nginx線程池的兩個重要數(shù)據(jù)結構ngx_thread_pool_t和ngx_thread_task_t。

ngx_thread_pool_t結構體

下面解釋下每個字段的用途:

  1. mtx: 互斥鎖,用于鎖定任務隊列,避免競爭狀態(tài)。

  2. queue: 任務隊列。

  3. waiting: 有多少個任務正在等待處理。

  4. cond: 用于通知線程池有任務需要處理。

  5. name: 線程池名稱。

  6. threads: 線程池由多少個線程組成(線程數(shù))。

  7. max_queue: 線程池最大能處理的任務數(shù)。

ngx_thread_task_t結構體

下面解釋下每個字段的用途:

  1. mtx: 互斥鎖,用于鎖定任務隊列,避免競爭狀態(tài)。

  2. queue: 任務隊列。

  3. waiting: 有多少個任務正在等待處理。

  4. cond: 用于通知線程池有任務需要處理。

  5. name: 線程池名稱。

  6. threads: 線程池由多少個線程組成(線程數(shù))。

  7. max_queue: 線程池最大能處理的任務數(shù)。


【文章福利】小編推薦自己的Linux內核技術交流群:【749907784】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)??

ngx_thread_task_t結構體

下面解釋下每個字段的用途:

  1. next: 指向下一個任務。

  2. id: 任務ID。

  3. ctx: 任務的上下文。

  4. handler: 處理任務的函數(shù)句柄。

  5. event: 跟任務關聯(lián)的事件對象(當線程池處理成任務之后將會由主線程調用event對象的handler回調函數(shù))。

線程池初始化

下面介紹下線程池的初始化過程。

在Nginx啟動的時候,首先會調用ngx_thread_pool_init_worker()函數(shù)來初始化線程池。ngx_thread_pool_init_worker()函數(shù)最終會調用ngx_thread_pool_init(),源碼如下:

ngx_thread_pool_init()最終調用pthread_create()函數(shù)創(chuàng)建線程池中的工作線程,工作線程會從ngx_thread_pool_cycle()函數(shù)開始執(zhí)行。

ngx_thread_pool_cycle()函數(shù)源碼如下:

ngx_thread_pool_cycle()函數(shù)的主要工作是從待處理的任務隊列中獲取一個任務,然后調用任務對象的handler()函數(shù)處理任務,完成后把任務放置到完成隊列中,并通過ngx_notify()通知主線程。

添加任務到任務隊列

通過上面的分析,我們知道了線程池是怎么從任務隊列獲取任務并處理。但任務隊列的任務從哪里來的呢?因為Nginx的使命是處理客戶端請求,所以可以知道任務是通過客戶端請求產生的。也就是說,任務是主線程創(chuàng)建的(主線程負責處理客戶端請求)。

主線程通過ngx_thread_task_post()函數(shù)向任務隊列中添加一個任務,代碼如下:

ngx_thread_task_post()函數(shù)首先調用ngx_thread_cond_signal()通知線程池的線程有任務需要處理,然后把任務添加到任務隊列中??赡苡腥藭?,先通知線程池在添加任務到任務隊列中會不會有順序問題。其實這樣做是沒問題的,這是因為只要主線程不調用ngx_thread_mutex_unlock()把互斥鎖解開,線程池中的工作線程是不會從ngx_thread_cond_wait()返回的。

收尾工作

當線程池把任務處理完后會把其放置到完成隊列中(ngx_thread_pool_done),然后調用ngx_notify()通知主線程有任務完成了。主線程收到通知后,會在事件模塊中進行收尾工作:調用task.event.handler()。task.event.handler由任務創(chuàng)建者設置,例如在ngx_http_copy_filter模塊的ngx_http_copy_thread_handler()函數(shù):

task.event.handler被設置為ngx_http_copy_thread_event_handler,就是說當任務處理完成后,主線程將會調用ngx_http_copy_thread_event_handler來進行收尾工作。

哪些操作會使用線程池

那么哪些操作會使用線程池去處理。一般來說,磁盤IO會使用線程池來處理。在ngx_http_copy_filter模塊中,會調用ngx_thread_read()讀取文件的內容(當啟用了線程池時),而ngx_thread_read()會把讀取文件內容的操作讓線程池去處理。ngx_thread_read()代碼如下:

從上面的代碼看到,task的handler被設置為ngx_thread_read_handler,也就是說在線程池中將會調用ngx_thread_read_handler()去讀取文件內容。而file->thread_handler()將會調用ngx_thread_task_post(),前面已經分析過,ngx_thread_task_post()會把任務添加到任務隊列中。

圖解

最后用一張圖來解釋Nginx線程池機制的原理吧。

原文作者:Linux內核那些事


一文淺析Nginx線程池!的評論 (共 條)

分享到微博請遵守國家法律
垣曲县| 仁寿县| 辽阳市| 响水县| 肃宁县| 桦南县| 邵阳县| 龙陵县| 大竹县| 房山区| 焦作市| 古交市| 习水县| 涞水县| 曲水县| 崇明县| 寻乌县| 郑州市| 梧州市| 西吉县| 准格尔旗| 乳山市| 日喀则市| 巴里| 华蓥市| 平利县| 乳源| 万州区| 浙江省| 滦南县| 泸溪县| 乐亭县| 石楼县| 天门市| 乐东| 七台河市| 甘孜县| 积石山| 新巴尔虎左旗| 闻喜县| 宁明县|