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数据转发请求的模块(参见nginx自定义模块编写-根据post参数路由到不同服务器),不过上次写的是处理模块,而这次写的是过滤模块,还是有一些区别的。

在正式开始前,先说一下写nginx自定义模块要注意的几个点:

  1. 上次的文章提到,在函数里用r-connection.log打印log会core,今天发现是ngx头文件和lua头文件引用顺序的问题,把ngx的头文件放在最前面即可解决
  2. nginx的一个字符串类型 ngx_str_t 有两个参数, len 和 data,这两个参数一定要一起使用,因为data的\0结尾,不一定是len的长度,这一点千万要注意
  3. 需要和cpp文件联合编译是,在ngx的编译参数里面加上–with-ld-opt=”-lstdc++”

OK,废话不多说,开始正式说我这次写的统计模块吧

需求背景呢,就是现在已经在nginx后面挂了很多服务器,需要用nginx来统计成功率,响应时间等等参数,在网上翻了半天,大部分居然是用access_log,然后用程序扫描$request_time来实现的,这对一个每秒几千次访问的服务器是不可忍受的,所以最终没办法,那就自己写一个呗~

重新看了nginx自定义模块的开发文档,整个调用过程如下:

但是实在是没找到请求整个结束时的回调函数,最接近的也就是用filter模块了(即过滤模块),当然这样统计出来的请求时间,可能会比实际时间短一些。

OK,定了要写那种模块后,我们来考虑一下具体的实现

  1. 为了性能最大话,上报使用UDP协议,并且不收取回包,socket创建之后不释放
  2. 既然涉及到网络上报,就需要设置上报ip,port,来源等参数
  3. 过滤模块有两个函数,分别是ngx_http_output_header_filter_pt和ngx_http_output_body_filter_pt
  4. 上报的字段应该包括,method,uri,request_time,http状态码,目标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"];

//转发的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,整个代码基本就是这样了

惯例,下面又发现的新问题,纠结了我好久,现在还是没解决

  1. ngx_log_error在打印%u的时候,会导致进程退出,至今不知道为啥,解决方式就是打印成%d
  2. ngx_log_error在打印%f的时候,会打印成int,而且即使指定%.2f之类的,打印的结果也不对,不知为啥,解决方式就是把值*1000变成int

最后,代码已经上传的googlecode
https://vimercode.googlecode.com/svn/trunk/nginx_stat_report

时间: 2024-10-12 23:12:59

nginx自定义模块编写-实时统计模块--转载的相关文章

nginx自定义模块编写-根据post参数路由到不同服务器

nginx可以轻松实现根据不同的url 或者 get参数来转发到不同的服务器,然而当我们需要根据http包体来进行请求路由时,nginx默认的配置规则就捉襟见肘了,但是没关系,nginx提供了强大的自定义模块功能,我们只要进行需要的扩展就行了. 我们来理一下思路,我们的需求是: nginx根据http包体的参数,来选择合适的路由 在这之前,我们先来考虑另一个问题: 在nginx默认配置的支持下,能否实现服务器间的跳转呢?即类似于状态机,从一个服务器执行OK后,跳转到另一台服务器,按照规则依次传递

Nginx自定义模块编写:根据post参数路由到不同服务器

Nginx自定义模块编写:根据post参数路由到不同服务器 2014-05-05 15:27 blogread IT技术博客 字号:T | T Nginx可以轻松实现根据不同的url 或者 get参数来转发到不同的服务器,然而当我们需要根据http包体来进行请求路由时,Nginx默认的配置规则就捉襟见肘了,但是没关系,Nginx提供了强大的自定义模块功能,我们只要进行需要的扩展就行了. 我们来理一下思路,我们的需求是: Nginx根据http包体的参数,来选择合适的路由 在这之前,我们先来考虑另

面试题笔记:实现Nginx Upload 模块 功能上传文件。

linux服务器开发测评题目———————————————————————————— 搭建一个nginx服务器,能完成文件上传功能.主要构成有: <1> 用于测试服务器上传功能用的前端html页面 <2> nginx web服务器,包括了文件上传功能模块,注意配置好配置文件 <3> 对于上传成功的文件,给前端返回upload successfully信息 动手搭建完成后,针对上面的几点要求截几张图,同时把前端html页面,nginx配置文件,和假如需要使用的业务逻辑代码

Nginx的模块开发指南

原文:http://www.evanmiller.org/nginx-modules-guide.html 译文:http://blog.csdn.net/tab_tab_tab/article/details/51407418 解蝙蝠侠的漫画人物有助于充分认识Nginx.Web服务器. 首先,蝙蝠侠快. Nginx也快. 然后,蝙蝠侠同犯罪做斗争,Nginx和浪费CPU周期和内存泄漏做斗争. 最后,蝙蝠侠在压力下进行 工作.Nginx就其本身而言,擅长重的服务器负载下运行. 但是,蝙蝠侠将在没

nginx -- handler模块(100%)

handler模块简介 相信大家在看了前一章的模块概述以后,都对nginx的模块有了一个基本的认识.基本上作为第三方开发者最可能开发的就是三种类型的模块,即handler,filter和load-balancer.Handler模块就是接受来自客户端的请求并产生输出的模块.有些地方说upstream模块实际上也是一种handler模块,只不过它产生的内容来自于从后端服务器获取的,而非在本机产生的. 在上一章提到,配置文件中使用location指令可以配置content handler模块,当Ng

NancyFx 2.0的开源框架的使用-CustomModule(自定义模块)

NancyFx框架的自定义模块 新建一个空的Web项目 然后通过NuGet库安装下面的包 Nancy Nancy.Hosting.Aspnet 然后添加Models,Module,Views三个文件夹,并在Models文件里面添加NancyRouteAttribute类 //路由的方法 public string Method { get; set; } //路由的路径 public string Path { get; set; } public NancyRouteAttribute(str

Nginx学习笔记六Nginx的模块开发

1.Nginx配置文件主要组成:main(全局配置)这部分的指令将影响其他所有部分.server(虚拟主机配置)这部分指令主要用于指定虚拟主机域名,IP和端口.upstream(主要为反向代理,负载均衡相关配置)这部分指令用于设置反向代理及后端服务 器的负载均衡.location(目录匹配配置)这部分指令用于匹配网页位置(例如,根目录"/","/images",等 等). location部分会继承server部分的指令,而server部分会继承main部分的指令.

mac下Nginx+lua模块编译安装

Nginx的nb之处就不说了,lua也是一个小巧的脚本语言,由标准C编写而成,几乎可以运行在所有的平台上,也非常强大,其他特性请自行度娘.nginx_lua_module是由淘宝的工程师清无(王晓哲)和春来(章亦春)所开发的nginx第三方模块,它能将lua语言嵌入到nginx配置中,从而使用lua就极大增强了nginx的能力 http://wiki.nginx.org/HttpLuaModule 下面说说mac下Nginx如何编译集成nginx_lua_module模块 1. 下载nginx需

js利用闭包封装自定义模块的几种方法

1.自定义模块: 具有特定功能的js文件 将所有的数据和功能都封装在一个函数的内部 只向外暴露一个包含有n个方法的对象或者函数 模块使用者只需要通过模块暴露的对象调用方法来实现相对应的功能 1.利用函数方法自调用 /** * Created by lonecloud on 2017/9/10. */ (function (window) { var DEBUG="debug" /** * 打印日志 * @param args */ function log(args) { consol