diff --git a/nginx-demo/nginx-src/nginx-1.26.0/gdb.cfg b/nginx-demo/nginx-src/nginx-1.26.0/gdb.cfg new file mode 100644 index 0000000..3764352 --- /dev/null +++ b/nginx-demo/nginx-src/nginx-1.26.0/gdb.cfg @@ -0,0 +1,16 @@ +break ngx_http_upstream_check_timeout_handler +break ngx_http_upstream_check_http_reinit +disable $bpnum +break ngx_http_upstream_check_http_parse +break ngx_http_upstream_check_http_init +disable $bpnum +break ngx_http_upstream_check_recv_handler +break ngx_http_upstream_check_send_handler +break /workspaces/cpp-5/nginx_upstream_check_module/ngx_http_upstream_check_module.c:1332 +disable $bpnum +watch check_peers_ctx +break /workspaces/cpp-5/nginx_upstream_check_module/ngx_http_upstream_check_module.c:3757 +watch check_peers_ctx +watch check_peers_ctx +break ngx_http_upstream_check_begin_handler +break ngx_http_upstream_check_connect_handler diff --git a/nginx-demo/nginx_upstream_check_module/ngx_http_upstream_check_module.c b/nginx-demo/nginx_upstream_check_module/ngx_http_upstream_check_module.c index 67bce96..640bd3f 100644 --- a/nginx-demo/nginx_upstream_check_module/ngx_http_upstream_check_module.c +++ b/nginx-demo/nginx_upstream_check_module/ngx_http_upstream_check_module.c @@ -117,7 +117,7 @@ typedef ngx_int_t (*ngx_http_upstream_check_packet_parse_pt) typedef void (*ngx_http_upstream_check_packet_clean_pt) (ngx_http_upstream_check_peer_t *peer); -struct ngx_http_upstream_check_peer_s { /*peer关键配置类:包括一组回调函数*/ +struct ngx_http_upstream_check_peer_s { ngx_flag_t state; ngx_pool_t *pool; ngx_uint_t index; @@ -125,17 +125,17 @@ struct ngx_http_upstream_check_peer_s { /*peer关键配置类:包括一组回 ngx_str_t *upstream_name; ngx_addr_t *check_peer_addr; ngx_addr_t *peer_addr; - ngx_event_t check_ev; /*关键数据结构:check event*/ - ngx_event_t check_timeout_ev; /*关键数据结构: check timeout event*/ - ngx_peer_connection_t pc; + ngx_event_t check_ev; /* 主动探测事件 */ + ngx_event_t check_timeout_ev; /* 主动探测超时事件 */ + ngx_peer_connection_t pc; /* 保存与后端服务器的连接 */ void *check_data; - ngx_event_handler_pt send_handler; /*TODO: ???*/ - ngx_event_handler_pt recv_handler; /*TODO: ???*/ + ngx_event_handler_pt send_handler; /* ngx_connection_t中的write事件handler:向后端服务器发送探测数据 */ + ngx_event_handler_pt recv_handler; /* ngx_connection_t中的read事件handler:从后端服务器接收探测结果数据 */ - ngx_http_upstream_check_packet_init_pt init; /*TODO: ???*/ - ngx_http_upstream_check_packet_parse_pt parse; /*TODO: ???*/ - ngx_http_upstream_check_packet_clean_pt reinit; /*TODO: ???*/ + ngx_http_upstream_check_packet_init_pt init; + ngx_http_upstream_check_packet_parse_pt parse; + ngx_http_upstream_check_packet_clean_pt reinit; ngx_http_upstream_check_peer_shm_t *shm; ngx_http_upstream_check_srv_conf_t *conf; @@ -163,7 +163,7 @@ typedef struct { #define NGX_CHECK_HTTP_5XX 0x0010 #define NGX_CHECK_HTTP_ERR 0x8000 -typedef struct {/*核心配置:包括了check相关的回调函数*/ +typedef struct { ngx_uint_t type; ngx_str_t name; @@ -173,12 +173,12 @@ typedef struct {/*核心配置:包括了check相关的回调函数*/ /* HTTP */ ngx_uint_t default_status_alive; - ngx_event_handler_pt send_handler; /*TODO: ???*/ - ngx_event_handler_pt recv_handler; /*TODO: ???*/ + ngx_event_handler_pt send_handler; + ngx_event_handler_pt recv_handler; - ngx_http_upstream_check_packet_init_pt init; /*TODO: ???*/ - ngx_http_upstream_check_packet_parse_pt parse; /*TODO: ???*/ - ngx_http_upstream_check_packet_clean_pt reinit; /*TODO: ???*/ + ngx_http_upstream_check_packet_init_pt init; + ngx_http_upstream_check_packet_parse_pt parse; + ngx_http_upstream_check_packet_clean_pt reinit; unsigned need_pool; unsigned need_keepalive; @@ -941,9 +941,9 @@ ngx_http_upstream_check_free_peer(ngx_uint_t index) ngx_shmtx_unlock(&peer[index].shm->mutex); } - +/*核心函数:根据当前配置,加载peer对象,为各个peer对象注册回调函数*/ static ngx_int_t -ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) +ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) /*此函数被调用的时间是:master进程创建出worker进程时,在初始化当前module的cycle对象时,被调用*/ { ngx_uint_t i; ngx_msec_t t, delay; @@ -978,7 +978,7 @@ ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) for (i = 0; i < peers->peers.nelts; i++) { peer[i].shm = &peer_shm[i]; - peer[i].check_ev.handler = ngx_http_upstream_check_begin_handler; /* TODO 整个回调函数处理什么事件event?*/ + peer[i].check_ev.handler = ngx_http_upstream_check_begin_handler; /* 回调函数:处理定时check后端服务器的事件*/ peer[i].check_ev.log = cycle->log; peer[i].check_ev.data = &peer[i]; peer[i].check_ev.timer_set = 0; @@ -999,8 +999,8 @@ ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) } } - peer[i].send_handler = cf->send_handler; - peer[i].recv_handler = cf->recv_handler; + peer[i].send_handler = cf->send_handler; /* send event handler*/ + peer[i].recv_handler = cf->recv_handler; /* recv event handler*/ peer[i].init = cf->init; peer[i].parse = cf->parse; @@ -1013,13 +1013,13 @@ ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) delay = ucscf->check_interval > 1000 ? ucscf->check_interval : 1000; t = ngx_random() % delay; - ngx_add_timer(&peer[i].check_ev, t); + ngx_add_timer(&peer[i].check_ev, t); /*为每个后端服务器(即peer)添加timer:nginx后端会计时,时间到就触发事件的回调函数*/ } return NGX_OK; } -/* 处理check相关event*/ +/* timer event:定期check的事件*/ static void ngx_http_upstream_check_begin_handler(ngx_event_t *event) { @@ -1055,7 +1055,7 @@ ngx_http_upstream_check_begin_handler(ngx_event_t *event) return; } - interval = ngx_current_msec - peer->shm->access_time; + interval = ngx_current_msec - peer->shm->access_time; /* 距离上一次检测过去了多少毫秒 */ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, event->log, 0, "http check begin handler index: %ui, owner: %P, " "ngx_pid: %P, interval: %M, check_interval: %M", @@ -1111,7 +1111,7 @@ ngx_http_upstream_check_connect_handler(ngx_event_t *event) peer = event->data; ucscf = peer->conf; - if (peer->pc.connection != NULL) { + if (peer->pc.connection != NULL) { /* 已与后端服务器建立连接 */ c = peer->pc.connection; if ((rc = ngx_http_upstream_check_peek_one_byte(c)) == NGX_OK) { goto upstream_check_connect_done; @@ -1122,20 +1122,20 @@ ngx_http_upstream_check_connect_handler(ngx_event_t *event) } ngx_memzero(&peer->pc, sizeof(ngx_peer_connection_t)); - peer->pc.sockaddr = peer->check_peer_addr->sockaddr; /*TODO 挑选后端服务器*/ + peer->pc.sockaddr = peer->check_peer_addr->sockaddr; peer->pc.socklen = peer->check_peer_addr->socklen; peer->pc.name = &peer->check_peer_addr->name; - peer->pc.get = ngx_event_get_peer; + peer->pc.get = ngx_event_get_peer; /* 如何挑选后端服务器?由Nginx内置的轮询(round robin)和IP哈希(ip hash)模块提供! */ peer->pc.log = event->log; peer->pc.log_error = NGX_ERROR_ERR; peer->pc.cached = 0; peer->pc.connection = NULL; - rc = ngx_event_connect_peer(&peer->pc); + rc = ngx_event_connect_peer(&peer->pc); /* 连接后端服务器,等待触发write和send事件的回调函数 */ - if (rc == NGX_ERROR || rc == NGX_DECLINED) { + if (rc == NGX_ERROR || rc == NGX_DECLINED) { /* 检测后端服务器失败 */ ngx_http_upstream_check_status_update(peer, 0); ngx_http_upstream_check_clean_event(peer); return; @@ -1171,7 +1171,7 @@ ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c) ngx_int_t n; ngx_err_t err; - n = recv(c->fd, buf, 1, MSG_PEEK); + n = recv(c->fd, buf, 1, MSG_PEEK); /* 系统调用:从指定socket接收数据 */ err = ngx_socket_errno; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err, @@ -1315,7 +1315,7 @@ ngx_http_upstream_check_send_handler(ngx_event_t *event) goto check_send_fail; } - if (peer->init == NULL || peer->init(peer) != NGX_OK) { + if (peer->init == NULL || peer->init(peer) != NGX_OK) { /* 初始化peer:调用ngx_http_upstream_check_http_init */ ngx_log_error(NGX_LOG_ERR, event->log, 0, "check init error with peer: %V ", @@ -1329,7 +1329,7 @@ ngx_http_upstream_check_send_handler(ngx_event_t *event) while (ctx->send.pos < ctx->send.last) { - size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos); + size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos); /* 向后端服务器发送探测数据 */ #if (NGX_DEBUG) { @@ -1360,14 +1360,14 @@ ngx_http_upstream_check_send_handler(ngx_event_t *event) return; -check_send_fail: +check_send_fail: /*心跳数据发送失败*/ ngx_http_upstream_check_status_update(peer, 0); ngx_http_upstream_check_clean_event(peer); } static void -ngx_http_upstream_check_recv_handler(ngx_event_t *event) +ngx_http_upstream_check_recv_handler(ngx_event_t *event) /*处理后端服务器返回的响应数据*/ { u_char *new_buf; ssize_t size, n; @@ -1380,8 +1380,8 @@ ngx_http_upstream_check_recv_handler(ngx_event_t *event) return; } - c = event->data; - peer = c->data; + c = event->data; + peer = c->data; /*响应数据被nginx封装在event中:rev event的data中,是connection类型,connection的data中是自定义的peer数据(如何关联上的呢?在ngx_http_upstream_check_begin_handler处理check事件时),peer中有check上下文数据,其中就包含了send和recv数据*/ if (peer->state != NGX_HTTP_CHECK_SEND_DONE) { @@ -1392,7 +1392,7 @@ ngx_http_upstream_check_recv_handler(ngx_event_t *event) return; } - ctx = peer->check_data; + ctx = peer->check_data; /*健康检测上下文*/ if (ctx->recv.start == NULL) { /* 1/2 of the page_size, is it enough? */ @@ -1449,7 +1449,7 @@ ngx_http_upstream_check_recv_handler(ngx_event_t *event) } } - rc = peer->parse(peer); + rc = peer->parse(peer); /*解析响应数据:调用ngx_http_upstream_check_http_parse*/ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http check parse rc: %i, peer: %V ", @@ -2516,19 +2516,19 @@ ngx_http_upstream_check_status_update(ngx_http_upstream_check_peer_t *peer, ucscf = peer->conf; - if (result) { + if (result) { /* 1 检测成功 */ peer->shm->rise_count++; peer->shm->fall_count = 0; - if (peer->shm->down && peer->shm->rise_count >= ucscf->rise_count) { + if (peer->shm->down && peer->shm->rise_count >= ucscf->rise_count) { /* 成功次数达到复活阈值,复活后端服务器 */ peer->shm->down = 0; ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "enable check peer: %V ", &peer->check_peer_addr->name); } - } else { + } else { /* 0 检测失败 */ peer->shm->rise_count = 0; peer->shm->fall_count++; - if (!peer->shm->down && peer->shm->fall_count >= ucscf->fall_count) { + if (!peer->shm->down && peer->shm->fall_count >= ucscf->fall_count) { /* 失败次数达到关停阈值,关停后端服务器 */ peer->shm->down = 1; ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "disable check peer: %V ", @@ -2536,7 +2536,7 @@ ngx_http_upstream_check_status_update(ngx_http_upstream_check_peer_t *peer, } } - peer->shm->access_time = ngx_current_msec; + peer->shm->access_time = ngx_current_msec; /* 记录检测时间,以毫秒为单位*/ } @@ -2574,7 +2574,7 @@ ngx_http_upstream_check_clean_event(ngx_http_upstream_check_peer_t *peer) peer->state = NGX_HTTP_CHECK_ALL_DONE; if (peer->check_data != NULL && peer->reinit) { - peer->reinit(peer); + peer->reinit(peer); /*重新初始化peer:调用ngx_http_upstream_check_http_reinit函数*/ } peer->shm->owner = NGX_INVALID_PID; @@ -2614,7 +2614,7 @@ ngx_http_upstream_check_finish_handler(ngx_event_t *event) static ngx_int_t ngx_http_upstream_check_need_exit() { - if (ngx_terminate || ngx_exiting || ngx_quit) { + if (ngx_terminate || ngx_exiting || ngx_quit) { /* 当nginx正在终止时,清空所有相关event */ ngx_http_upstream_check_clear_all_events(); return 1; } @@ -2624,7 +2624,7 @@ ngx_http_upstream_check_need_exit() static void -ngx_http_upstream_check_clear_all_events() +ngx_http_upstream_check_clear_all_events() /* 删除timer,关闭connection,销毁peer的内存pool */ { ngx_uint_t i; ngx_connection_t *c; @@ -3089,7 +3089,7 @@ ngx_http_upstream_check(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts;/*数组指针,指向:{{len = 5, data = 0x555555706795 "check"}, {len = 13, data = 0x55555570679b "interval=1000"}, {len = 6, data = 0x5555557067a9 "rise=1"}, {len = 6, data = 0x5555557067b0 "fall=3"}, {len = 12, data = 0x5555557067b7 "timeout=2000"}, {len = 9, data = 0x5555557067c4 "type=http"}}*/ - /* 获取已经注册的自定义configuration结构体, TODO: 那么,在哪里注册的configuration结构体呢?*/ + /* 获取已经注册的自定义configuration结构体, 那么,在哪里注册的configuration结构体呢?在ngx_http_upstream_check_module_ctx变量中*/ ucscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_check_module); if (ucscf == NULL) { @@ -3754,7 +3754,7 @@ ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf) shm_name, shm_size); shm_zone->data = cf->pool; - check_peers_ctx = ucmcf->peers; + check_peers_ctx = ucmcf->peers; /*获取后端服务器列表*/ shm_zone->init = ngx_http_upstream_check_init_shm_zone; } diff --git a/tutorial/README.adoc b/tutorial/README.adoc index 8536bdb..973d4cf 100644 --- a/tutorial/README.adoc +++ b/tutorial/README.adoc @@ -77,7 +77,15 @@ image::./imgs/nginx-src-structure.png[Nginx源码结构] === 3. 一个开源实例模块 开源地址:link:https://github.com/yaoweibin/nginx_upstream_check_module[nginx_upstream_check_module] -=== 4. 编译开源组件 +=== 4. 编译开源模块 + +在编译之前,需要参照开源模块的README文件,对nginx源码打补丁,否则主动探测功能会失败: +[source,shell] +---- +cd /path/to/nginx_source_code/nginx-1.26.0/ +patch -p1 < /path/to/nginx_http_upstream_check_module/check_1.20.1+.patch +---- + 为了便于编译,我们将编译命令和参数写成shell脚本(`configure-health-check.sh`): [source,shell] ---- @@ -158,6 +166,7 @@ image::./imgs/debug-nginx.png[调试nginx] === 3.1 源码解读 + ==== 3.1.1 配置与编译 ngx_http_upstream_check_module模块的 `config` 文件内容如下: @@ -234,72 +243,197 @@ fi 1. `ngx_feature="ngx_http_upstream_check_module"` 定义了正在配置的 Nginx 模块的名称。 - 2. `ngx_feature_name=` 通常用于定义一个宏名称,但这里留空,表示可能不需要定义特定的宏。 - 3. `ngx_feature_run=no` 表示不需要执行编译后的测试程序来验证特性。这可能是因为模块的存在与否不依赖于运行时检测。 - 4. `ngx_feature_incs=` 留空,表示没有特定的头文件需要包含。 - 5. `ngx_feature_libs=""` 留空,表示没有特定的库需要链接。 - 6. `ngx_feature_path="$ngx_addon_dir"` 设置模块的路径为 `ngx_addon_dir` 变量的值,这通常是模块的安装目录。 - 7. `ngx_feature_deps="$ngx_addon_dir/ngx_http_upstream_check_module.h"` 定义模块的依赖头文件,这里是模块的头文件。 - 8. `ngx_check_src="$ngx_addon_dir/ngx_http_upstream_check_module.c"` 指定模块的源文件路径。 - 9. `ngx_feature_test="int a;"` 提供一个非常简单的测试代码,这里仅仅声明了一个整型变量,实际上这个测试可能不会对模块的检测产生实际作用。 - 10. `. auto/feature` 执行 Nginx 的 `auto/feature` 脚本来处理配置。 - 11. `if [ $ngx_found = yes ]; then` 如果 `auto/feature` 脚本执行后 `ngx_found` 变量被设置为 `yes`,则继续执行以下命令。 - 12. `have=NGX_HTTP_UPSTREAM_CHECK . auto/have` 使用 `auto/have` 脚本定义一个宏,表示检测到 `ngx_http_upstream_check_module`。 - 13. `CORE_INCS="$CORE_INCS $ngx_feature_path"` 将模块的路径添加到 Nginx 核心的头文件搜索路径中。 - 14. `ngx_addon_name=ngx_http_upstream_check_module` 设置第三方模块的名称。 - 15. `HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_check_module"` 将模块名称添加到 HTTP 模块列表中。 - 16. `NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_feature_deps"` 将模块的依赖项添加到第三方模块的依赖列表中。 - 17. `NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_check_src"` 将模块的源文件添加到第三方模块的源文件列表中。 - 18. `else` 如果 `ngx_found` 不是 `yes`,执行错误处理。 - 19. `cat << END ... END` 输出错误信息,指出 `ngx_http_upstream_check_module` 模块的添加出现错误。 - 20. `exit 1` 退出脚本并返回状态码 1,表示配置过程中出现错误。 整体来看,这个 `config` 文件的作用是检测 `ngx_http_upstream_check_module` 是否可以被添加到 Nginx 中,并根据检测结果更新 Nginx 的配置,以便在编译时包含这个模块。如果检测失败,则输出错误信息并退出配置过程。 ---- -== 四. 开发流程 +==== 3.1.2 代码调试与跟踪 + +首先,使用gdb对编译后的nginx进行debugging +[source,shell] +---- +# 调式nginx +gdb /opt/nginx/sbin/nginx + +# 载入事先准备好的断点 +source gdb.cfg + +# 开启子进程调试 +set follow-fork-mode child +set set detach-on-fork on -== 五. 其他 +# 终端显示更加友好 +set print pretty + +# 开始运行nginx +run +---- + +【此处忽略阅读代码过程...】 + +==== 3.1.3 代码总结 + +===== 3.1.3.1 关键数据结构 + +结构体: `ngx_module_t` 结构体,名为 `ngx_http_upstream_check_module` 的变量 +[source,c] +---- +ngx_module_t ngx_http_upstream_check_module = { + NGX_MODULE_V1, + &ngx_http_upstream_check_module_ctx, /* module context */ + ngx_http_upstream_check_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_http_upstream_check_init_process, /* init process: 在master进程fork出workder进程的时候调用 */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; +---- +注意: **其中的回调函数 `ngx_http_upstream_check_init_process`是在Nginx master进程创建worker进程后开始执行的,是`ngx_http_uptstream_check_module`模块的入口** ,我们看看这个函数做了什么? + +===== 3.1.3.2 模块入口 + +[source,c] +---- +static ngx_int_t +ngx_http_upstream_check_init_process(ngx_cycle_t *cycle) +{ + ngx_http_upstream_check_main_conf_t *ucmcf; + + if (ngx_process != NGX_PROCESS_WORKER) { + return NGX_OK; + } + + ucmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_check_module); + if (ucmcf == NULL) { + return NGX_OK; + } + + return ngx_http_upstream_check_add_timers(cycle); +} +---- + +1. 首先判断当前进程是不是worker进程,如果不是worker进程,则直接返回; +2. 然后,获取当前module的、已经**事先装载的配置数据**(“事先”的意思是:在创建worker进程之前、在初始化和装载配置数据阶段,此处暂略,后面再讲); +3. 最后进入 `ngx_http_upstream_check_add_timers` 函数,这是核心函数,为当前模块注册timer:定时向后端服务器发送健康检测请求,并统计检测结果。关键代码如下: + [source,c] + ---- + static ngx_int_t ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle) + { + ... + for (i = 0; i < peers->peers.nelts; i++) { + peer[i].shm = &peer_shm[i]; + + peer[i].check_ev.handler = ngx_http_upstream_check_begin_handler; /* 注册check事件的回调函数 */ + ... + + peer[i].check_timeout_ev.handler = ngx_http_upstream_check_timeout_handler; /* 注册check事件的超时回调函数 */ + ... + cf = ucscf->check_type_conf; /* check_type_conf的赋值来自于全局变量ngx_check_types[] */ + ... + peer[i].send_handler = cf->send_handler; /* 发送check数据的回调函数 */ + peer[i].recv_handler = cf->recv_handler; /* 接收check响应数据的回调函数 */ + + ... + ngx_add_timer(&peer[i].check_ev, t); /* 为check事件添加定时器 */ + } + + return NGX_OK; + } + ---- +需要注意几点: + +- 首先,Nginx的高性能秘诀是其Event模型和Event Loop机制(略,读者可自行查阅资料),同样地,这个模块为主动探测也创建了两个event变量(`ngx_http_upstream_check_peer_s` 中的 `check_ev` 和 `check_timeout_ev` 变量),当前函数就为这两个事件各注册一个回调函数(handler); +- 除此以外,还有nginx内置的两个事件:`ngx_connection_t` 中的 `write` 和 `read` 事件,主要完成网络连接的IO操作,Nginx规定每个event都必须注册一个回调函数,当前函数也为 `write` 和 `read` 事件注册回调函数,回调函数的指派来自全局变量数组: `ngx_check_types` (细节藏在 `ngx_http_upstream_check_begin_handler` 函数中),我们暂时只关注通过http协议的主动探测; +[source,c] +---- +static ngx_check_conf_t ngx_check_types[] = { + ... + { NGX_HTTP_CHECK_HTTP, + ngx_string("http"), + ngx_string("GET / HTTP/1.0\r\n\r\n"), + NGX_CONF_BITMASK_SET | NGX_CHECK_HTTP_2XX | NGX_CHECK_HTTP_3XX, + ngx_http_upstream_check_send_handler, /* ngx_connection_t结构体中 write 事件的回调函数 */ + ngx_http_upstream_check_recv_handler, /* ngx_connection_t结构体中 send 事件的回调函数 */ + ... + 1, + 1 }, + ... +---- +- 最后,调用 `ngx_add_timer()` 为 `check_ev` 事件注册定时器,Nginx会定时调用 `ngx_http_upstream_check_begin_handler()` 函数,一旦发现超时,则会调用 `ngx_http_upstream_check_timeout_handler`。 + +===== 3.1.3.3 主动检测逻辑 + + 继续跟踪 `ngx_http_upstream_check_begin_handler()` 函数即可,注意其中一个点: + +1. 如何保证多个worker进程之间的互斥操作的? +2. 如何挑选后端服务器(peer)的? 在此文中,peer的选择主要由nginx内置负载均衡模块完成,例如, `ngx_http_upstream_round_robin` 模块或ip hash模块, `ngx_http_upstream_check` 模块只完成地后端服务器的主动探测和探测结果汇总。 通过给nginx源码打补丁, `ngx_http_upstream_check` 模块将 `ngx_http_upstream_check_add_peer()` 的调用插入到nginx原生round_robin模块中 `ngx_http_upstream_round_robin.c` 的初始化函数 `ngx_http_upstream_init_round_robin()` 中。 +3. 如何解析后端服务器的响应数据的? 跟踪 `ngx_http_upstream_check_recv_handler()` 函数。 + +===== 3.1.3.4 检测超时逻辑 + + 跟踪 `ngx_http_upstream_check_timeout_handler()` 函数即可 + +===== 3.1.3.5 配置初始化逻辑 + +一个数据结构:注册模块配置指令的数据接口 +[source,c] +---- +static ngx_command_t ngx_http_upstream_check_commands[] = { + + { ngx_string("check"), + NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, + ngx_http_upstream_check, /* 从配置文件中解析check指令的参数,即主动探测的配置*/ + 0, + 0, + NULL }, + ... +---- +接下来,跟踪 `ngx_http_upstream_check()` 函数即可。 -== 六. 参考资料 +== 四. 参考资料 1. 负载均衡解释:link:https://www.nginx.org.cn/article/detail/440[] 2. nginx中文配置手册:link:https://wizardforcel.gitbooks.io/nginx-doc/content/index.html[] 3. 高可用配置示例:link:https://blog.csdn.net/IT_10/article/details/89365436[]