点赞
评论
收藏
分享
举报
Nginx stream模块的执行阶段
发表于2020-09-08 18:27

浏览 8.7k

文章标签

Nginx的stream模块提供了TCP负载均衡的功能,最初的stream模块比较简单,在nginx-1.11.4后也开始采用类似HTTP模块中分阶段处理请求的方式。

stream模块的处理阶段

在ngx_stream.h中定义了stream模块的7个阶段。如下面所示

  1. typedef enum {
  2. NGX_STREAM_POST_ACCEPT_PHASE = 0,
  3. NGX_STREAM_PREACCESS_PHASE,
  4. NGX_STREAM_ACCESS_PHASE,
  5. NGX_STREAM_SSL_PHASE,
  6. NGX_STREAM_PREREAD_PHASE,
  7. NGX_STREAM_CONTENT_PHASE,
  8. NGX_STREAM_LOG_PHASE
  9. } ngx_stream_phases;

与HTTP模块相同,每个阶段有相应的checker检查方法和handler回调方法,每个阶段都有零个或多个ngx_stream_phase_handler_t结构体。

  1. typedef struct ngx_stream_phase_handler_s ngx_stream_phase_handler_t;
  2. typedef ngx_int_t (*ngx_stream_phase_handler_pt)(ngx_stream_session_t *s,
  3. ngx_stream_phase_handler_t *ph);
  4. typedef ngx_int_t (*ngx_stream_handler_pt)(ngx_stream_session_t *s);
  5. typedef void (*ngx_stream_content_handler_pt)(ngx_stream_session_t *s);
  6. struct ngx_stream_phase_handler_s {
  7. ngx_stream_phase_handler_pt checker;
  8. ngx_stream_handler_pt handler;
  9. ngx_uint_t next;
  10. };

stream模块接到请求后,初始化连接后调用ngx_stream_core_run_phases依次执行各个阶段的处理函数。

  1. void
  2. ngx_stream_core_run_phases(ngx_stream_session_t *s)
  3. {
  4. ngx_int_t rc;
  5. ngx_stream_phase_handler_t *ph;
  6. ngx_stream_core_main_conf_t *cmcf;
  7. cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
  8. ph = cmcf->phase_engine.handlers;
  9. while (ph[s->phase_handler].checker) {
  10. rc = ph[s->phase_handler].checker(s, &ph[s->phase_handler]);
  11. if (rc == NGX_OK) {
  12. return;
  13. }
  14. }
  15. }

POST_ACCEPT、PREACCESS、ACCESS阶段

POST_ACCEPT、PREACCESS、ACCESS阶段的checker检查方法都是ngx_stream_core_generic_phase,这三个阶段主要进行访问控制的一些工作。因为stream模块处理TCP请求,阶段之间关系比较简单,将模块挂载在哪个阶段只会影响执行的顺序。

下面是ngx_stream_core_generic_phase函数

  1. ngx_int_t
  2. ngx_stream_core_generic_phase(ngx_stream_session_t *s,
  3. ngx_stream_phase_handler_t *ph)
  4. {
  5. ngx_int_t rc;
  6. /*
  7. * generic phase checker,
  8. * used by all phases, except for preread and content
  9. */
  10. ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  11. "generic phase: %ui", s->phase_handler);
  12. rc = ph->handler(s);
  13. if (rc == NGX_OK) {
  14. s->phase_handler = ph->next;
  15. return NGX_AGAIN;
  16. }
  17. if (rc == NGX_DECLINED) {
  18. s->phase_handler++;
  19. return NGX_AGAIN;
  20. }
  21. if (rc == NGX_AGAIN || rc == NGX_DONE) {
  22. return NGX_OK;
  23. }
  24. if (rc == NGX_ERROR) {
  25. rc = NGX_STREAM_INTERNAL_SERVER_ERROR;
  26. }
  27. ngx_stream_finalize_session(s, rc);
  28. return NGX_OK;
  29. }

与HTTP模块类似,根据rc = ph->handler(s)结果进行处理。

  • rc为NGX_OK表示这一阶段的工作完成,进入下一个阶段
  • rc为NGX_DECLINED表示进入下一个模块处理
  • rc为NGX_AGAIN表示当前请求暂时无法完成,返回NGX_OK
  • rc为NGX_DONE表示当前请求告一段落,会被再次调用,返回NGX_OK
  • rc为NGX_ERROR表示出现错误,结束请求
  • rc为其他值时表示处理完成,此时rc为状态码

注意,TCP请求中没有HTTP请求那样的状态码,这里的状态码只是表示连接处理的信息,如源超时状态码为502。

SSL阶段

SSL阶段的checker检查方法也是ngx_stream_core_generic_phase,这里挂载了ngx_stream_ssl_module,开启SSL时这里进行SSL握手的处理。

PREREAD阶段

这个阶段的特点是会读取下游的请求包体。读取后调用handler回调函数处理。读取的数据会保存在c->buffer中。如CONTENT阶段中ngx_stream_proxy_module在处理时会将c->buffer中的内容发送给上游源服务器。如果你在CONTENT阶段之前读取了下游的数据又想将这些数据通过proxy发送给上游,就可以加数据放到c->buffer中。

这里官方挂载了ngx_stream_ssl_preread_module模块,当TCP连接采用SSL通信时用这个模块可以解析client hello握手包,从extensions字段中得到server_name赋值给变量$ssl_preread_server_name中。这个模块只能在监听地址没有开启SSL,但是上下游却是通过SSL进行通信的情况下使用。

PREREAD阶段checker检查方法是ngx_stream_core_preread_phase。如下所示

  1. ngx_int_t
  2. ngx_stream_core_preread_phase(ngx_stream_session_t *s,
  3. ngx_stream_phase_handler_t *ph)
  4. {
  5. size_t size;
  6. ssize_t n;
  7. ngx_int_t rc;
  8. ngx_connection_t *c;
  9. ngx_stream_core_srv_conf_t *cscf;
  10. c = s->connection;
  11. c->log->action = "prereading client data";
  12. cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
  13. if (c->read->timedout) {
  14. rc = NGX_STREAM_OK;
  15. } else if (c->read->timer_set) {
  16. rc = NGX_AGAIN;
  17. } else {
  18. rc = ph->handler(s);
  19. }
  20. while (rc == NGX_AGAIN) {
  21. if (c->buffer == NULL) {
  22. c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size);
  23. if (c->buffer == NULL) {
  24. rc = NGX_ERROR;
  25. break;
  26. }
  27. }
  28. size = c->buffer->end - c->buffer->last;
  29. if (size == 0) {
  30. ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full");
  31. rc = NGX_STREAM_BAD_REQUEST;
  32. break;
  33. }
  34. if (c->read->eof) {
  35. rc = NGX_STREAM_OK;
  36. break;
  37. }
  38. if (!c->read->ready) {
  39. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  40. rc = NGX_ERROR;
  41. break;
  42. }
  43. if (!c->read->timer_set) {
  44. ngx_add_timer(c->read, cscf->preread_timeout);
  45. }
  46. c->read->handler = ngx_stream_session_handler;
  47. return NGX_OK;
  48. }
  49. n = c->recv(c, c->buffer->last, size);
  50. if (n == NGX_ERROR) {
  51. rc = NGX_STREAM_OK;
  52. break;
  53. }
  54. if (n > 0) {
  55. c->buffer->last += n;
  56. }
  57. rc = ph->handler(s);
  58. }
  59. if (c->read->timer_set) {
  60. ngx_del_timer(c->read);
  61. }
  62. if (rc == NGX_OK) {
  63. s->phase_handler = ph->next;
  64. return NGX_AGAIN;
  65. }
  66. if (rc == NGX_DECLINED) {
  67. s->phase_handler++;
  68. return NGX_AGAIN;
  69. }
  70. if (rc == NGX_DONE) {
  71. return NGX_OK;
  72. }
  73. if (rc == NGX_ERROR) {
  74. rc = NGX_STREAM_INTERNAL_SERVER_ERROR;
  75. }
  76. ngx_stream_finalize_session(s, rc);
  77. return NGX_OK;
  78. }

CONTENT阶段

CONTENT阶段的检查方法是ngx_stream_core_content_phase

  1. ngx_int_t
  2. ngx_stream_core_content_phase(ngx_stream_session_t *s,
  3. ngx_stream_phase_handler_t *ph)
  4. {
  5. ngx_connection_t *c;
  6. ngx_stream_core_srv_conf_t *cscf;
  7. c = s->connection;
  8. c->log->action = NULL;
  9. cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
  10. if (c->type == SOCK_STREAM
  11. && cscf->tcp_nodelay
  12. && ngx_tcp_nodelay(c) != NGX_OK)
  13. {
  14. ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
  15. return NGX_OK;
  16. }
  17. cscf->handler(s);
  18. return NGX_OK;
  19. }

很显然这一阶段只能有一个处理方法,如进行TCP代理时需要ngx_stream_proxy_module,这里挂载的就是proxy模块的handler函数

LOG阶段

LOG阶段虽然是一个独立的阶段,却是在连接结束时调用的。在CONTENT阶段结束时就会调用ngx_stream_finalize_session结束请求。这部分在ngx_stream_handler.c中

  1. void
  2. ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc)
  3. {
  4. ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  5. "finalize stream session: %i", rc);
  6. s->status = rc;
  7. ngx_stream_log_session(s);
  8. ngx_stream_close_connection(s->connection);
  9. }
  10. static void
  11. ngx_stream_log_session(ngx_stream_session_t *s)
  12. {
  13. ngx_uint_t i, n;
  14. ngx_stream_handler_pt *log_handler;
  15. ngx_stream_core_main_conf_t *cmcf;
  16. cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
  17. log_handler = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.elts;
  18. n = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.nelts;
  19. for (i = 0; i < n; i++) {
  20. log_handler[i](s);
  21. }
  22. }

转载于:https://my.oschina.net/u/2539854/blog/1047240

已修改于2023-03-09 02:24
创作不易,留下一份鼓励
守望

暂无个人介绍

关注



写下您的评论
发表评论
全部评论(0)

按点赞数排序

按时间排序

关于作者
守望
这家伙很懒还未留下介绍~
89
文章
0
问答
18
粉丝
相关文章
介绍nginx网页配置工具QQ技术交流群1:1106758598QQ技术交流群2:560797506邮箱: cym1102@qq.com官网地址: http://www.nginxwebui.cn码云: https://gitee.com/cym1102/nginxWebUIgithub: https://github.com/cym1102/nginxWebUI功能特点nginxWebUI也可管理多个nginx服务器集群,随时一键切换到对应服务器上进行nginx配置,也可以一键将某台服务器配置同步到其他服务器,方便集群管理.部署此项目后,配置nginx再也不用上网各种搜索配置代码,再也不用手动申请和配置ssl证书,只需要在本项目中进行增删改查就可方便的配置和启动nginx。技术说明本项目是基于springBoot的web系统,数据库使用sqlite,因此服务器上不需要安装任何数据库项目启动时会释放一个.sqlite.db到系统用户文件夹中,注意进行备份本系统通过Let'sencrypt申请证书,使用acme.sh脚本
点赞 6
浏览 23.4k
  前三周学习了陶辉老师的“NGINX基础培训系列课程”,感觉受益良多,在这里想把一些知识点记录一下,和大家分享一下知识点,也方便日后的随手查看,温故知新。  首先,我们了解到了Nginx的版本,Nginx发布版本分为主线版本和稳定版本,区分两个版本也非常简单,主线版本版本号为单数,比如1.19,稳定版本为双数,比如1.18,今天我要说的是稳定版本,这个版本会尽量少的减少Nginx的bug问题,适用于生产环境,这里我不建议使用Nginx和其他软件一样在生产环境中落后一个或多个大版本使用,之前生产环境做过漏扫,发现我们编译自带的Nginx版本为:nginx/1.13.3(查询命令为nginx-V),结果出现了多个漏洞,四个高危和一个中危漏洞:        通过升级Nginx到稳定版最新版本后修复!  其次,是Nginx发行版本的选择,目前比较流行的有:nginx、nginxplus、Tengine、openresty、ope
点赞 1
浏览 16.8k
感谢您参加“NGINX从入门到精通进阶系列培训”!以下为培训的问答、课件和录像,希望您能通过此培训学有所得,祝学习进步!>问与答:- 基础篇+高级篇 - 应用篇+实战篇(New)>课件(PPT):基础篇:-NGINX概要、安装、配置:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-Setup.pdf-NGINX日志、运维:https://interact.f5.com/rs/653-SMC-783/images/cnfeb22-nginxcorecourse-maintenance.pdf高级篇:-NGINX变量、API:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-API.pdf-NGINXSSL、NJS:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-SSL.pdf
点赞 10
浏览 19.3k