開發(fā)一個Nginx模塊(C語言和c++版本)

開發(fā)第一個Nginx模塊

  1. 首先在/src下建立文件夾mymodule

  2. 配置config文件

    • config文件實際上是shell腳本

    • 開發(fā)一個HTTP模塊需要包含如下變量

        #僅僅在configure使用碱呼,一般是模塊名
        ngx_addon_name=ngx_mymodule
        
        #保存所有模塊名的數(shù)組证薇,不能直接賦值覆蓋攒驰,因為還有其他模塊在里面
        HTTP_MODULES="$HTTP_MODULES ngx_http_mymodule_module"
        
        #新增模塊的源碼文件
        NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mymodule_module.c"
      
  3. /src/mymodule/下創(chuàng)建源文件ngx_http_mymodule_module.c,必須和上面配置的一樣

  4. 測試下
    #執(zhí)行configure使用--add-module=src/mymodule添加自己開發(fā)的模塊 ./configure --add-module=src/mymodule/ --without-http_rewrite_module --without-http_gzip_module

    configures

    可見已經(jīng)添加了mymodule模塊憾股,由于還沒有寫代碼负芋,就不make install

  5. 編寫自己的模塊(C語言版本)

    • 大致流程

      • 1,nginx讀取到配置文件時,發(fā)現(xiàn)mymodule模塊
      • 2,調(diào)用ngx_http_mymodule_commands指定的ngx_http_mymodule回調(diào)函數(shù)
      • 3,ngx_http_mymodule回調(diào)時設(shè)置處理HTTP的回調(diào)函數(shù)ngx_http_mymodule_handler
- 首先引入需要的nginx核心模塊

            ```
            #include <ngx_config.h>
            #include <ngx_core.h>
            #include <ngx_http.h>
            #include <ngx_string.h>
            #include <ngx_http_request.h>
            #include <ngx_hash.h>
- 定義一個Nginx的HTTP模塊 ngx_module_t,結(jié)構(gòu)體說明如下:
            
            struct ngx_module_s {
                //表示當前模塊在這類模塊中的序號,對于所有http模塊,
                //ctx_index是由核心模塊ngx_http_module設(shè)置的
                //表達優(yōu)先級和模塊位置
                ngx_uint_t            ctx_index;
                //表示當前模塊在所有模塊中的序號
                ngx_uint_t            index;
                
                //保留字段,未使用
                ngx_uint_t            spare0;
                ngx_uint_t            spare1;
                ngx_uint_t            spare2;
                ngx_uint_t            spare3;
                //版本,目前只有1
                ngx_uint_t            version;
                //用于指向一類模塊的上下文結(jié)構(gòu)體,模塊上下文。指向特定類型模塊的公共接口
                void                 *ctx;
                //將處理nginx.conf中的配置項
                ngx_command_t        *commands;
                //模塊類型,HTTP_FILTER_MODULES劝赔,CORE_MODULES,EVENT_MODULES胆敞,HTTP_MODULES着帽,HTTP_HEADERS_FILER_MODULE
                //HTTP_FILTER_MODULES         --> http過濾模塊
                //
                //CORE_MODULES                --> 核心模塊
                //
                //EVENT_MODULES               --> 事件模塊
                //
                //HTTP_MODULES                --> HTTP模塊
                //
                //HTTP_HEADERS_FILER_MODULE   --> HTTP頭部過濾模塊
                //還可以自定義新的模塊
                ngx_uint_t            type;
                
                /**
                * 七個重要的模塊回調(diào)點
                */
                //master進程啟動時調(diào)用杂伟,但是目前框架從不調(diào)用,因此直接設(shè)置成NULL就行
                ngx_int_t           (*init_master)(ngx_log_t *log);
                //初始化所有模塊時被調(diào)用仍翰,master/worker模式下赫粥,在啟動worker前完成
                ngx_int_t           (*init_module)(ngx_cycle_t *cycle);
                //worker子進程已經(jīng)產(chǎn)生,每個worker進程初始化過程會調(diào)用所有模塊的init_process
                ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
                //由于nginx不支持多線程模式歉备,所以init_thread在框架中沒被調(diào)用過
                ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
                //此函數(shù)也沒被調(diào)用
                void                (*exit_thread)(ngx_cycle_t *cycle);
                //worker進程退出前調(diào)用
                void                (*exit_process)(ngx_cycle_t *cycle);
                //master退出前被調(diào)用
                void                (*exit_master)(ngx_cycle_t *cycle);
                
                //尚未使用傅是,用NGX_MODULE_V1_PADDING填充即可
                uintptr_t             spare_hook0;
                uintptr_t             spare_hook1;
                uintptr_t             spare_hook2;
                uintptr_t             spare_hook3;
                uintptr_t             spare_hook4;
                uintptr_t             spare_hook5;
                uintptr_t             spare_hook6;
                uintptr_t             spare_hook7;
            };
            ```
- 代碼如下:

            ```
            static ngx_int_t ngx_http_mymodule_handler(ngx_http_request_t *r);
            static char *
            ngx_http_mymodule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
            static ngx_command_t ngx_http_mymodule_commands[] = {
                    {
                            ngx_string("mymodule"),
                            NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
                            //set回調(diào)函數(shù)匪燕,
                            //char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
                            //當某個配置快中出現(xiàn)mymodule時蕾羊,就會回調(diào)此函數(shù)
                            ngx_http_mymodule,
                            NGX_HTTP_LOC_CONF_OFFSET,
                            0,
                            NULL
                    },
                    //空的ngx_command_t用于表示數(shù)組結(jié)束
                    //#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }
                    ngx_null_command
            
            };
            static ngx_http_module_t ngx_http_mymodule_module_ctx = {
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL
            };
            
            ngx_module_t ngx_http_mymodule_module = {
                    NGX_MODULE_V1,
                    //ctx,對于HTTP模塊來說,ctx必須是ngx_http_module_t接口
                    &ngx_http_mymodule_module_ctx,
                    //commands,
                    ngx_http_mymodule_commands,
                    //定義http模塊時帽驯,必須設(shè)置成NGX_HTTP_MODULE
                    NGX_HTTP_MODULE,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    NGX_MODULE_V1_PADDING
            };
            
            //配置項對應(yīng)的回調(diào)函數(shù)
            static char *
            ngx_http_mymodule(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);
            
                //在NGX_HTTP_CONTENT_PHASE階段會調(diào)用此回調(diào)函數(shù)
                clcf->handler = ngx_http_mymodule_handler;
            
                return NGX_CONF_OK;
            }
            
            //實際完成處理的回調(diào)函數(shù)
            /*
             * r 是nginx已經(jīng)處理完了的http請求頭
             */
            static ngx_int_t ngx_http_mymodule_handler(ngx_http_request_t *r)
            {
                return NGX_HTTP_NOT_ALLOWED;
            }
        ```
    
- 測試下
    ```
        ./configure --add-module=src/mymodule/ --without-http_rewrite_module --without-http_gzip_module
        make install
    ```
    配置nginx.conf
        
        location /test{
            mymodule;
         }
    由于直接返回了NGX_HTTP_NOT_ALLOWED(405狀態(tài)碼)
    ![405測試](http://upload-images.jianshu.io/upload_images/9669276-8f69f37c14c1a9ec?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  1. 實現(xiàn)對http請求的處理

         if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
     //非法請求方式 狀態(tài)碼 405
             return NGX_HTTP_NOT_ALLOWED;
         }
     //丟棄客戶端發(fā)送來的HTTP包體內(nèi)容
         ngx_int_t rc = ngx_http_discard_request_body(r);
         if (rc != NGX_OK) {
             return rc;
         }
     
         ngx_str_t type = ngx_string("text/plain");
         ngx_str_t response = ngx_string("codelover");
         r->headers_out.status = NGX_HTTP_OK;
         r->headers_out.content_length_n = response.len;
         r->headers_out.content_type = type;
     //自定義響應(yīng)頭
         ngx_table_elt_t* p = ngx_list_push(&r->headers_out.headers);
         p->hash = 1;
         p->key.len = sizeof("codelover")-1;
         p->key.data = (u_char*)"codelover";
         p->value.len = sizeof("codelover")-1;
         p->value.data = (u_char*)"codelover";
     
     //發(fā)送響應(yīng)頭
         rc = ngx_http_send_header(r);
         if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
             return rc;
         }
     
         ngx_buf_t *b;
     //r->pool內(nèi)存池
         b = ngx_create_temp_buf(r->pool, response.len);
         if (b == NULL) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
     
         ngx_memcpy(b->pos, response.data, response.len);
     //必須設(shè)置好last指針龟再,如果last和pos相等,是不會發(fā)送的
         b->last = b->pos + response.len;
     //聲明這是最后一塊緩沖區(qū)
         b->last_buf = 1;
     
         ngx_chain_t out;
         out.buf = b;
         out.next = NULL;
     
         return ngx_http_output_filter(r, &out);
         
    ![mymodule](http://upload-images.jianshu.io/upload_images/9669276-0e1ef3e227987a81?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    

    至此尼变,一個簡單的nginx模塊就開發(fā)完成利凑。

  2. 修改成c++版本

    • 1,編譯方式修改

      最好不要修改configure文件,所以修改Makefile文件:

        #新增g++編譯器嫌术,前提是已經(jīng)在系統(tǒng)安裝g++
        CXX = g++
        #修改連接器為g++
        LINK = $(CXX)
        #修改自己的模塊的編譯方式為g++
        objs/addon/mymodulecpp/ngx_http_mymodulecpp_module.o:   $(ADDON_DEPS) \
            src/mymodulecpp//ngx_http_mymodulecpp_module.cpp
            $(CXX) -c $(CFLAGS)  $(ALL_INCS) \
                -o objs/addon/mymodulecpp/ngx_http_mymodulecpp_module.o \
                src/mymodulecpp//ngx_http_mymodulecpp_module.cpp
      
    • 2,源碼修改
      把關(guān)于nginx的頭文件使用extern "C" 括起來哀澈,保證編譯時使用的是gcc,部分回調(diào)函數(shù)也要括起來

        extern "C"{
            #include <ngx_config.h>
            #include <ngx_core.h>
            #include <ngx_http.h>
            #include <ngx_string.h>
            #include <ngx_http_request.h>
            #include <ngx_hash.h>
            #include <ngx_http_config.h>
        static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r);
        static char *
        ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
        }
      
    • 3,可能的錯誤

      這里寫圖片描述

      這是由于g++不支持將void* 和其他類型的指針進行隱式轉(zhuǎn)換度气,因此需要修改
      clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
      ngx_table_elt_t* p = (ngx_table_elt_t*)ngx_list_push(&r->headers_out.headers);
      進行強制轉(zhuǎn)換

    • 4,參考代碼

      extern "C"{
          #include <ngx_config.h>
          #include <ngx_core.h>
          #include <ngx_http.h>
          #include <ngx_string.h>
          #include <ngx_http_request.h>
          #include <ngx_hash.h>
          #include <ngx_http_config.h>
      static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r);
      static char *
      ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
      }
      //只是包含了c++的庫
      //#include <iostream>
      //#include <string>
      //using namespace std;
      #include "ngx_http_mymodulecpp_module.h"
      
      static ngx_command_t ngx_http_mymodulecpp_commands[] = {
              {
                      ngx_string("mymodulecpp"),
                      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
                      //set回調(diào)函數(shù)割按,
                      //char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
                      //當某個配置快中出現(xiàn)mymodulecpp時,就會回調(diào)此函數(shù)
                      ngx_http_mymodulecpp,
                      NGX_HTTP_LOC_CONF_OFFSET,
                      0,
                      NULL
              },
              //空的ngx_command_t用于表示數(shù)組結(jié)束
              //#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }
              ngx_null_command
      
      };
      static ngx_http_module_t ngx_http_mymodulecpp_module_ctx = {
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL
      };
      
      ngx_module_t ngx_http_mymodulecpp_module = {
              NGX_MODULE_V1,
              //ctx,對于HTTP模塊來說磷籍,ctx必須是ngx_http_module_t接口
              &ngx_http_mymodulecpp_module_ctx,
              //commands,
              ngx_http_mymodulecpp_commands,
              //定義http模塊時适荣,必須設(shè)置成NGX_HTTP_MODULE
              NGX_HTTP_MODULE,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL,
              NGX_MODULE_V1_PADDING
      };
      
      //配置項對應(yīng)的回調(diào)函數(shù)
      static char *
      ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
      {
          ngx_http_core_loc_conf_t *clcf;
      
          clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
      
          //在NGX_HTTP_CONTENT_PHASE階段會調(diào)用此回調(diào)函數(shù)
          clcf->handler = ngx_http_mymodulecpp_handler;
      
          return NGX_CONF_OK;
      }
      //實際完成處理的回調(diào)函數(shù)
      /*
       * r 是nginx已經(jīng)處理完了的http請求頭
       */
      static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r)
      {
      
          if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
      //非法請求方式 狀態(tài)碼 405
              return NGX_HTTP_NOT_ALLOWED;
          }
      //丟棄客戶端發(fā)送來的HTTP包體內(nèi)容
          ngx_int_t rc = ngx_http_discard_request_body(r);
          if (rc != NGX_OK) {
              return rc;
          }
          //測試c++語法是否支持
          string* a = new string("cpp");
          ngx_str_t type = ngx_string("text/plain");
          ngx_str_t response = ngx_string(a->c_str());
          r->headers_out.status = NGX_HTTP_OK;
          r->headers_out.content_length_n = a->length();
          r->headers_out.content_type = type;
      //自定義響應(yīng)頭
          ngx_table_elt_t* p = (ngx_table_elt_t*)ngx_list_push(&r->headers_out.headers);
          p->hash = 1;
      
          p->key.len = sizeof("codelover")-1;
          p->key.data = (u_char*)"codelover";
          p->value.len = sizeof("codelover")-1;
          p->value.data = (u_char*)"codelover";
      
      //發(fā)送響應(yīng)頭
          rc = ngx_http_send_header(r);
          if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
              return rc;
          }
      
          ngx_buf_t *b;
      //r->pool內(nèi)存池
          b = ngx_create_temp_buf(r->pool, response.len);
          if (b == NULL) {
              return NGX_HTTP_INTERNAL_SERVER_ERROR;
          }
      
          ngx_memcpy(b->pos, response.data, response.len);
      //必須設(shè)置好last指針,如果last和pos相等院领,是不會發(fā)送的
          b->last = b->pos + response.len;
      //聲明這是最后一塊緩沖區(qū)
          b->last_buf = 1;
      
          ngx_chain_t out;
          out.buf = b;
          out.next = NULL;
      
          return ngx_http_output_filter(r, &out);
      }
      

    這里寫圖片描述

    至此就完成了使用c++進行模塊開發(fā)的例子
    github 代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弛矛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子比然,更是在濱河造成了極大的恐慌丈氓,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强法,死亡現(xiàn)場離奇詭異扒寄,居然都是意外死亡,警方通過查閱死者的電腦和手機拟烫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門该编,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人硕淑,你說我怎么就攤上這事课竣〖问辏” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵于樟,是天一觀的道長公条。 經(jīng)常有香客問我,道長迂曲,這世上最難降的妖魔是什么靶橱? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮路捧,結(jié)果婚禮上关霸,老公的妹妹穿的比我還像新娘。我一直安慰自己杰扫,他們只是感情好队寇,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著章姓,像睡著了一般佳遣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凡伊,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天零渐,我揣著相機與錄音,去河邊找鬼系忙。 笑死诵盼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的笨觅。 我是一名探鬼主播拦耐,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼见剩!你這毒婦竟也來了杀糯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤苍苞,失蹤者是張志新(化名)和其女友劉穎固翰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羹呵,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡骂际,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冈欢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歉铝。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖凑耻,靈堂內(nèi)的尸體忽然破棺而出太示,到底是詐尸還是另有隱情柠贤,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布类缤,位于F島的核電站臼勉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏餐弱。R本人自食惡果不足惜宴霸,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膏蚓。 院中可真熱鬧瓢谢,春花似錦、人聲如沸降允。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剧董。三九已至,卻和暖如春破停,著一層夾襖步出監(jiān)牢的瞬間翅楼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工真慢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毅臊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓黑界,卻偏偏與公主長得像管嬉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子朗鸠,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx蚯撩?那么一定聽過它的“同行”Apache吧!Ngi...
    JokerW閱讀 32,650評論 24 1,002
  • 前言 Nginx是當前最流行的HTTP Server之一烛占,根據(jù)W3Techs的統(tǒng)計胎挎,目前世界排名(根據(jù)Alexa)...
    GarfieldEr007閱讀 5,304評論 4 22
  • HTTP模塊的調(diào)用 worker 進程會在一個for 循環(huán)里面反復(fù)調(diào)用事件模塊檢測網(wǎng)絡(luò)事件。 基本數(shù)據(jù)結(jié)構(gòu) 對整形...
    Spike_3154閱讀 1,576評論 0 1
  • 框架代碼分析 核心模塊 啟動過程(main) 1忆家、全局ngx_cycle_t對象 1犹菇、ngx_init_cycle...
    AKEEM閱讀 1,077評論 1 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)芽卿,斷路器揭芍,智...
    卡卡羅2017閱讀 134,637評論 18 139