nginx代碼分析之 轉載

導讀:

  nginx代碼分析之(一)——初探

  不知道原創,作者聯繫下我吧。

  他的代碼我全看過了,感覺是高層模型下做的最優精簡指令。效果還是不錯的。

  發現nginx是無意間在瀏覽器中看到新浪的一個錯誤頁面“nginx ...”,不由起了好奇心,google了一把,發現這是一個支持負載均衡的反向代理服務器,俄羅斯人開發的,雖然沒有走GNU或BSD的License,但是也算是一個開源軟件。

  開源代理服務器最熟悉的還是Squid和Apache,但這兩者都是正反向代理通吃的,而作爲反向代理,實際上和正向代理有較大的差別。我想既然新浪也用它,那自然有它的獨到之處。查了一下,中文的網頁上說它的HTTP性能可以達到13000TPS以上,但是沒有說明數據的出處,國外的網站上暫時找不到相應的數據,但很多人拿它和lighttpd相比。

  很快下載了nginx 0.5.32版本的代碼,代碼不多,才8萬多行,在openssl的基礎上支持HTTPS。和Apache的30多萬行相比,精簡了很多,

  作爲web server或反向代理,要的就是一個快,要做到快,除了精簡的代碼之外,更關鍵的一點就是併發模型。

  Apache的弱點就在於它的併發模型是普通的進程/線程池,連接數和進程/線程數是1:1的,因此無論是prefork還是worker模式,都將每一個連接對應到一個獨立的進程/線程。

  這樣的併發模型在連接數不太多(1000以內)時還算可以,但在大規模併發時,其進程/線程總數會非常多。由於Apache本身也比較吃內存,所以到了1000以上的併發時,服務器的內存基本上也就被吃的差不多了,操作系統也在頻繁地做進程/線程的切換,非常吃力。

  相比之下,更高級的大型網絡服務系統(如電信的智能網系統)一般採用進程/線程池+狀態機的模型——也即接數和進程/線程數是m:n的,這樣進程/線程總數就不會由於連接的增多而增多,避免了內存和調度切換的開銷,但這種做法對程序邏輯的要求較高,需要一個連接拆分爲多個邏輯狀態(創建,讀,寫,關閉等,根據實際業務還可以更加細化)每個進程/線程處理完某一種狀態後,需要改變該連接的狀態值,後續狀態由下一個空閒的進程/線程處理。

  nginx就採用了這樣的併發模型,對於連接狀態的存儲,nginx主要採用了這樣一個複雜結構。

  struct ngx_connection_s {

  void *data;

  ngx_event_t *read;

  ngx_event_t *write;

  ...

  };

  結構ngx_event_t存儲了連接IO狀態的詳細信息,同時所有的ngx_event_t組成了兩個全局的鏈表,以便進行存取操作。

  在這兩個數據結構的基礎上,nginx使用了下面這兩個函數來完成每個進程/線程的循環

  1. ngx_locked_post_event

  這個函數負責更新某一個連接的狀態,在檢查到連接IO狀態改變(比如通過select)後被調用。

  nginx以module的方式提供了select語義的多種實現:

  poll

  devpoll

  epoll

  eventport

  kqueue

  rtsig

  後面4種,都是BSD/Linux爲加速IO操作而提供的異步IO模型

  2. ngx_event_thread_process_posted

  這個函數檢查event表,並調用event對應的handler函數,每次處理1個event。

  這兩個函數組合使用,就實現了最基本的m:n併發模型。

  nginx代碼分析之(二)——Empty Gif是如何工作的

  訪問新浪時,時常會有一些網頁返回空白(但不是“此頁無法顯示”),從瀏覽器的信息中可以知道此時服務器返回了一個1×1的空白gif圖片。

  這實際上是nginx實現的,nginx有一個名爲Empty Gif的module,專門負責此項工作。

  由於這個module比較簡單,我們就先從它入手,來看看nginx的模塊實現。

  

  模塊註冊Empty Gif這個module只有一個文件——ngx_http_empty_gif_module.c

  這個文件比較簡單,一開始定義並初始化了3個變量。

  static ngx_command_t ngx_http_empty_gif_commands[] = {...};

  static ngx_http_module_t ngx_http_empty_gif_module_ctx = {...};

  ngx_module_t ngx_http_empty_gif_module = {...};

  其中只有ngx_http_empty_gif_module是非靜態的,我將暫時將其稱爲module主結構變量,

  而其餘兩個變量都可以由它訪問到。

  但是如果繼續查看nginx的源碼,會發現沒有其他地方引用ngx_http_empty_gif_module,

  那這個module是怎麼註冊並應用起來的呢?

  如果熟悉Apache的代碼,會發現這和Apache 2.0的module機制非常類似——每個module都對應到一個module主結構變量,通過這個主結構變量可以訪問到這個module的其他內容,該module所有的函數也用函數指針的方式存放在這些結構變量中。

  而且Apache同樣沒有其他地方的代碼引用到module主結構變量。這是因爲module不是必須的,該module在某一個特定的編譯版本里是可以不存在的。因此一個module是否有效,不是通過代碼來決定,而是通過編譯選項來實現。

  在nginx代碼的auto目錄中,有一個名爲sources的文件,根據編譯選項(configure的參數)的不同,m4宏變量HTTP_MODULES的值會發生變化:

  如果指定了使用empty gif模塊(默認就是使用了),則最終m4宏變量HTTP_MODULES的值可能如下:

  HTTP_MODULES="ngx_http_module /

  ngx_http_core_module /

  ngx_http_log_module /

  ngx_http_upstream_module /

  ngx_http_empty_gif_module "

  注意:這裏的ngx_http_empty_gif_module字符串對應了ngx_http_empty_gif_module.c文件中的Module主結構變量名。

  編譯之前的configure結束後,會在objs目錄下生成一個名爲ngx_modules.c的文件,此文件的內容如下:

  #include

  #include

  extern ngx_module_t ngx_core_module;

  extern ngx_module_t ngx_errlog_module;

  extern ngx_module_t ngx_conf_module;

  ...

  extern ngx_module_t ngx_http_empty_gif_module;

  ...

  ngx_module_t *ngx_modules[] = {

  &ngx_core_module,

  &ngx_errlog_module,

  &ngx_conf_module,

  ...

  &ngx_http_empty_gif_module,

  ...

  NULL

  };

  在此生成了對ngx_http_empty_gif_module變量的引用,並將其放到了ngx_modules表中,

  通過相關函數可以進行存取。

  這樣,在編譯時就完成了Empty Gif模塊註冊的過程。

  模塊的初始化和應用初始化一般都是根據配置文件的內容來進行,但和我們一般寫程序的做法不同——nginx並沒有在一個統一的地方處理所有的配置,而是讓每個模塊負責處理自己的配置項,如果沒有編譯這個模塊,則其對應的配置項就無法處理,這也是又一個和Apache的相似之處。

  nginx使用了ngx_command_t結構來描述某一個模塊對應的配置項及處理函數。

  以Empty Gif模塊爲例:

  static ngx_command_t ngx_http_empty_gif_commands[] = {

  { ngx_string("empty_gif"),

  NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

  ngx_http_empty_gif,

  0,

  0,

  NULL },

  0);"> ngx_null_command

  };

  上面的定義表明:

  1. Empty Gif模塊只處理一個配置項——“empty_gif”

  2. 這個配置是一個Location相關的配置(NGX_HTTP_LOC_CONF),

  即只有在處理某一個URL子集,如 /test_[0-9]*.gif時才生效。

  

  實際的配置文件可能如下:

  location ~ /test_[0-9].gif {

  empty_gif;

  }

  3. 這個配置項不帶參數(NGX_CONF_NOARGS)

  4. 配置處理函數是ngx_http_empty_gif

  ngx_http_empty_gif函數的實現很簡單:

  static char *

  ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

  {

  ngx_http_core_loc_conf_t *clcf;

  clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  clcf->handler = ngx_http_empty_gif_handler;

  return NGX_CONF_OK;

  }

  ngx_http_conf_get_module_loc_conf是一個宏,用於獲得Location相關的配置表cf中ngx_http_core_module對應的項,獲取之後,Empty Gif模塊將自己的處理函數掛到了ngx_http_core_module對應的handler上。

  這樣,nginx在處理HTTP請求時,如果發現其URL匹配到Empty Gif所屬的Location,

  如URL(/test_1.gif)匹配到Location(/test_[0-9].gif),

  則使用ngx_http_empty_gif作爲處理函數,這個函數直接向瀏覽器寫回一幅1×1的空白gif圖片。



本文轉自

http://www.sameparty.com/x/?action=show&id=626
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章