日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

nginx自定义模块编写-实时统计模块--转载

發(fā)布時間:2025/4/5 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 nginx自定义模块编写-实时统计模块--转载 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文:http://www.vimer.cn/2012/05/nginx%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A8%A1%E5%9D%97%E7%BC%96%E5%86%99-%E5%AE%9E%E6%97%B6%E7%BB%9F%E8%AE%A1%E6%A8%A1%E5%9D%97.html

不是第一次寫nginx的自定義模塊了,之前有寫過根據POST數據轉發(fā)請求的模塊(參見nginx自定義模塊編寫-根據post參數路由到不同服務器),不過上次寫的是處理模塊,而這次寫的是過濾模塊,還是有一些區(qū)別的。

在正式開始前,先說一下寫nginx自定義模塊要注意的幾個點:

  • 上次的文章提到,在函數里用r-connection.log打印log會core,今天發(fā)現是ngx頭文件和lua頭文件引用順序的問題,把ngx的頭文件放在最前面即可解決
  • nginx的一個字符串類型 ngx_str_t 有兩個參數, len 和 data,這兩個參數一定要一起使用,因為data的\0結尾,不一定是len的長度,這一點千萬要注意
  • 需要和cpp文件聯合編譯是,在ngx的編譯參數里面加上–with-ld-opt=”-lstdc++”
  • OK,廢話不多說,開始正式說我這次寫的統計模塊吧

    需求背景呢,就是現在已經在nginx后面掛了很多服務器,需要用nginx來統計成功率,響應時間等等參數,在網上翻了半天,大部分居然是用access_log,然后用程序掃描$request_time來實現的,這對一個每秒幾千次訪問的服務器是不可忍受的,所以最終沒辦法,那就自己寫一個唄~

    重新看了nginx自定義模塊的開發(fā)文檔,整個調用過程如下:

    但是實在是沒找到請求整個結束時的回調函數,最接近的也就是用filter模塊了(即過濾模塊),當然這樣統計出來的請求時間,可能會比實際時間短一些。

    OK,定了要寫那種模塊后,我們來考慮一下具體的實現

  • 為了性能最大話,上報使用UDP協議,并且不收取回包,socket創(chuàng)建之后不釋放
  • 既然涉及到網絡上報,就需要設置上報ip,port,來源等參數
  • 過濾模塊有兩個函數,分別是ngx_http_output_header_filter_pt和ngx_http_output_body_filter_pt
  • 上報的字段應該包括,method,uri,request_time,http狀態(tài)碼,目標IP,等等
  • UDP上報client這里,因為是用的公司的庫,而且又很簡單,這里就不細說了,大家看代碼也會看到,我在里面用的是static變量來保證不析構:

    1 static COpenApiMonitorClient client;

    ?

    參數配置這里,因為上報庫的要求,需要ip,port,來源,所以配置代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 typedef struct { ????ngx_str_t??host; ????ngx_int_t??port; ????ngx_str_t??collect_point; } ngx_http_stat_report_conf_t; static ngx_command_t??ngx_http_stat_report_filter_commands[] = { ????{ ngx_string("stat_report_host"), ????????NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ????????ngx_conf_set_str_slot, ????????NGX_HTTP_LOC_CONF_OFFSET, ????????offsetof(ngx_http_stat_report_conf_t, host), ????????NULL }, ????{ ngx_string("stat_report_port"), ????????NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ????????ngx_conf_set_num_slot, ????????NGX_HTTP_LOC_CONF_OFFSET, ????????offsetof(ngx_http_stat_report_conf_t, port), ????????NULL }, ????{ ngx_string("stat_report_collect_point"), ????????NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ????????ngx_conf_set_str_slot, ????????NGX_HTTP_LOC_CONF_OFFSET, ????????offsetof(ngx_http_stat_report_conf_t, collect_point), ????????NULL }, ????ngx_null_command };

    ?

    對于是選擇ngx_http_output_header_filter_pt還是ngx_http_output_body_filter_pt這里,我最終選擇了ngx_http_output_header_filter_pt,雖然說計算請求時間上會有差別,但是因為ngx_http_output_body_filter_pt會進入多次,寫起來更復雜些,所以就走簡單的了~

    代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 static ngx_int_t ngx_http_stat_report_header_filter(ngx_http_request_t *r) { ????ngx_http_stat_report_conf_t??*conf; ????conf = (ngx_http_stat_report_conf_t??*)ngx_http_get_module_loc_conf(r, ngx_http_stat_report_filter_module); ????SendStatReport(r, conf); ????return ngx_http_next_header_filter(r); }

    ?

    對于上報字段這里,主要是ngx_http_request_t每個字段的意義搞了我很久時間,這里也不多解釋了,直接貼代碼,大家應該能直接看懂了

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 ngx_http_request_body_t* rb = r->request_body; char* body = NULL; int body_size = 0; if (rb && rb->buf) {?? ????body = (char*)rb->buf->pos; ????body_size = rb->buf->last - rb->buf->pos; }?? string str_uri = r->uri.data ? string((char*)r->uri.data,r->uri.len) : ""; string protocol = r->http_protocol.data ? string((char*)r->http_protocol.data, r->http_protocol.len) : ""; string str_data; map<string,string> params; if (r->method == 2) // get {?? ????pkg.method = "GET"; ????str_data = r->args.data ? string((char*)r->args.data, r->args.len) : ""; }?? else if (r->method == 8) {?? ????pkg.method = "POST"; ????str_data = (body && body_size>0) ? string(body, body_size) : ""; }?? else {?? ????return -1; }?? //ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "args:%s uri:%s protocol:%s end", str_data.c_str(), str_uri.c_str(), protocol.c_str()); Trans2MapParams(str_data, params); ngx_msec_int_t ms = get_pass_time_ms(r); double time_sec = ((double)ms)/1000; pkg.appid = strtoul(params["appid"].c_str(), NULL, 10); pkg.rc = 0; if (r->headers_out.status >= 400) // 就代表有問題 {?? ????pkg.rc = r->headers_out.status; }?? pkg.timestamp = time(NULL); pkg.time = time_sec; pkg.pf = params["pf"]; //轉發(fā)的IP和port pkg.svr_name = ""; if (r->upstream && r->upstream->peer.name && r->upstream->peer.name->data) {?? ????pkg.svr_name = string((char*)r->upstream->peer.name->data, r->upstream->peer.name->len); } pkg.interface = str_uri; pkg.protocol = ParseProtocol(protocol); pkg.collect_point = conf->collect_point.data ? string((char*)conf->collect_point.data, conf->collect_point.len) : "";

    ?

    OK,整個代碼基本就是這樣了

    慣例,下面又發(fā)現的新問題,糾結了我好久,現在還是沒解決

  • ngx_log_error在打印%u的時候,會導致進程退出,至今不知道為啥,解決方式就是打印成%d
  • ngx_log_error在打印%f的時候,會打印成int,而且即使指定%.2f之類的,打印的結果也不對,不知為啥,解決方式就是把值*1000變成int
  • 最后,代碼已經上傳的googlecode
    https://vimercode.googlecode.com/svn/trunk/nginx_stat_report

    轉載于:https://www.cnblogs.com/davidwang456/p/4353113.html

    總結

    以上是生活随笔為你收集整理的nginx自定义模块编写-实时统计模块--转载的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。