为 nginx 编写实时统计的自定义模块

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

在函数里用r-connection.log打印log会core,今发现是ngx头文件和lua头文件引用顺序的问题,把ngx的头文件放在最前面即可解决

nginx的一个字符串类型 ngx_str_t 有两个参数, len 和 data,这两个参数一定要一起使用,因为data的结尾,不一定是len的长度,这一点千万要注意

需要和cpp文件联合编译是,在ngx的编译参数里面加上–with-ld-opt=”-lstdc++”

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

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

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

 


 

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

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

为了性能最大话,上报使用UDP协议,并且不收取回包,socket创建之后不释放

既然涉及到网络上报,就需要设置上报ip,port,来源等参数

过滤模块有两个函数,分别是ngx_http_output_header_filter_pt和ngx_http_output_body_filter_pt

上报的字段应该包括,method,uri,request_time,http状态码,目标IP,等等

UDP上报client这里,因为是用的公司的库,而且又很简单,这里就不细说了,大家看代码也会看到,我在里面用的是static变量来保证不析构:

static COpenApiMonitorClient client;

 

参数配置这里,因为上报库的要求,需要ip,port,来源,所以配置代码如下:

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会进入多次,写起来更复杂些,所以就走简单的了~

代码如下:

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每个字段的意义搞了我很久时间,这里也不多解释了,直接贴代码,大家应该能直接看懂了

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

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

ngx_log_error在打印%u的时候,会导致进程退出,至今不知道为啥,解决方式就是打印成%d

ngx_log_error在打印%f的时候,会打印成int,而且即使指定%.2f之类的,打印的结果也不对,不知为啥,解决方式就是把值*1000变成int

最后,代码已经上传的googlecode

https://vimercode.googlecode.com/svn/trunk/nginx_stat_report

时间: 2024-11-08 21:20:25

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

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

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

Kafka项目实战-用户日志上报实时统计之应用概述

1.概述 本课程的视频教程地址:<Kafka实战项目之应用概述> 本课程是通过一个用户实时上报日志来展开的,通过介绍 Kafka 的业务和应用场景,并带着大家搭建本 Kafka 项目的实战开发环境.下面我们来看看本课程有哪些课时,如下图所示: 接下来,我们开始第一课时的学习:<Kafka 回顾>. 2.内容 2.1 Kafka 回顾 本课时简述 Kafka 平台部署的注意事项,以及 Kafka 在企业中的业务场景和应用场景.让大家了解 Kafka 在企业中的使用. 本课时主要包含以

Kafka项目实战-用户日志上报实时统计之分析与设计

1.概述 本课程的视频教程地址:<Kafka实战项目之分析与设计>  本课程我通过一个用户实时上报日志案例作为基础,带着大家去分析Kafka这样一个项目的各个环节,从而对项目的整体设计做比较合理的规划,最终让大家能够通过本课程去掌握类似Kafka项目的分析与设计.下面,我给大家介绍本课程包含的课时内容,如下图所示: 接下来,我们开始第一课时的学习:<项目整体概述>. 2.内容 2.1 项目整体设计 项目整体概述主要讲解一个项目产生的背景,以及该项目背后的目的,从而让大家更好的去把握

Kafka项目实战-用户日志上报实时统计之编码实践

1.概述 本课程的视频教程地址:<Kafka实战项目之编码实践>  该课程我以用户实时上报日志案例为基础,带着大家去完成各个KPI的编码工作,实现生产模块.消费模块, 数据持久化,以及应用调度等工作, 通过对这一系列流程的演示,让大家能够去掌握Kafka项目的相关编码以及调度流程.下面,我们首先来预览本课程所包含的课时,他们分别有: 接下来,我们开始第一课时的学习:<数据生产实现> 2.内容 2.1 数据生产实现 本课时主要给大家演示Kafka数据生产的代码实现,在前面搭建好的集群

基于Storm的Nginx log实时监控系统

[编者按]Hadoop的缺点也和它的优点同样鲜明--延迟大,响应缓慢,运维复杂.被人广受诟病,但是 有需求就有创造,在Hadoop基本奠定了大数据霸主地位的时候,很多的开源项目都是以弥补Hadoop的实时性为目标而被创造出来,Storm正是在这个时候横空出世,Storm是一个免费开源.分布式.高容错的实时计算系统.Storm令持续不断的流计算变得容易,弥补了Hadoop批处理所不能满足的实时要求. 以下为原文: 背景UAE(UC App Engine)是一个UC内部的PaaS平台,总体架构有点类

如何在React Native中写一个自定义模块

前言 在 React Native 项目中可以看到 node_modules 文件夹,这是存放 node 模块的地方,Node.js 的包管理器 npm 是全球最大的开源库生态系统.提到npm,一般指两层含义:一是 Node.js 开放式模块登记和管理系统,另一种是 Node.js 默认的模块管理器,是一个命令行软件,用来安装和管理 node 模块.本文旨在探讨如何在 React Native 中写一个自定义的 npm 模块(类似于插件),并上传到 npm 上供他人使用. npm 使用介绍 np

如何在 React Native 中写一个自定义模块

前言 在 React Native 项目中可以看到 node_modules 文件夹,这是存放 node 模块的地方,Node.js 的包管理器 npm 是全球最大的开源库生态系统.提到npm,一般指两层含义:一是 Node.js 开放式模块登记和管理系统,另一种是 Node.js 默认的模块管理器,是一个命令行软件,用来安装和管理 node 模块.本文旨在探讨如何在 React Native 中写一个自定义的 npm 模块(类似于插件),并上传到 npm 上供他人使用. npm 使用介绍 np

天猫“双11”成交额实时统计技术详解

阿里巴巴资深技术专家莫问在2017年12月20日云栖大会北京峰会上做了题为"Apache Flink技术进阶"的主题演讲.Apache Flink作为流式计算引擎,支持了"双十一对的"实时计算,已经被国内外的公司使用.其中关于"Flink的技术特点"."阿里巴巴的Flink版本--Blink"以及"Blink在实际场景中的应用" 等经验首次对外详细剖析,很有价值.以下为视频内容整理: Apache Flin

如何用ASP编写网站统计系统

在目前的网站统计系统决大部分都是CGI的,但编写起来特别复杂,而ASP学起来简单,更有和数据库结合的优点,所以结合自己曾经做过的网站统计系统,和大家探讨一下ASP编写网站统计系统. 大家都看过网易的网站统计系统,它可以统计总访问量,每日平均访问量,当日访问量,最高访问量,最高访问日期,日流量分析,月流量分析,周流量分析,浏览器分析,等等. 其实要做一个ASP的访问统计系统关键是系统表结构的设计.以及如何来采集用户的CGI变量,如何来显示用户的信息.也就是说系统的关键是两个ASP程序,统计程序和显