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

nginx对keepalive和pipeline请求处理分析

:本篇文章主要介绍了nginx对keepalive和pipeline请求处理分析,对于PHP教程有兴趣的同学可以参考一下。

原创文章,转载请注明: 转载自pagefault

本文链接地址: nginx对keepalive和pipeline请求处理分析

这次主要来看nginx中对keepalive和pipeline的处理,这里概念就不用介绍了。直接来看nginx是如何来做的。

首先来看keepalive的处理。我们知道http 1.1中keepalive是默认的,除非客户端显式的指定connect头为close。下面就是nginx判断是否需要keepalive的代码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

void

ngx_http_handler(ngx_http_request_t *r)

{

.........................................

switch(r->headers_in.connection_type) {

case0:

//如果版本大于1.0则默认是keepalive

r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);

break;

caseNGX_HTTP_CONNECTION_CLOSE:

//如果指定connection头为close则不需要keepalive

r->keepalive = 0;

break;

caseNGX_HTTP_CONNECTION_KEEP_ALIVE:

r->keepalive = 1;

break;

}

..................................

}


然后我们知道keepalive也就是当前的http request执行完毕后并不会直接关闭当前的连接,因此nginx的keepalive的相关处理也就是清理request的函数中。

nginx清理requst的函数是ngx_http_finalize_request,这个函数中会调用ngx_http_finalize_connection来释放连接,而keepalive的相关判断就在这个函数中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

staticvoid

ngx_http_finalize_connection(ngx_http_request_t *r)

{

ngx_http_core_loc_conf_t *clcf;

clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

.....................................................................

//可以看到如果设置了keepalive,并且timeout大于0,就进入keepalive的处理。

if(!ngx_terminate

&& !ngx_exiting

&& r->keepalive

&& clcf->keepalive_timeout > 0)

{

ngx_http_set_keepalive(r);

return;

} elseif(r->lingering_close && clcf->lingering_timeout > 0) {

ngx_http_set_lingering_close(r);

return;

}

ngx_http_close_request(r, 0);

}

通过上面我们能看到keepalive是通过ngx_http_set_keepalive来进行设置的,接下来我们就来详细的看这个函数。

在这个函数里面会顺带处理pipeline的请求,因此我们一并来看,首先nginx是如何区分pipeline请求的呢,它会假设如果从客户端读取的数据多包含了一些数据,也就是解析完当前的request之后,还有一部分数据,这时,就认为是pipeline请求。

还有一个很重要的地方就是http_connection,我们在前面的blog知道,如果需要alloc large header时候,会先从hc->free里面取,如果没有的话,会新建,然后交给hc->busy去管理。而这个buf,就会在这里被重用,因为large buf的话,需要重新alloc第二次,如果这里buf有重用的话,减少一次分配。

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

hc = r->http_connection;

b = r->header_in;

//一般情况下,当解析完header_in之后,pos会设置为last。也就是读取到的数据刚好是一个完整的http请求.当pos小于last,则说明可能是一个pipeline请求。

if(b->pos last) {

/* the pipelined request */

if(b != c->buffer) {

/*

* If the large header buffers were allocated while the previous

* request processing then we do not use c->buffer for

* the pipelined request (see ngx_http_init_request()).

*

* Now we would move the large header buffers to the free list.

*/

cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

//如果free为空,则新建

if(hc->free== NULL) {

//可以看到是large_client_headers的个数

hc->free= ngx_palloc(c->pool,

cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));

if(hc->free== NULL) {

ngx_http_close_request(r, 0);

return;

}

}

//然后清理当前的request的busy

for(i = 0; i nbusy - 1; i++) {

f = hc->busy[i];

hc->free[hc->nfree++] = f;

f->pos = f->start;

f->last = f->start;

}

//保存当前的header_in buf,以便与下次给free使用。

hc->busy[0] = b;

hc->nbusy = 1;

}

}

然后接下来这部分就是free request,并设置keepalive 定时器.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

r->keepalive = 0;

ngx_http_free_request(r, 0);

c->data = hc;

//设置定时器

ngx_add_timer(rev, clcf->keepalive_timeout);

//然后设置可读事件

if(ngx_handle_read_event(rev, 0) != NGX_OK) {

ngx_http_close_connection(c);

return;

}

wev = c->write;

wev->handler = ngx_http_empty_handler;

然后接下来这部分就是对pipeline的处理。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

if(b->pos last) {

ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");

#if (NGX_STAT_STUB)

(void) ngx_atomic_fetch_add(ngx_stat_reading, 1);

#endif

//设置标记。

hc->pipeline = 1;

c->log->action = "reading client pipelined request line";

//然后扔进post queue,继续进行处理.

rev->handler = ngx_http_init_request;

ngx_post_event(rev, &ngx_posted_events);

return;

}

到达下面,则说明不是pipeline的请求,因此就开始对request, http_connection 进行清理工作。

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

if(ngx_pfree(c->pool, r) == NGX_OK) {

hc->request = NULL;

}

b = c->buffer;

if(ngx_pfree(c->pool, b->start) == NGX_OK) {

/*

* the special note for ngx_http_keepalive_handler() that

* c->buffer's memory was freed

*/

b->pos = NULL;

} else{

b->pos = b->start;

b->last = b->start;

}

.....................................................................

if(hc->busy) {

for(i = 0; i nbusy; i++) {

ngx_pfree(c->pool, hc->busy[i]->start);

hc->busy[i] = NULL;

}

hc->nbusy = 0;

}

设置keepalive的handler。

1

2

3

4

5

6

7

8

9

//后面会详细分析这个函数

rev->handler = ngx_http_keepalive_handler;

if(wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {

if(ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {

ngx_http_close_connection(c);

return;

}

}

最后是对tcp push的处理,这里暂时我就不介绍了,接下来我会有专门一篇blog来介绍nginx对tcp push的操作。

然后我们来看ngx_http_keepalive_handler函数,这个函数是处理keepalive连接,当在连接上再次有可读的事件的时候,就会调用这个handler。

这个handler比较简单,就是创建新的buf,然后重新开始一个http request的执行(调用ngx_http_init_request)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

b = c->buffer;

size = b->end - b->start;

if(b->pos == NULL) {

/*

* The c->buffer's memory was freed by ngx_http_set_keepalive().

* However, the c->buffer->start and c->buffer->end were not changed

* to keep the buffer size.

*/

//重新分配buf

b->pos = ngx_palloc(c->pool, size);

if(b->pos == NULL) {

ngx_http_close_connection(c);

return;

}

b->start = b->pos;

b->last = b->pos;

b->end = b->pos + size;

}

然后尝试读取数据,如果没有可读数据,则会将句柄再次加入可读事件

1

2

3

4

5

6

7

8

9

n = c->recv(c, b->last, size);

c->log_error = NGX_ERROR_INFO;

if(n == NGX_AGAIN) {

if(ngx_handle_read_event(rev, 0) != NGX_OK) {

ngx_http_close_connection(c);

}

return;

}

最后如果读取了数据,则进入request的处理。

1

ngx_http_init_request(rev);

最后我们再来看ngx_http_init_request函数,这次主要来看当时pipeline请求的时候,nginx是如何来重用request的。
这里要注意hc->busy[0],前面我们知道,如果是pipeline请求,我们会保存前面没有解析完毕的request header_in,这是因为我们可能已经读取了pipeline请求的第二个请求的一些头。

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

//取得request,这里我们知道,在pipeline请求中,我们会保存前一个request.

r = hc->request;

if(r) {

//如果存在,则我们重用前一个request.

ngx_memzero(r, sizeof(ngx_http_request_t));

r->pipeline = hc->pipeline;

//如果nbusy存在

if(hc->nbusy) {

//则保存这个header_in,然后下面直接解析。

r->header_in = hc->busy[0];

}

} else{

r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));

if(r == NULL) {

ngx_http_close_connection(c);

return;

}

hc->request = r;

}

//保存请求

c->data = r;

从上面的代码,然后再结合我前一篇blog,我们就知道large header主要是针对pipeline的了,因为在pipeline中,前一个request如果多读了下一个request的一些头的话,这样子下次解析的时候就有可能会超过本来分配的client_header_buffer_size,此时,我们就需要重新分配一个header,也就是large header了,所以这里httpconnection主要就是针对pipeline的情况,而keepalive的连接并不是pipeline的请求的话,为了节省内存,就把前一个request释放掉了.

以上就介绍了nginx对keepalive和pipeline请求处理分析,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

推荐阅读
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 解决php错误信息不显示在浏览器上的方法
    本文介绍了解决php错误信息不显示在浏览器上的方法。作者发现php中的各种错误信息并不显示在浏览器上,而是需要在日志文件中查看。为了解决这个问题,作者提供了一种解决方式:通过修改php.ini文件中的display_errors参数为On,并重启服务。这样就可以在浏览器上直接显示php错误信息了。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 本文介绍了在无法联网的情况下,通过下载rpm包离线安装zip和unzip的方法。详细介绍了如何搜索并下载合适的rpm包,以及如何使用rpm命令进行安装。 ... [详细]
  • 负载均衡_Nginx反向代理动静分离负载均衡及rewrite隐藏路径详解(Nginx Apache MySQL Redis)–第二部分
    nginx反向代理、动静分离、负载均衡及rewrite隐藏路径详解 ... [详细]
  • Linux下部署Symfoy2对app/cache和app/logs目录的权限设置,symfoy2logs
    php教程|php手册xml文件php教程-php手册Linux下部署Symfoy2对appcache和applogs目录的权限设置,symfoy2logs黑色记事本源码,vsco ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 关羽败走麦城时路过马超封地 马超为何没有出手救人
    对当年关羽败走麦城,恰好路过马超的封地,为啥马超不救他?很感兴趣的小伙伴们,趣历史小编带来详细的文章供大家参考。说到英雄好汉,便要提到一本名著了,没错,那就是《三国演义》。书中虽 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 橱窗设计的表现手法及其应用
    本文介绍了橱窗设计的表现手法,包括直接展示、寓意与联想、夸张与幽默等。通过对商品的折、拉、叠、挂、堆等陈列技巧,橱窗设计能够充分展现商品的形态、质地、色彩、样式等特性。同时,寓意与联想可以通过象形形式或抽象几何道具来唤起消费者的联想与共鸣,创造出强烈的时代气息和视觉空间。合理的夸张和贴切的幽默能够明显夸大商品的美的因素,给人以新颖奇特的心理感受,引起人们的笑声和思考。通过这些表现手法,橱窗设计能够有效地传达商品的个性内涵,吸引消费者的注意力。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
author-avatar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有