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的自定義模塊了,之前有寫過根據(jù)POST數(shù)據(jù)轉(zhuǎn)發(fā)請(qǐng)求的模塊(參見nginx自定義模塊編寫-根據(jù)post參數(shù)路由到不同服務(wù)器),不過上次寫的是處理模塊,而這次寫的是過濾模塊,還是有一些區(qū)別的。
在正式開始前,先說一下寫nginx自定義模塊要注意的幾個(gè)點(diǎn):
OK,廢話不多說,開始正式說我這次寫的統(tǒng)計(jì)模塊吧
需求背景呢,就是現(xiàn)在已經(jīng)在nginx后面掛了很多服務(wù)器,需要用nginx來統(tǒng)計(jì)成功率,響應(yīng)時(shí)間等等參數(shù),在網(wǎng)上翻了半天,大部分居然是用access_log,然后用程序掃描$request_time來實(shí)現(xiàn)的,這對(duì)一個(gè)每秒幾千次訪問的服務(wù)器是不可忍受的,所以最終沒辦法,那就自己寫一個(gè)唄~
重新看了nginx自定義模塊的開發(fā)文檔,整個(gè)調(diào)用過程如下:
但是實(shí)在是沒找到請(qǐng)求整個(gè)結(jié)束時(shí)的回調(diào)函數(shù),最接近的也就是用filter模塊了(即過濾模塊),當(dāng)然這樣統(tǒng)計(jì)出來的請(qǐng)求時(shí)間,可能會(huì)比實(shí)際時(shí)間短一些。
OK,定了要寫那種模塊后,我們來考慮一下具體的實(shí)現(xiàn)
UDP上報(bào)client這里,因?yàn)槭怯玫墓镜膸?kù),而且又很簡(jiǎn)單,這里就不細(xì)說了,大家看代碼也會(huì)看到,我在里面用的是static變量來保證不析構(gòu):
| 1 | static COpenApiMonitorClient client; |
?
參數(shù)配置這里,因?yàn)樯蠄?bào)庫(kù)的要求,需要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 }; |
?
對(duì)于是選擇ngx_http_output_header_filter_pt還是ngx_http_output_body_filter_pt這里,我最終選擇了ngx_http_output_header_filter_pt,雖然說計(jì)算請(qǐng)求時(shí)間上會(huì)有差別,但是因?yàn)閚gx_http_output_body_filter_pt會(huì)進(jìn)入多次,寫起來更復(fù)雜些,所以就走簡(jiǎn)單的了~
代碼如下:
| 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); } |
?
對(duì)于上報(bào)字段這里,主要是ngx_http_request_t每個(gè)字段的意義搞了我很久時(shí)間,這里也不多解釋了,直接貼代碼,大家應(yīng)該能直接看懂了
| 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"]; //轉(zhuǎn)發(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,整個(gè)代碼基本就是這樣了
慣例,下面又發(fā)現(xiàn)的新問題,糾結(jié)了我好久,現(xiàn)在還是沒解決
最后,代碼已經(jīng)上傳的googlecode
https://vimercode.googlecode.com/svn/trunk/nginx_stat_report
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4353113.html
總結(jié)
以上是生活随笔為你收集整理的nginx自定义模块编写-实时统计模块--转载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring JTA multiple
- 下一篇: 使用Atomikos Transacti