最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(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線程池!的評論 (共 條)

分享到微博請遵守國家法律
灯塔市| 商洛市| 东乡| 大新县| 石渠县| 邢台县| 娱乐| 昆明市| 交口县| 临朐县| 泗洪县| 南通市| 山西省| 苗栗市| 阿巴嘎旗| 辛集市| 两当县| 黑水县| 彭山县| 荃湾区| 涪陵区| 厦门市| 旅游| 东莞市| 勃利县| 莎车县| 黄陵县| 醴陵市| 迁西县| 清新县| 吴旗县| 商河县| 英吉沙县| 揭阳市| 邯郸县| 衡南县| 玛纳斯县| 卢氏县| 弥渡县| 苏尼特右旗| 青岛市|