热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

nginx模块之计算HTTP首包响应时间

这里讲计算nginx的首包响应时间,那首先首包是什么意思呢?它表示从nginx收到GET请求到发送第一个数据包的这段时间。这段时间包括了nginx协议分析到准备数据的这段时间,在CDN系统上它可作为一

这里讲计算nginx的首包响应时间,那首先首包是什么意思呢?它表示从nginx收到GET请求到发送第一个数据包的这段时间。这段时间包括了nginx协议分析到准备数据的这段时间,在CDN系统上它可作为一项质量监控指标。例如有可能服务器磁盘io过高导致准备数据的时间过长,也有可能本地缓存MISS需要回源等。那么,我们怎么设计一个HTTP模块来计算这个首包响应时间呢?NGINX HTTP框架依据常见的处理流程划分了11个HTTP请求处理阶段,我们自己设计模块的时候通常是在相应的阶段添加自己的请求处理方法,这里也不例外。当HTTP框架在建立的TCP连接上接收到客户发送的完整HTTP请求头部时,首先会执行NGX_HTTP_POST_READ_PHASE阶段的checker方法。任意HTTP模块需要在NGX_HTTP_POST_READ_PHASE阶段处理HTTP请求时,必然首先在ngx_http_core_main_conf_t结构体中的phases[NGX_HTTP_POST_READ_PHASE]动态数组中添加自己实现的ngx_http_handler_pt方法。例如,nginx realip模块的ngx_http_realip_handlers方法就是添加在这个阶段,在nginx作为反向代理时,它会获取用户的真实ip,并将该ip添加到HTTP头部,这样proxy后端的服务器通过解析HTTP头部字段就能拿到用户的真实ip了。nginx realip它在postconfiguration方法中是这样将自己定义的ngx_http_handle_pt处理方法添加到HTTP框架中去的,如下所示:

//该方法就是postfiguration接口的实现
static ngx_int_t
ngx_http_realip_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;

cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}

*h = ngx_http_realip_handler;

h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}

*h = ngx_http_realip_handler;
return NGX_OK;
}

我们要计算nginx首包响应的时间,也就是要计算收到完整请求到发送首包的这段时间,因此可以考虑在NGX_HTTP_POST_READ_PHASE阶段添加自定义方法,先初始化一个时间,这就是开始时间。那么,我们在哪里去标记发送首包的这个时间呢?这个需要先了解一下nginx发送响应的流程。nginx明确地将HTTP响应分为两个部分:HTTP头部和HTTP包体,它调用ngx_http_send_header发送HTTP头部,如果还有包体要发送将调用ngx_http_output_filter发送HTTP包体。我们标记首包发送时间理应在发送头部响应的逻辑里,此时普通HTTP模块已经处理请求完毕。以下是ngx_http_send_header代码:

ngx_int_t
ngx_http_send_header(ngx_http_request_t *r)
{
if (r->post_action) {
return NGX_OK;
}

if (r->header_sent) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"header already sent");
return NGX_ERROR;
}

if (r->err_status) {
r->headers_out.status = r->err_status;
r->headers_out.status_line.len = 0;
}

return ngx_http_top_header_filter(r);
}
发送头部时调用了ngx_http_top_header_filter,HTTP框架对其定义如下:

extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;

typedef ngx_int_t (*ngx_http_output_header_filter_pt) (ngx_http_request_t *r);

ngx_http_output_header_filter_pt是HTTP过滤模块处理HTTP头部的方法原型,HTTP框架将所有过滤模块组成一个过滤链表,而ngx_http_top_header_filter为该链表头。在发送HTTP头部时,从ngx_http_top_header_filter指针指向的过滤模块开始执行。我们要做的就是新增一个过滤模块,目的在于记录首包发送的时间。计算首包响应的模块代码如下:

#include 
#include
#include

typedef struct {
ngx_flag_t onoff;
} ngx_http_first_byte_loc_conf_t;

typedef struct {
time_t first_byte_sec; /* head filter phase */
ngx_msec_t first_byte_msec; /* head filter phase */
} ngx_http_first_byte_ctx_t;

static ngx_command_t ngx_http_first_byte_commands[] = {
{
ngx_string("first_byte"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_first_byte_loc_conf_t, onoff),
NULL
},
ngx_null_command
};

static ngx_int_t ngx_http_first_byte_init(ngx_conf_t* cf);
static void* ngx_http_first_byte_create_loc_conf(ngx_conf_t* cf);
static char* ngx_http_first_byte_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child);
static ngx_int_t ngx_http_first_byte_add_variable(ngx_conf_t* cf);
static ngx_int_t ngx_http_first_byte_var_get_handler(ngx_http_request_t* r,
ngx_http_variable_value_t* v, uintptr_t data);
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_int_t ngx_http_first_byte_post_read_phase_handler(ngx_http_request_t* r);

static ngx_http_module_t ngx_http_first_byte_module_ctx = {
ngx_http_first_byte_add_variable, /* preconfiguration */
ngx_http_first_byte_init, /* postconfiguration */

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

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

ngx_http_first_byte_create_loc_conf, /* create location configuration */
ngx_http_first_byte_merge_loc_conf, /* merge location configuration */
};

ngx_module_t ngx_http_first_byte_module = {
NGX_MODULE_V1,
&ngx_http_first_byte_module_ctx, /* module context */
ngx_http_first_byte_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
};

static ngx_int_t ngx_http_first_byte_add_variable(ngx_conf_t* cf) {
ngx_http_variable_t* var = NULL;
ngx_str_t str = ngx_string("first_byte");

var = ngx_http_add_variable(cf, &str, NGX_HTTP_VAR_CHANGEABLE);

if (var == NULL) {
return NGX_ERROR;
}

var->get_handler = ngx_http_first_byte_var_get_handler;

return NGX_OK;
}

static ngx_int_t ngx_http_first_byte_var_get_handler(ngx_http_request_t* r,
ngx_http_variable_value_t* v, uintptr_t data) {
ngx_http_first_byte_ctx_t* ctx = ngx_http_get_module_ctx(r, ngx_http_first_byte_module);

if (!ctx || ctx->first_byte_sec == 0) {
v->valid = 0;
v->not_found = 1;
return NGX_OK;
}

u_char* p = ngx_pcalloc(r->pool, 20);
v->len = ngx_sprintf(p, "%l", (ctx->first_byte_sec - r->start_sec) * 1000 +
(ctx->first_byte_msec - r->start_msec)) - p;
v->data = p;
v->valid = 1;
v->not_found = 0;
return NGX_OK;
}

/*
响应头过滤方法中设置first_byte_sec,first_byte_msec时间
*/
static ngx_int_t ngx_http_first_byte_header_filter(ngx_http_request_t* r) {
ngx_http_first_byte_loc_conf_t* fblcf = ngx_http_get_module_loc_conf(r,
ngx_http_first_byte_module);

if (!fblcf->onoff) {
return ngx_http_next_header_filter(r);
}

ngx_time_t* tp = NULL;
tp = ngx_timeofday();
ngx_http_first_byte_ctx_t* ctx = ngx_http_get_module_ctx(r, ngx_http_first_byte_module);

if (!ctx) {
return ngx_http_next_header_filter(r);
}

ctx->first_byte_sec = tp->sec; //开始发送请求响应头,计时
ctx->first_byte_msec = tp->msec;
return ngx_http_next_header_filter(r);
}

static ngx_int_t ngx_http_first_byte_post_read_phase_handler(ngx_http_request_t* r) {

// get config value
ngx_http_first_byte_loc_conf_t* fblcf = ngx_http_get_module_loc_conf(r, ngx_http_first_byte_module);

// if == 0 return next handler
if (!fblcf->onoff) {
return NGX_DECLINED; //当前处理方法执行完毕,按照顺序执行下一个ngx_http_handler_pt方法
}

ngx_time_t* tp = NULL;
tp = ngx_timeofday();
// maollc memory form current ngx_http_request mem pool
// so when the request is closed,the mem is GCed too
//必须在当前请求的内存池r->pool中分配上下文结构体,这样请求结束的时候占用的结构体才会释放
ngx_http_first_byte_ctx_t* ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_first_byte_ctx_t));
ctx->first_byte_sec = 0;
ctx->first_byte_msec = 0;
ngx_http_set_ctx(r, ctx, ngx_http_first_byte_module);

return NGX_DECLINED;
}

static ngx_int_t ngx_http_first_byte_init(ngx_conf_t* cf) {
ngx_http_handler_pt* h = NULL;
ngx_http_core_main_conf_t* cmcf = NULL;

ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_first_byte_header_filter;

cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);

if (h == NULL) {
return NGX_ERROR;
}

*h = ngx_http_first_byte_post_read_phase_handler;

return NGX_OK;
}

static void* ngx_http_first_byte_create_loc_conf(ngx_conf_t* cf) {
ngx_http_first_byte_loc_conf_t* fblcf = ngx_pcalloc(cf->pool,
sizeof(ngx_http_first_byte_loc_conf_t));
fblcf->Onoff= NGX_CONF_UNSET;
return fblcf;
}

static char* ngx_http_first_byte_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) {
ngx_http_first_byte_loc_conf_t* prev = (ngx_http_first_byte_loc_conf_t*) parent;
ngx_http_first_byte_loc_conf_t* cOnf= (ngx_http_first_byte_loc_conf_t*) child;

ngx_conf_merge_value(conf->onoff, prev->onoff, 0);

return NGX_CONF_OK;
}




推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文详细介绍了解决全栈跨域问题的方法及步骤,包括添加权限、设置Access-Control-Allow-Origin、白名单等。通过这些操作,可以实现在不同服务器上的数据访问,并解决后台报错问题。同时,还提供了解决second页面访问数据的方法。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
author-avatar
圈儿丫头1986
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有