博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《深入剖析Nginx》——2.6 特殊应用逻辑的调试
阅读量:7098 次
发布时间:2019-06-28

本文共 3878 字,大约阅读时间需要 12 分钟。

本节书摘来自异步社区《深入剖析Nginx》一书中的第2章,第2.6节,作者: 高群凯 更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.6 特殊应用逻辑的调试

前面所讲的调试方法都是针对Nginx本身很容易跑到的逻辑,而对于某些只有在特定情况下才会被执行到的代码,又该怎样去调试呢?举个例子,我们知道Nginx里有大量的超时处理,比如,如果读取客户端请求头部数据超时,Nginx就将执行对应的超时处理函数,假设我想通过单步执行的方式来了解这部分相关逻辑,无疑就得让Nginx的执行逻辑走到这条路径上来。由于此时影响Nginx行为的决定因素是客户端所发送的请求头部数据,我们就必须在客户端做动作来构造出这种场景。一般的浏览器,如IE、Firefox等发出请求的行为基本已经固定,而常用的命令行工具,比如curl、wget的源代码又略显复杂,定制它们的请求动作和改变环境来构造所需的场景相对较为麻烦,所以一种更便利的方法就是我们自己写个socket通信的客户端即可,而这并不需要多少代码。

下面给出一个测试示例用代码,为了简单,所以服务器IP和端口都是固定在代码里的,用于发送数据的函数write()调用也未做返回值判断等(后续还有其他类似测试代码也是如此,这点请注意)。

00: 代码片段2.6-1,文件名: request_timeout.c01: /**02:  * gcc -Wall -g -o request_timeout request_timeout.c03:  */04: #include 
05: #include
06: #include
07: #include
08: #include
09: #include
10: #include
11: #include
12: #include
13: 14: //char req_header[] = "GET / HTTP/1.1\r\nUser-Agent: curl/7.19.7\r\nHost: 127.0.0.1\ r\nAccept: */*\r\n\r\n";15: char req_header[] = "GET / HTTP/1.1\r\nUser-Agent: curl/7.19.7\r\n";16: 17: int main(int argc, char *const *argv)18: {19: int sockfd;20: struct sockaddr_in server_addr;21: 22: if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {23: fprintf (stderr, "Socket error,%s\r\n", strerror (errno));24: return -1;25: }26: 27: bzero (&server_addr, sizeof (server_addr));28: server_addr.sin_family = AF_INET;29: server_addr.sin_port = htons (80);30: 31: if(!inet_aton("192.168.1.1", &server_addr.sin_addr)) {32: fprintf (stderr, "Bad address:%s\r\n", strerror (errno));33: close (sockfd);34: return -1;35: }36: 37: if (connect (sockfd, (struct sockaddr *) (&server_addr),38: sizeof (struct sockaddr)) == -1) {39: fprintf (stderr, "Connect Error:%s\r\n", strerror (errno));40: close (sockfd);41: return -1;42: }43: 44: write (sockfd, req_header, strlen(req_header));45: 46: close (sockfd);47: return 0;48: }

该程序的代码比较简单,变量req_header存储的是http请求头部数据,被注释掉的是正常的请求头,而我这里使用的请求头是不完整的(正常请求头可以用wget、curl或wireshark1等工具获得,异常请求头必须根据自己所预期场景来进行构造,比如在这里,其他异常情况的请求头可能导致Nginx以其他错误方式返回而不是进行超时监控),所以这会使得Nginx在接收到该请求后,持续等待进一步的头部数据,直到超时。编译这个源代码得到应用程序request_timeout。

将接受http请求的Nginx工作进程绑定到gdb,然后在超时函数ngx_event_expire_timers()内的第149行下断点并按c继续。

75: 代码片段2.6-2,文件名: ngx_event_timer.c76: void77: ngx_event_expire_timers(void)78: {79: …147:                ev->timedout = 1;148: 149:                ev->handler(ev);

这个断点是Nginx已经捕获到超时事件,设置其超时旗标并调用对应的回调函数进行处理。在另一个gdb内执行request_timeout,当然,我们需要让它停止在第47行2,避免程序退出,导致它与Nginx工作进程之间的连接断开。等待约60秒(Nginx读取请求头部数据的默认超时时间为60秒,可通过配置指令client_header_timeout修改)后,attach到Nginx工作进程的gdb就会断下来,按s跟进函数,再顺着执行路径而下就会发现此时Nginx将执行到这个逻辑里。

955: 代码片段2.6-3,文件名: ngx_event_timer.c956: static void957: ngx_http_process_request_headers(ngx_event_t *rev)958: {959: …976:      if (rev->timedout) {977:             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");978:             c->timedout = 1;979:             ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);980:             return;981:      }

将执行到第976行的if判断内部,即连接超时,我们看到对于在读取请求头部数据超时的情况下,Nginx工作进程最后所做的几步主要工作,即日志记录、关闭请求并返回。通过这样一个实例,我们也就了解了如何去调试这样的特殊应用逻辑,不仅仅只是针对客户端,对于后端应用服务器也能如此进行模拟构造。

上面演示的环境构造步骤,虽然比较简单且能真实模拟,但毕竟需要我们了解它的细节,也就是需知道触发这种情况的前提条件,如果前提条件比较多,那么模拟起来可能还是比较麻烦,其实,如果我们只是了解一下Nginx如果这样执行会怎么样,那么完全可以通过利用gdb的p命令或set命令修改对应条件变量的值来达到目的。比如在前面的例子里,在一般情况下,rev->timedout为0,即不超时而无法执行第977-980行代码,但我又想看一下执行这几条语句的情况会怎么样,那么就可以像下面这样做。

Breakpoint 1, ngx_http_process_request_headers (rev=0x94a6bfc) at src/http/ ngx_http_ request.c:976976     if (rev->timedout) {(gdb) p rev->timedout $1 = 0(gdb) p rev->timedout=1 $2 = 1(gdb) n977            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");(gdb) set rev->timedout=0(gdb) p rev->timedout$3 = 0(gdb)

通过执行“p rev->timedout=1”把变量rev->timedout的值改为1,这样就执行到第977行了,当然,如上所示,set命令也可以改变Nginx执行变量的值。值得特别注意的是,这样做仅仅只是因为改变了条件判断的变量值而使得Nginx程序执行路径发生变化,但是其在新的路径上,可能由于使用的某些变量值不是原本所期望的情况而导致执行异常。

转载地址:http://mboql.baihongyu.com/

你可能感兴趣的文章
Twitter 宣布抛弃 Mesos,全面转向Kubernetes
查看>>
30 岁转行做Python开发晚吗?而且是零基础
查看>>
zabbix主动模式和被动模式-添加监控主机-添加自定义模板- 处理图形中的乱码-自动发现...
查看>>
Qt之QPainter的坐标变换:QTransform
查看>>
关于vue的生命周期
查看>>
【更新】Essential Studio for ASP.NET MVC更新至2018 v4(四)
查看>>
Java基础知识
查看>>
Solr搜索引擎 — SolrCloud安装和集群配置
查看>>
debian 6 上安装 fcitx-4.x
查看>>
DualPivotQuicksort
查看>>
linux Centos 6.5 安装桌面环境GNOME
查看>>
OSChina 周六乱弹 ——我的闺蜜是总统
查看>>
Redux DevTools 工具 (redux-devtools是一个有趣而又高效的redux开发工具)
查看>>
Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
查看>>
禁止百度流氓安装
查看>>
自定义异常笔记
查看>>
17款最佳的代码审查工具
查看>>
mysql_5.7.17 安装时无法启动,尝试很多方法最后一招搞定!!!
查看>>
Nginx 配置多站点
查看>>
Object-C代码练习【代码块】
查看>>