<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    Nginx 模塊開發(1)―― 一個稍稍能說明問題模塊開發 Step By Step 過程

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接 1. Nginx 介紹        Nginx是俄羅斯人編寫的十分輕量級的HTTP服務器,它的發音為“engine X”, 是一個高性能的HTTP和反向代理服務器,同時也是一個IMAP/POP3/SMTP 代理服務器.Nginx是由俄羅斯人 Igor Sysoev為俄羅斯訪問量第二的 Rambler.ru站點開發的,從2004年開始它已經在該站點運行了七八年了。Igor Sysoev在建立的項目時,使用基于BSD許可。      英文主頁:http://nginx.org.        Nginx以事件驅動的方式編寫,所以有非常好的性能,同時也是一個非常高效的反向代理、負載平衡。      現在,Igor將源代碼以類BSD許可證的形式發布。Nginx因為它的穩定性、豐富的模塊庫、靈活的配置和低系統資源的消耗而聞名.業界一致認為它是Apache+mod_proxy_balancer的輕量級代替者,不僅是因為響應靜態頁面的速度非常快,而且它的模塊數量也非常非常豐富。對proxy 和 rewrite模塊的支持很徹底,還支持mod_fcgi、ssl、vhosts ,適合用來做mongrel clusters的前端HTTP響應。

      nginx做為HTTP服務器,有以下幾項基本特性:

    處理靜態文件,索引文件以及自動索引;打開文件描述符緩沖.
    無緩存的反向代理加速,簡單的負載均衡和容錯.
    FastCGI,簡單的負載均衡和容錯.
    模塊化的結構。包括gzipping, byte ranges, chunked responses,以及 SSI-filter等filter。如果由FastCGI或其它代理服務器處理單頁中存在的多個SSI,則這項處理可以并行運行,而不需要相互等待。
    支持SSL 和 TLSSNI.

      Nginx專為性能優化而開發,性能是其最重要的考量,實現上非常注重效率。它支持內核 epoll、kqueue 等高性能并發模型,能經受高負載的考驗。

      Nginx具有很高的穩定性。其它HTTP服務器,當遇到訪問的峰值,或者有人惡意發起慢速連接時,也很可能會導致服務器物理內存耗盡頻繁交換,失去響 應,只能重啟服務器。例如當前apache一旦上到200個以上進程,web響應速度就明顯非常緩慢了。而Nginx采取了分階段資源分配技術,使得它的 CPU與內存占用率非常低。nginx官方表示保持10,000個沒有活動的連接,它只占2.5M內存,所以類似DOS這樣的攻擊對nginx來說基本上 是毫無用處的。就穩定性而言,nginx比lighthttpd更勝一籌。

      Nginx支持熱部署。它的啟動特別容易, 并且幾乎可以做到7*24不間斷運行,即使運行數個月也不需要重新啟動。你還能夠在不間斷服務的情況下,對軟件版本進行進行升級。

      Nginx采用master-slave模型,能夠充分利用SMP的優勢,且能夠減少工作進程在磁盤I/O的阻塞延遲。當采用select()/poll()調用時,還可以限制每個進程的連接數。

      Nginx代碼質量非常高,代碼很規范,手法成熟, 模塊擴展也很容易。特別值得一提的是強大的Upstream與Filter鏈。 Upstream為諸如reverse proxy,與其他服務器通信模塊的編寫奠定了很好的基礎。而Filter鏈最酷的部分就是各個filter不必等待前一個filter執行完畢。它可以 把前一個filter的輸出做為當前filter的輸入,這有點像Unix的管線。這意味著,一個模塊可以開始壓縮從后端服務器發送過來的請求,且可以在 模塊接收完后端服務器的整個請求之前把壓縮流轉向客戶端。

      當然,nginx還很年輕,多多少少存在一些問題,比如:Nginx是俄羅斯人創建,目前文檔方面還不是很完善.因為文檔大多是俄語,所以文檔方面這也是個障礙.盡管nignx的模塊比較多,但它們還不夠完善。對腳本的支持力度不夠。

      這些問題,nginx的作者和社區都在努力解決,我們有理由相信nginx將繼續以高速的增長率來分享輕量級HTTP服務器市場,會有一個更美好的未來。   2. 準備工作       去官方主頁 http://nginx.org/ 下載最新的Nginx源碼包,這里給出目前最新的源碼包的直接連接: http://nginx.org/download/nginx-1.0.0.tar.gz 

     

    [zieckey@freebsd7.2 ~]$ mkdir nginx
    [zieckey@freebsd7.2 ~]$ cd nginx
    [zieckey@freebsd7.2 ~/nginx]$ wget http://nginx.org/download/nginx-1.0.0.tar.gz
    [zieckey@freebsd7.2 ~/nginx]$ tar zxf nginx-1.0.0.tar.gz
    [zieckey@freebsd7.2 ~/nginx]$ mkdir module_dev_urlquery
    [zieckey@freebsd7.2 ~/nginx]$ cd module_dev_urlquery/
    [zieckey@freebsd7.2 module_dev_urlquery]$


    這里我們下載了 nginx-1.0.0 的源碼,然后準備開發一個 module_dev_urlquery 的module嵌入到nginx中 

    3. 準備好 module_dev_urlquery 模塊的配置文件  一個 nginx module 一般都以個文件夾的方式存在,module源文件和該module的配置文件都在這個文件下面。 這里我們的module叫 module_dev_urlquery,其路徑為 /home/zieckey/nginx/module_dev_urlquery 。 其配置文件名為:config ,其路徑為 /home/zieckey/nginx/module_dev_urlquery/config [zieckey@freebsd7.2 module_dev_urlquery]$ vim config   然后輸入以下內容:

     

    [zieckey@freebsd7.2 module_dev_urlquery]$ vim config
    ngx_addon_name=ngx_http_p2s_module
    HTTP_MODULES="$HTTP_MODULES ngx_http_p2s_module"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_p2s_module.c"

     

    這里三行內容稍稍解釋下: 第一行是表示這個 nginx module 在程序中名字 第二行表示這是一個HTTP module,后面的名字與第一個行保存一致 第三是這個module的源文件路徑,值得說明的是 $ngx_addon_dir 這個變量是 nginx 的內置腳本的內置變量,代表了這個 module 的文件夾的絕對路徑,這里就是 /home/zieckey/nginx/module_dev_urlquery   4. 準備源代碼文件 ngx_http_p2s_module.c  這里的文件名和路徑必須與上面 config 文件中的一致 [zieckey@freebsd7.2 module_dev_urlquery]$ vim ngx_http_p2s_module.c 輸入以下源程序內容:

     


    #include <ngx_core.h>
    #include <ngx_http.h>
    #include <nginx.h>

    typedef struct {
        unsigned long consume;
        char* ini_buf;
        size_t buflen;//the lenght of the ini_buf

    } ngx_http_p2s_conf_t;

    static char *ngx_http_p2s_urlquery_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

    static void *ngx_http_p2s_create_conf(ngx_conf_t *cf);

    static uint8_t* get_raw_http_body( ngx_http_request_t* r, size_t* body_len );

    static ngx_command_t ngx_http_p2s_commands[] =
    {/*{{{*/

        { ngx_string("p2s_urlquery"), //The command name, it MUST BE the same as nginx.conf location block's command

        NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
        ngx_http_p2s_urlquery_set,
        0,
        0,
        NULL },

        ngx_null_command
    };/*}}}*/

    static ngx_http_module_t ngx_http_p2s_module_ctx =
    {/*{{{*/
        NULL, /* preconfiguration */ 
        NULL, /* postconfiguration */

        NULL, /* create main configuration */
        NULL, /* init main configuration */

        NULL, /* create server configuration */
        NULL, /* merge server configuration */

        ngx_http_p2s_create_conf, /* create location configration */
        NULL /* merge location configration */
    };/*}}}*/

    ngx_module_t ngx_http_p2s_module =
    {/*{{{*/
        NGX_MODULE_V1,
        &ngx_http_p2s_module_ctx, /* module context */
        ngx_http_p2s_commands, /* module directives */
        NGX_HTTP_MODULE, /* module type */
        NULL, /* init master */
        NULL, /* init module */
        NULL, /* init process */
        NULL, /* init thread */
        NULL, /* exit thread */
        NULL, /* exit process */
        NULL, /* exit master */
        NGX_MODULE_V1_PADDING
    };/*}}}*/


    /**
    * @brief Get the HTTP body data from the ngx_http_request_t struct.
    * @warning DONNOT release the return pointer.
    * @param[in] ngx_http_request_t * r -
    * The HTTP request of NGINX struct which holds the HTTP data.
    * @param[out] size_t * body_len - The body data length will stored here.
    * @return uint8_t* - A pointer to a memory where 
    * stored the HTTP body raw binary data.
    * The memory is allocated from nginx memory pool,
    * so the caller don't need to warry about the memory release work.
    */
    static uint8_t* get_raw_http_body( ngx_http_request_t* r, size_t* body_len )
    {/*{{{*/
        printf( "%s\n", __PRETTY_FUNCTION__ );
        ngx_chain_t* bufs = r->request_body->bufs;
        *body_len = 0;

        ngx_buf_t* buf = NULL;
        uint8_t* data_buf = NULL;
        size_t content_length = 0;

        if ( r->headers_in.content_length == NULL )
        {
            return NULL;
        }

        // malloc space for data_buf

        content_length = atoi( (char*)(r->headers_in.content_length->value.data) );

        data_buf = ( uint8_t* )ngx_palloc( r->pool , content_length + 1 );

        size_t buf_length = 0;

        while ( bufs )
        {
            buf = bufs->buf;

            bufs = bufs->next;

            buf_length = buf->last - buf->pos ;

            if( *body_len + buf_length > content_length )
            {

                memcpy( data_buf + *body_len, buf->pos, content_length - *body_len );
                *body_len = content_length ;

                break;
            }

            memcpy( data_buf + *body_len, buf->pos, buf->last - buf->pos );
            *body_len += buf->last - buf->pos;
        }

        if ( *body_len )
        {
            data_buf[*body_len] = '\0';
        }

        return data_buf;
    }/*}}}*/


    /**
    * Process the client request.
    * The client post data has stored in <code>r</code>
    */
    static void p2s_urlquery_process_handler(ngx_http_request_t *r)
    {/*{{{*/
        printf( "%s\n", __PRETTY_FUNCTION__ );
        ngx_int_t rc = NGX_OK;
        ngx_buf_t *b = NULL;
        ngx_chain_t out;


        ngx_http_p2s_conf_t *conf = NULL;
        conf = (ngx_http_p2s_conf_t *)ngx_http_get_module_loc_conf(r, ngx_http_p2s_module);
        if (conf == NULL)
        {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return ;
        }

        struct timeval tv;
        gettimeofday(&tv, NULL);
        size_t bodylen = 0;
        uint8_t* contents = get_raw_http_body( r, &bodylen );
        printf( "time=%f http body data len=%d:\n%s\n", (tv.tv_sec + tv.tv_usec/1000000.0f ), (int)bodylen, (char*)contents );
        printf("----------------------http body data end-------------------\n");

        /* Prepare for output, 128 is preserved for robust */
        b = ngx_create_temp_buf( r->pool, 128 + conf->buflen );
        if (b == NULL)
        {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return ;
        }
        out.buf = b;
        out.next = NULL;

        b->last = ngx_sprintf(b->pos, "%s", conf->ini_buf);

        r->headers_out.status = NGX_HTTP_OK;
        r->headers_out.content_length_n = b->last - b->pos;
        r->headers_out.content_type.len = sizeof("text/plain") - 1;
        r->headers_out.content_type.data = (u_char *) "text/plain";

        b->last_buf = 1;/* there will be no more buffers in the request */

        rc = ngx_http_send_header(r);

        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
        {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return ;
        }

        ngx_http_output_filter(r, &out);
        ngx_http_finalize_request(r, 0);
    }/*}}}*/

    /**
    * Reading data handler
    * After read all the data from client we set a process handler
    */
    static ngx_int_t
    ngx_http_p2s_urlquery_handler(ngx_http_request_t *r)
    {/*{{{*/
        printf( "%s\n", __PRETTY_FUNCTION__ );
        ngx_int_t rc = NGX_DONE;
        rc = ngx_http_read_client_request_body( r, p2s_urlquery_process_handler );
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }

        return NGX_DONE;
    }/*}}}*/


    /**
    * set the request reading data handler
    */
    static char *
    ngx_http_p2s_urlquery_set( ngx_conf_t *cf, ngx_command_t *cmd, void *conf )
    {/*{{{*/
        printf( "%s\n", __PRETTY_FUNCTION__ );
        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);
        clcf->handler = ngx_http_p2s_urlquery_handler;

        return NGX_CONF_OK;
    }/*}}}*/


    static void *
    ngx_http_p2s_create_conf(ngx_conf_t *cf)
    {/*{{{*/
        printf( "%s\n", __PRETTY_FUNCTION__ );
        ngx_http_p2s_conf_t *conf;

        conf = (ngx_http_p2s_conf_t *)ngx_pcalloc(cf->pool, sizeof(ngx_http_p2s_conf_t));
        if (conf == NULL) {
            return NGX_CONF_ERROR;
        }

        conf->consume = 0;

        /* we open the nginx config file and send it back to client*/
        FILE *fp = fopen( "../conf/nginx.conf", "r");
        if( fp == NULL )
        {
            return NGX_CONF_ERROR;
        }

        fseek(fp, 0, SEEK_END);
        long len = ftell( fp );
        if ( len < 0 )
        {
            return NGX_CONF_ERROR;
        }
        conf->buflen = (size_t)(len + 1);
        conf->ini_buf = (char *)ngx_palloc( cf->pool, len + 1 );
        fseek(fp, 0, SEEK_SET);
        fread(conf->ini_buf, 1, len, fp);
        conf->ini_buf[len] = 0;
        fclose(fp);

        return conf;
    }/*}}}*/

     


    5. 編譯運行 現在可以編譯nginx和我們剛剛寫好的模塊了。

     

    [zieckey@freebsd7.2 ~/nginx]$ cd
    [zieckey@freebsd7.2 ~]$ cd nginx/
    [zieckey@freebsd7.2 ~/nginx]$ mkdir bininstalled
    [zieckey@freebsd7.2 ~/nginx]$ cd nginx-1.0.0
    [zieckey@freebsd7.2 nginx-1.0.0]$ ./configure --add-module=/home/zieckey/nginx/module_dev_urlquery --prefix=/home/zieckey/nginx/bininstalled
    ./configure: error: the HTTP rewrite module requires the PCRE library.
    You can either disable the module by using --without-http_rewrite_module
    option, or install the PCRE library into the system, or build the PCRE library
    statically from the source with nginx by using --with-pcre=<path> option.


    第一次錯誤,我們現在這個freebsd系統上沒有pcre庫,太悲催了,不過還好,這里給出了提示,說可以通過 --without-http_rewrite_module 來禁用使用pcre庫的 HTTP rewrite 模塊,我們試一試: 

    [zieckey@freebsd7.2 nginx-1.0.0]$ ./configure --add-module=/home/zieckey/nginx/module_dev_urlquery --prefix=/home/zieckey/nginx/bininstalled --without-http_rewrite_module
    adding module in /home/zieckey/nginx/module_dev_urlquery
    + ngx_http_p2s_module was configured


    重點看到上面幾行信息,說明我們自己寫的模塊module_dev_urlquery已經被nginx接納,生成makefile成功。 

    [zieckey@freebsd7.2 nginx-1.0.0]$ make
    [zieckey@freebsd7.2 nginx-1.0.0]$ make install
    [zieckey@freebsd7.2 nginx-1.0.0]$ cd /home/zieckey/nginx/bininstalled/
    [zieckey@freebsd7.2 bininstalled]$ ls
    conf html logs sbin
    [zieckey@freebsd7.2 bininstalled]$ cd conf
    [zieckey@freebsd7.2 conf]$ vim ngi
    nginx.conf nginx.conf.default 
    [zieckey@freebsd7.2 conf]$ vim nginx.conf


    默認的配置文件是下面(將‘#’開頭的注釋行刪除之后) 

    worker_processes 1;
    events {
        worker_connections 1024;

    http {
        include mime.types;
        default_type application/octet-stream;
        sendfile on;
        keepalive_timeout 65;
        server {
            listen 80;
            server_name localhost;
            location / {
                root html;
                index index.html index.htm;
            } 
       }
    }


    我們在最上面添加如下選項:daemon off;  可以讓程序不以daemon的方式運行,這樣我們可以看到一些調試的打印信息。
    另外,在 server 里面添加一個 URI :
    location /urlquery {
        p2s_urlquery;
    }
    最后,我們修改下http監聽的端口號,從默認的80改為8088,因為有些時候,我們并沒有權限在80端口上監聽連接。
    修改之后的配置文件全文如下: 

    daemon off;
    worker_processes 1;
    events {
        worker_connections 1024;
    }
    http {
        include mime.types;
        default_type application/octet-stream;
        sendfile on;
        keepalive_timeout 65;
        server {
            listen 8088;
            server_name localhost;
            location / {
                root html;
                index index.html index.htm;
            }
            location /urlquery {
                p2s_urlquery;
            }
        }
    }


    好了,到此為止,我們的所有工作就都準備好了,可以啟動nginx 

    [zieckey@freebsd7.2 conf]$ cd /home/zieckey/nginx/bininstalled/sbin/
    [zieckey@freebsd7.2 sbin]$ ./nginx
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_urlquery_set


    新開一個終端,用curl來發起一個http post請求,post數據由-d參數指定: 

    [zieckey@freebsd7.2 ~]$ curl -d "user=zieckey&verifykey=123456" http://localhost:8088/urlquery
    daemon off;
    worker_processes 1;
    events {
        worker_connections 1024;
    }

    http {
        include mime.types;
        default_type application/octet-stream;
        sendfile on;
        keepalive_timeout 65;
        server {
            listen 8088;
            server_name localhost;
            location / {
                root html;
                index index.html index.htm;
                    }
                    location /urlquery {
                            p2s_urlquery;
                    }
            }
    }
    [zieckey@freebsd7.2 ~]$


    然后可以看看nginx服務器的一些輸出信息: 

    [zieckey@freebsd7.2 sbin]$ ./nginx
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_create_conf
    ngx_http_p2s_urlquery_set
    ngx_http_p2s_urlquery_handler
    p2s_urlquery_process_handler
    get_raw_http_body
    time=1303552000.000000 http body data len=29:
    user=zieckey&verifykey=123456
    ----------------------http body data end-------------------

     

    6. 其他說明 6.1 配置文件和ngx_command_t的對應關系:



    static ngx_command_t ngx_http_p2s_commands[] =
    {/*{{{*/
        { ngx_string("p2s_urlquery"), // 命令名,請對照 nginx.conf 看這個
          NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
          ngx_http_p2s_urlquery_set,
          0,
          0,
          NULL },

          ngx_null_command
    };/*}}}*/

    nginx.conf 中的 location 字段的配置是這樣的:
            location /urlquery {
                p2s_urlquer;
            }

     

    這里的 /urlquery 是 URL 請求里的 URI 部分, 例如 http://localhost/urlquery 這條URL的請求會被這條配置項處理, 這條配置項,會去找命令名為“p2s_urlquery”的 ngx_command 去處理。

     

    6.2 模塊名 代碼中的模塊變量名必須與config配置文件的名字一致 config 配置文件的內容:ngx_addon_name=ngx_http_p2s_module 代碼中的內容:

     

    ngx_module_t ngx_http_p2s_module = 
    {/*{{{*/
        NGX_MODULE_V1,
        &ngx_http_p2s_module_ctx, /* module context */
        ngx_http_p2s_commands, /* module directives */
        NGX_HTTP_MODULE, /* module type */
        NULL, /* init master */
        NULL, /* init module */
        NULL, /* init process */
        NULL, /* init thread */
        NULL, /* exit thread */
        NULL, /* exit process */
        NULL, /* exit master */
        NGX_MODULE_V1_PADDING
    };/*}}}*/

     

    模塊的config配置文件會幫助 configure 生成一個ngx_modules.c文件,該文件里會引用 ngx_module_t  ngx_http_p2s_module 這個外部定義的變量,所以必須保持一致。在執行 ./configure 完后,在nginx-1.0.0源碼的根目錄會多出來一個文件夾objs,在這里可以找到ngx_modules.c文件。   6.3 代碼調用關系 nginx程序啟動的時候,會去讀配置文件 conf/nginx.conf ,每讀到一個配置項,就會調用 module 注冊的回調函數,所以這里可以看到nginx啟動的時候調用 ngx_http_p2s_create_conf 函數好幾次。 這個函數是由 

     

    static ngx_http_module_t ngx_http_p2s_module_ctx = 
    {/*{{{*/
        NULL, /* preconfiguration */ 
        NULL, /* postconfiguration */

        NULL, /* create main configuration */
        NULL, /* init main configuration */

        NULL, /* create server configuration */
        NULL, /* merge server configuration */

        ngx_http_p2s_create_conf, /* create location configration */
        NULL /* merge location configration */
    };/*}}}*/

    指定的。   然后,在一個客戶端發起http請求的時候,例如我們這里的 “curl http://localhost:8088/urlquery” 命令實際上是發起一個 http 請求,URI 是 urlquery,所以,nginx 根據配置文件的配置項,找到  urlquery 對應的處理模塊名 p2s_urlquery, 然后調用 p2s_urlquery 這個名字關聯的 ngx_http_p2s_urlquery_set 回調函數。 ngx_http_p2s_urlquery_set 回調函數又會設置一個 ngx_http_p2s_urlquery_handler 回調函數,ngx_http_p2s_urlquery_handler 回調函數會在 nginx 讀取客戶端發過來的http數據的時候被調用。 ngx_http_p2s_urlquery_handler 又會設置一個 p2s_urlquery_process_handler 回調函數, p2s_urlquery_process_handler 會在 nginx 讀取完客戶端發過來的http數據之后的時候被調用, 不難發現,p2s_urlquery_process_handler函數是我們真正的處理邏輯,因為這個時候已經收完了客戶端的http數據。   p2s_urlquery_process_handler 函數先調用 get_raw_http_body 這個我們自己封裝的函數將客戶端發送過來的 http data 讀取到一個緩沖區中,然后處理(這里我們只是簡單的printf)。然后,我們準備好數據,發送會客戶端。  



    到此,我們完成了一個很簡單的模塊的開發和調試工作,也不是那么困難。
    同時對一些函數調用,配置文件關系等等比較含糊的地方做了詳細說明。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全