注:当前分析基于 Nginx之搭建反向代理实现tomcat分布式集群 的配置。html
下面介绍在上面的配置中用到的指令。nginx
语法:upstream name { ... } 默认值:none 使用环境:http
该指令用于设置一组能够在 proxy_pass 和 fastcgi_pass 指令中使用的代理服务器,默认的负载均衡方式为轮询。示例以下:express
upstream backend { server backend1.example.com weight = 5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; }
语法:server name [parameters] 默认值:none 使用环境:upstream
该指令用于指定后端服务器的名称和参数。服务器的名称能够是一个域名、一个 IP 地址、端口号或 UNIX Socket。后端
在后端服务器名称以后,能够跟如下参数:数组
将指定请求传递到上游服务器,格式为 URL。缓存
typedef struct ngx_http_upstream_s ngx_http_upstream_t; struct ngx_http_upstream_s { /* * 处理读事件的回调方法,每个阶段都有不一样的 read_event_handler */ ngx_http_upstream_handler_pt read_event_handler; /* * 处理写事件的回调方法,每个阶段都有不一样的 write_event_handler */ ngx_http_upstream_handler_pt write_event_handler; /* * 表示主动向上游服务器发起的链接 */ ngx_peer_connection_t peer; /* * 当向下游客户端转发响应时(ngx_http_request_t 结构体中的 subrequest_in_memory * 标志位为 0),若是打开了缓存且认为上游网速更快(conf 配置中的 buffering 标志 * 位为 1),这时会使用 pipe 成员来转发响应。在使用这种方式转发响应时,必须由 * HTTP 模块在使用 upstream 机制前构造 pipe 结构体,不然会出现严重的 coredump * 错误. */ ngx_event_pipe_t *pipe; /* * request_bufs 以链表的方式把 ngx_buf_t 缓存区连接起来,它表示全部须要发送到 * 上游服务器的请求内容。因此,HTTP 模块实现的 create_request 回调方法就在于 * 构造 request_bufs 链表 */ ngx_chain_t *request_bufs; /* * 定义了向下游发送响应的方式 */ ngx_output_chain_ctx_t output; ngx_chain_writer_ctx_t writer; /* * 使用 upstream 机制时的各类配置 */ ngx_http_upstream_conf_t *conf; ngx_http_upstream_srv_conf_t *upstream; #if (NGX_HTTP_CACHE) ngx_array_t *caches; #endif /* * HTTP 模块在实现 process_header 方法时,若是但愿 upstream 直接转发响应, * 就须要把解析出的响应头部适配为 HTTP 的响应头部,同时须要把包头中的信息 * 设置到 headers_in 结构体,这样,会把 headers_in 中设置的头部添加到要发 * 送到下游客户端的响应头部 headers_out 中 */ ngx_http_upstream_headers_in_t headers_in; /* * 用于解析主机域名 */ ngx_http_upstream_resolved_t *resolved; ngx_buf_t from_client; /* * 接收上游服务器响应包头的缓冲区,在不须要把响应直接转发给客户端, * 或者 buffering 标志位为 0 的状况下转发包体时,接收包体的缓冲区 * 仍然使用 buffer。注意,若是没有自定义 input_filter 方法处理包体, * 将会使用 buffer 存储所有的包体,这时 buffer 必须足够大,它的大小 * 由 ngx_http_upstream_conf_t 配置结构体中的 buffer_size 成员决定 */ ngx_buf_t buffer; /* * 表示来自上游服务器的响应包体的长度 */ off_t length; /* * out_bufs 在两种场景下有不一样的意义:1. 当不须要转发包体,且使用默认 * 的 input_filter 方法(也就是 ngx_http_upstream_non_buffered_filter * 方法)处理包体时,out_bufs 将会指向响应包体,事实上,out_bufs 链表 * 中会产生多个 ngx_buf_t 缓冲区,每一个缓冲区都指向 buffer 缓存中的一部 * 分,而这里的一部分就是每次调用 recv 方法接收到的一段 TCP 流。2. 当 * 须要转发响应包体到下游时(buffering 标志位为 0,即如下游网速优先), * 这个链表指向上一次向下游转发响应到如今这段时间内接收自上游的缓存响应 */ ngx_chain_t *out_bufs; /* * 当须要转发响应包体到下游时(buffering 标志位为 0,即如下游网速优先), * 它表示上一次向下游转发响应时没有发送完的内容 */ ngx_chain_t *busy_bufs; /* * 这个链表将用于回收 out_bufs 中已经发送给下游的 ngx_buf_t 结构体,这 * 一样应用在 buffering 标志位为 0 即如下游网速优先的场景 */ ngx_chain_t *free_bufs; /* * 处理包体前的初始化方法,其中 data 参数用于传递用户数据结构,它实际上 * 就是下面的 input_filter_ctx 指针 */ ngx_int_t (*input_filter_init)(void *data); /* * 处理包体的方法,其中 data 参数用于传递用户数据结构,它实际上就是下面的 * input_filter_ctx 指针,而 bytes 表示本次接收到的包体长度。返回 NGX_ERROR * 时表示处理包体错误,请求须要结束,不然都将继续 upstream 流程 */ ngx_int_t (*input_filter)(void *data, ssize_t bytes); /* * 用于传递 HTTP 模块自定义的数据结构,在 input_filter_init 和 input_filter * 方法被回调时会做为参数传递过去 */ void *input_filter_ctx; #if (NGX_HTTP_CACHE) ngx_int_t (*create_key)(ngx_http_request_t *r); #endif /* * HTTP 模块实现的 create_request 方法用于构造发往上游服务器的请求 */ ngx_int_t (*create_request)(ngx_http_request_t *r); /* * 与上游服务器的通讯失败后,若是按照重试规则还须要再次向上游服务器发起 * 链接,则会调用 reinit_request 方法 */ ngx_int_t (*reinit_request)(ngx_http_request_t *r); /* * 解析上游服务器返回响应的包头,返回 NGX_AGAIN 表示包头尚未接收完整, * 返回 NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包头不合法,返回 NGX_ERROR * 表示出现错误,返回 NGX_OK 表示解析到完整的包头. */ ngx_int_t (*process_header)(ngx_http_request_t *r); void (*abort_request)(ngx_http_request_t *r); /* * 请求结束时会调用 */ void (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc); /* * 在上游返回的响应出现 Location 或者 Refresh 头部时表示重定向时,会经过 * ngx_http_upstream_process_headers 方法调用到可由 HTTP 模块实现的 * rewrite_redirect 方法 */ ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r, ngx_table_elt_t *h); ngx_msec_t timeout; /* * 用于表示上游响应的错误码、包体长度等信息 */ ngx_http_upstream_state_t *state; ngx_str_t method; /* * schema 和 uri 成员仅在记录日志时会用到,除此以外没有意义 */ ngx_str_t schema; ngx_str_t uri; #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_str_t ssl_name; #endif ngx_http_cleanup_pt *cleanup; /* * 是否指定文件缓存路径的标志位 */ unsigned store:1; /* * 是否启用文件缓存 */ unsigned cacheable:1; unsigned accel:1; /* * 是否基于 SSL 协议访问上游服务器 */ unsigned ssl:1; #if (NGX_HTTP_CACHE) unsigned cache_status:3; #endif /* * 向下游转发上游的响应包体时,是否开启更大的内存及临时磁盘文件用于 * 缓存来不及发送到下游的响应包体. */ unsigned buffering:1; unsigned keepalive:1; unsigned upgrade:1; /* * request_sent 表示是否已经向上游服务器发送了请求,当 request_sent 为 * 1 时,表示 upstream 机制已经向上游服务器发送了所有或者部分的请求。 * 事实上,这个标志位更多的是为了使用 ngx_output_chain 方法发送请求, * 由于该方法发送请求时会自动把未发送完的 request_bufs 链表记录下来, * 为了防止反复发送重复请求,必须有 request_sent 标志位记录是否调用过 * ngx_output_chain 方法 */ unsigned request_sent:1; unsigned request_body_sent:1; /* * 将上游服务器的响应划分为包头和包尾,若是把响应直接转发给客户端, * header_sent 标志位表示包头是否发送,header_sent 为 1 时表示已经 * 把包头转发给客户端了。若是不转发响应到客户端,则 header_sent * 没有意义. */ unsigned header_sent:1; };
typedef struct { /* * 当在 ngx_http_upstream_t 结构体中没有实现 resolved 成员时,upstream 这个 * 结构体才会生效,它会定义上游服务器的配置 */ ngx_http_upstream_srv_conf_t *upstream; /* * 创建 TCP 链接的超时时间,实际上就是写事件添加到定时器中设置的超时时间 */ ngx_msec_t connect_timeout; /* * 发送请求的超时时间。一般就是写事件添加到定时器中设置的超时时间 */ ngx_msec_t send_timeout; /* * 接收响应的超时时间。一般就是读事件添加到定时器中设置的超时时间 */ ngx_msec_t read_timeout; ngx_msec_t next_upstream_timeout; /* * TCP 的 SO_SNOLOWAT 选项,表示发送缓冲区的下限 */ size_t send_lowat; /* * 定义了接收头部的缓冲区分配的内存大小(ngx_http_upstream_t 中的 buffer * 缓冲区),当不转发响应给下游或者在 buffering 标志位为 0 的状况下转发 * 响应时,它一样表示接收包体的缓冲区大小 */ size_t buffer_size; size_t limit_rate; /* * 仅当 buffering 标志位为 1,而且向下游转发响应时生效。它会设置到 * ngx_event_pipe_t 结构体的 busy_size 成员中 */ size_t busy_buffers_size; /* * 在 buffering 标志位为 1 时,若是上游速度快于下游速度,将有可能把来自上游的 * 响应存储到临时文件中,而 max_temp_file_size 指定了临时文件的最大长度。实际 * 上,它将限制 ngx_event_pipe_t 结构体中的 temp_file */ size_t max_temp_file_size; /* * 表示将缓冲区中的响应写入临时文件时一次写入字符流的最大长度 */ size_t temp_file_write_size; size_t busy_buffers_size_conf; size_t max_temp_file_size_conf; size_t temp_file_write_size_conf; /* * 以缓存响应的方式转发上游服务器的包体时所使用的内存大小 */ ngx_bufs_t bufs; /* * 针对 ngx_http_upstream_t 结构体中保存解析完的包头的 headers_in 成员, * ignore_headers 能够按照二进制位使得 upstream 在转发包头时跳过对某些 * 头部的处理。做为 32 位整型,理论上 ignore_headers 最多能够表示 32 个 * 须要跳过不予处理的头部 */ ngx_uint_t ignore_headers; /* * 以二进制位来表示一些错误码,若是处理上游响应时发现这些错误码,那么在 * 没有将响应转发给下游客户端时,将会选择下一个上游服务器来重发请求 */ ngx_uint_t next_upstream; /* * 在 buffering 标志位为 1 的状况下转发响应时,将有可能把响应存放到临时文件 * 中。在 ngx_http_upstream_t 中的 store 标志位为 1 时,store_access 表示 * 所建立的目录、文件的权限. */ ngx_uint_t store_access; ngx_uint_t next_upstream_tries; /* * 决定转发响应方式的标志位,buffering 为 1 时表示打开缓存,这时认为上游 * 的网速快于下游的网速,会尽可能地在内存或者磁盘中缓存来自上游的响应;若是 * buffering 为 0,仅会开辟一块固定大小的内存块做为缓存来转发响应 */ ngx_flag_t buffering; ngx_flag_t request_buffering; ngx_flag_t pass_request_headers; ngx_flag_t pass_request_body; /* * 标志位,为 1 时表示与上游服务器交互时将不检查 Nginx 与下游客户端间的链接 * 是否断开。也就是说,即便下游客户端主动关闭了链接,也不会中断与上游服务器 * 间的交互. */ ngx_flag_t ignore_client_abort; /* * 当解析上游响应的包头时,若是解析后设置到 headers_in 结构体中的 status_n * 错误码大于 400,则会试图把它与 error_page 中指定的错误码相匹配,若是匹配 * 上,则发送 error_page 中指定的响应,不然继续返回上游服务器的错误码. */ ngx_flag_t intercept_errors; /* * buffering 标志位为 1 的状况下转发响应时才有意义。这时,若是 cyclic_temp_file * 为 1,则会试图复用临时文件中已经使用过的空间。不建议将 cyclic_temp_file * 设为 1. */ ngx_flag_t cyclic_temp_file; ngx_flag_t force_ranges; /* * 在 buffering 标志位为 1 的状况下转发响应时,存放临时文件的路径 */ ngx_path_t *temp_path; /* * 不转发的头部。其实是经过 ngx_http_upstream_hide_hash 方法, * 根据 hide_headers 和 pass_headers 动态数组构造出的须要隐藏的 * HTTP 头部散列表 */ ngx_hash_t hide_headers_hash; /* * 当转发上游响应头部(ngx_http_upstream_t 中 headers_in 结构体中的头部) * 给下游客户端时,若是不但愿某些头部转发给下游,就设置到 hide_headers * 动态数组中 */ ngx_array_t *hide_headers; /* * 当转发上游响应头部(ngx_http_upstream_t 中的 headers_in 结构体中的头部) * 给下游客户端时,upstream 机制默认不会转发如 "Data"、"Server" 之类的头部, * 若是确实但愿直接转发它们到下游,就设置到 pass_headers 动态数组中 */ ngx_array_t *pass_headers; /* * 链接上游服务器时使用的本机地址 */ ngx_http_upstream_local_t *local; #if (NGX_HTTP_CACHE) ngx_shm_zone_t *cache_zone; ngx_http_complex_value_t *cache_value; ngx_uint_t cache_min_uses; ngx_uint_t cache_use_stale; ngx_uint_t cache_methods; off_t cache_max_range_offset; ngx_flag_t cache_lock; ngx_msec_t cache_lock_timeout; ngx_msec_t cache_lock_age; ngx_flag_t cache_revalidate; ngx_flag_t cache_convert_head; ngx_flag_t cache_background_update; ngx_array_t *cache_valid; ngx_array_t *cache_bypass; ngx_array_t *cache_purge; ngx_array_t *no_cache; #endif /* * 当 ngx_http_upstream_t 中的 store 标志位为 1 时,若是须要将上游的响应 * 存放到文件中,store_lengths 将表示存放路径的长度,而 store_values * 表示存放路径 */ ngx_array_t *store_lengths; ngx_array_t *store_values; #if (NGX_HTTP_CACHE) signed cache:2; #endif signed store:2; /* * 上面的 intercept_errors 标志位定义了 400 以上的错误码将会与 error_page * 比较后再行处理,实际上这个规则是能够有一个例外状况,若是将 intercept_404 * 标志位设为 1,当上游返回 404 时会直接转发这个错误码给下游,而不会去与 * errpr_page 进行比较. */ unsigned intercept_404:1; /* * 为 1 时,将会根据 ngx_http_upstream_t 中 headers_in 结构体里的 X-Accel-Buffering * 头部(它的值会是 yes 和 no)来改变 buffering 标志位,当其值为 yes 时,buffering * 标志位为 1。所以,change_buffering 为 1 时将有可能根据上游服务器返回的响应头部, * 动态地决定是以上游网速优先仍是如下游网速优先 */ unsigned change_buffering:1; #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; ngx_flag_t ssl_session_reuse; ngx_http_complex_value_t *ssl_name; ngx_flag_t ssl_server_name; ngx_flag_t ssl_verify; #endif ngx_str_t module; NGX_COMPAT_BEGIN(2) NGX_COMPAT_END } ngx_http_upstream_conf_t;
static ngx_command_t ngx_http_upstream_commands[] = { { ngx_string("upstream"), NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, ngx_http_upstream, 0, 0, NULL }, { ngx_string("server"), NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, ngx_http_upstream_server, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL }, ngx_null_command };
假设 upstream{} 的配置以下:tomcat
upstream rong { server 192.168.56.101:8080; server 192.168.56.101:8081; }
则调用该函数进行解析:服务器
static char * ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; void *mconf; ngx_str_t *value; ngx_url_t u; ngx_uint_t m; ngx_conf_t pcf; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *http_ctx; ngx_http_upstream_srv_conf_t *uscf; ngx_memzero(&u, sizeof(ngx_url_t)); value = cf->args->elts; u.host = value[1]; u.no_resolve = 1; u.no_port = 1; /* 先从 upstreams 数组中检测是否已有相同的项存在,若没有则新建立一个 * ngx_http_upstream_srv_conf_t,该结构体表明一个上游服务器, * 而后将其添加到 upstreams 数组中 */ uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN |NGX_HTTP_UPSTREAM_BACKUP); if (uscf == NULL) { return NGX_CONF_ERROR; } /* 对于 http{} 中的每个 block,都要构建属于该 block 的 ngx_http_conf_ctx_t, * 该结构体中包含三个指针数组成员:main_conf、srv_conf、loc_conf,存放着该 * block 解析到的全部配置 */ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } http_ctx = cf->ctx; /* 当前 block 的 main_conf 指向上一级的 block 的 main_conf,对于 upstream, * 上一级的 block 即为 http{} */ ctx->main_conf = http_ctx->main_conf; /* 下面是分配建立属于该 upstream{} 的 srv_conf 和 loc_conf */ /* the upstream{}'s srv_conf */ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->srv_conf == NULL) { return NGX_CONF_ERROR; } /* 将上面建立的 ngx_http_upstream_srv_conf_t 的首地址存放到当前 upstream 模块 * 在 srv_conf 数组中的索引处 * 注:当前 ngx_http_upstream_module 模块没有实现 create_srv_conf 函数,所以 * 下面调用的 create_srv_conf 不会致使这里的值被覆盖了 */ ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf; uscf->srv_conf = ctx->srv_conf; /* the upstream{}'s loc_conf */ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->loc_conf == NULL) { return NGX_CONF_ERROR; } for (m = 0; cf->cycle->modules[m]; m++) { if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) { continue; } module = cf->cycle->modules[m]->ctx; if (module->create_srv_conf) { mconf = module->create_srv_conf(cf); if (mconf == NULL) { return NGX_CONF_ERROR; } ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; } if (module->create_loc_conf) { mconf = module->create_loc_conf(cf); if (mconf == NULL) { return NGX_CONF_ERROR; } ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf; } } /* 建立 servers 数组,该数组中每个元素都为 ngx_http_upstream_server_t * 类型的结构体,该结构体表明 upstream{} 中的 server 指令 */ uscf->servers = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_server_t)); if (uscf->servers == NULL) { return NGX_CONF_ERROR; } /* parse inside upstream{} */ pcf = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_UPS_CONF; /* 这里开始解析 upstream{} */ rv = ngx_conf_parse(cf, NULL); *cf = pcf; if (rv != NGX_CONF_OK) { return rv; } /* 若 upstream{} 中没有配置 server 指令,则代表发生错误了 */ if (uscf->servers->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no servers are inside upstream"); return NGX_CONF_ERROR; } return rv; }
ngx_http_upstream_srv_conf_t * ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) { ngx_uint_t i; ngx_http_upstream_server_t *us; ngx_http_upstream_srv_conf_t *uscf, **uscfp; ngx_http_upstream_main_conf_t *umcf; if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) { if (ngx_parse_url(cf->pool, u) != NGX_OK) { if (u->err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in upstream \"%V\"", u->err, &u->url); } return NULL; } } /* 获取 ngx_http_upstream_module 模块在 main 级别的配置结构体 */ umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module); /* upstreams 是一个数组,每个数组元素的类型为 ngx_http_upstream_srv_conf_t */ uscfp = umcf->upstreams.elts; /* 遍历该数组,检测是否已有相同的存在,如果,则返回该已添加到 upstreams 数组中的 * ngx_http_upstream_srv_conf_t 结构体 */ for (i = 0; i < umcf->upstreams.nelts; i++) { if (uscfp[i]->host.len != u->host.len || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len) != 0) { continue; } if ((flags & NGX_HTTP_UPSTREAM_CREATE) && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate upstream \"%V\"", &u->host); return NULL; } if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "upstream \"%V\" may not have port %d", &u->host, u->port); return NULL; } if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "upstream \"%V\" may not have port %d in %s:%ui", &u->host, uscfp[i]->port, uscfp[i]->file_name, uscfp[i]->line); return NULL; } if (uscfp[i]->port && u->port && uscfp[i]->port != u->port) { continue; } if (flags & NGX_HTTP_UPSTREAM_CREATE) { uscfp[i]->flags = flags; uscfp[i]->port = 0; } return uscfp[i]; } /* 若upstreams数组中没有,则新建立一个 ngx_http_upstream_srv_conf_t */ uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t)); if (uscf == NULL) { return NULL; } uscf->flags = flags; /* 该代理服务器的对应的域名,对于上面的示例则为 rong */ uscf->host = u->host; /* 配置文件的绝对路径 */ uscf->file_name = cf->conf_file->file.name.data; /* 记录 upstream {} 在配置文件中的行号 */ uscf->line = cf->conf_file->line; uscf->port = u->port; uscf->no_port = u->no_port; if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { uscf->servers = ngx_array_create(cf->pool, 1, sizeof(ngx_http_upstream_server_t)); if (uscf->servers == NULL) { return NULL; } us = ngx_array_push(uscf->servers); if (us == NULL) { return NULL; } ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); us->addrs = u->addrs; us->naddrs = 1; } /* 将该新构建的 ngx_http_upstream_srv_conf_t 添加到 upstreams 数组中 * 每个 ngx_http_upstream_srv_conf_t 表明一个代理服务器 */ uscfp = ngx_array_push(&umcf->upstreams); if (uscfp == NULL) { return NULL; } *uscfp = uscf; return uscf; }
server 指令就是为 upstream 定义一个服务器地址(带有端口号的域名、IP 地址,或者是 UNIX 套接字)和一个可选的参数。参数以下:cookie
static char * ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_upstream_srv_conf_t *uscf = conf; time_t fail_timeout; ngx_str_t *value, s; ngx_url_t u; ngx_int_t weight, max_conns, max_fails; ngx_uint_t i; ngx_http_upstream_server_t *us; /* 从 servers 数组中取出一个已经分配好的 ngx_http_upstream_server_t * 该数组中每个 ngx_http_upstream_server_t 结构体表明一个 server */ us = ngx_array_push(uscf->servers); if (us == NULL) { return NGX_CONF_ERROR; } ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); value = cf->args->elts; /* 默认权重为 1,值越大表示该服务器的优先级越高 */ weight = 1; max_conns = 0; max_fails = 1; fail_timeout = 10; /* 若该 server 指令有第 3 个以上的参数,则进行解析 */ for (i = 2; i < cf->args->nelts; i++) { if (ngx_strncmp(value[i].data, "weight=", 7) == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) { goto not_supported; } weight = ngx_atoi(&value[i].data[7], value[i].len - 7); if (weight == NGX_ERROR || weight == 0) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) { goto not_supported; } max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10); if (max_conns == NGX_ERROR) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) { goto not_supported; } max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10); if (max_fails == NGX_ERROR) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) { goto not_supported; } s.len = value[i].len - 13; s.data = &value[i].data[13]; fail_timeout = ngx_parse_time(&s, 1); if (fail_timeout == (time_t) NGX_ERROR) { goto invalid; } continue; } if (ngx_strcmp(value[i].data, "backup") == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) { goto not_supported; } us->backup = 1; continue; } if (ngx_strcmp(value[i].data, "down") == 0) { if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) { goto not_supported; } us->down = 1; continue; } goto invalid; } ngx_memzero(&u, sizeof(ngx_url_t)); /* 该服务器的 url */ u.url = value[1]; /* 若没有指定端口,则该服务器的默认端口为 80 */ u.default_port = 80; /* 解析该 url */ if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in upstream \"%V\"", u.err, &u.url); } return NGX_CONF_ERROR; } us->name = u.url; us->addrs = u.addrs; us->naddrs = u.naddrs; us->weight = weight; us->max_conns = max_conns; us->max_fails = max_fails; us->fail_timeout = fail_timeout; return NGX_CONF_OK; invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; not_supported: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "balancing method does not support parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; }
ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) { u_char *p; size_t len; p = u->url.data; len = u->url.len; /* unix */ if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { return ngx_parse_unix_domain_url(pool, u); } /* IPv6 */ if (len && p[0] == '[') { return ngx_parse_inet6_url(pool, u); } /* IPv4 */ return ngx_parse_inet_url(pool, u); }
假设当前 server 指定的 url 为: "192.168.56.101:8080"网络
static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) { u_char *p, *host, *port, *last, *uri, *args; size_t len; ngx_int_t n; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif u->socklen = sizeof(struct sockaddr_in); sin = (struct sockaddr_in *) &u->sockaddr; sin->sin_family = AF_INET; u->family = AF_INET; host = u->url.data; last = host + u->url.len; port = ngx_strlchr(host, last, ':'); uri = ngx_strlchr(host, last, '/'); args = ngx_strlchr(host, last, '?'); if (args) { if (uri == NULL || args < uri) { uri = args; } } if (uri) { if (u->listen || !u->uri_part) { u->err = "invalid host"; return NGX_ERROR; } u->uri.len = last - uri; u->uri.data = uri; last = uri; if (uri < port) { port = NULL; } } /* 存在端口号 */ if (port) { port++; len = last - port; /* 将端口号转化为整型值 */ n = ngx_atoi(port, len); if (n < 1 || n > 65535) { u->err = "invalid port"; return NGX_ERROR; } u->port = (in_port_t) n; /* 将主机字节序的端口号转化为网络字节序 */ sin->sin_port = htons((in_port_t) n); /* 存放字符串形式的端口号 */ u->port_text.len = len; u->port_text.data = port; last = port - 1; } else { if (uri == NULL) { if (u->listen) { /* test value as port only */ n = ngx_atoi(host, last - host); if (n != NGX_ERROR) { if (n < 1 || n > 65535) { u->err = "invalid port"; return NGX_ERROR; } u->port = (in_port_t) n; sin->sin_port = htons((in_port_t) n); u->port_text.len = last - host; u->port_text.data = host; u->wildcard = 1; return NGX_OK; } } } u->no_port = 1; u->port = u->default_port; sin->sin_port = htons(u->default_port); } len = last - host; if (len == 0) { u->err = "no host"; return NGX_ERROR; } u->host.len = len; u->host.data = host; if (u->listen && len == 1 && *host == '*') { sin->sin_addr.s_addr = INADDR_ANY; u->wildcard = 1; return NGX_OK; } sin->sin_addr.s_addr = ngx_inet_addr(host, len); if (sin->sin_addr.s_addr != INADDR_NONE) { if (sin->sin_addr.s_addr == INADDR_ANY) { u->wildcard = 1; } u->naddrs = 1; u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NGX_ERROR; } ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in)); u->addrs[0].sockaddr = (struct sockaddr *) sin; u->addrs[0].socklen = sizeof(struct sockaddr_in); p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); if (p == NULL) { return NGX_ERROR; } u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", &u->host, u->port) - p; u->addrs[0].name.data = p; return NGX_OK; } if (u->no_resolve) { return NGX_OK; } if (ngx_inet_resolve_host(pool, u) != NGX_OK) { return NGX_ERROR; } u->family = u->addrs[0].sockaddr->sa_family; u->socklen = u->addrs[0].socklen; ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen); switch (u->family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) &u->sockaddr; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { u->wildcard = 1; } break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) &u->sockaddr; if (sin->sin_addr.s_addr == INADDR_ANY) { u->wildcard = 1; } break; } return NGX_OK; }
该指令指定请求被传递到上游服务器,格式为 ULR。
static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_pass"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, ngx_http_proxy_pass, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ... };
当在配置文件中检测到有 proxy_pass 指令时,会调用 ngx_http_proxy_pass 函数进行解析:
static char * ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_proxy_loc_conf_t *plcf = conf; size_t add; u_short port; ngx_str_t *value, *url; ngx_url_t u; ngx_uint_t n; ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is duplicate"; } /* 获取 ngx_http_core_module 模块 loc 级别的配置结构体 */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); /* 设置 handler 的回调函数 */ clcf->handler = ngx_http_proxy_handler; /* 该 Location 的名称 */ if (clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } /* value = "proxy_pass" */ value = cf->args->elts; /* proxy_pass 的值,即要将请求传递给上游服务器的 url */ url = &value[1]; /* 检测是否有脚本变量,返回脚本变量的个数 */ n = ngx_http_script_variables_count(url); if (n) { ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); sc.cf = cf; sc.source = url; sc.lengths = &plcf->proxy_lengths; sc.values = &plcf->proxy_values; sc.variables = n; sc.complete_lengths = 1; sc.complete_values = 1; if (ngx_http_script_compile(&sc) != NGX_OK) { return NGX_CONF_ERROR; } #if (NGX_HTTP_SSL) plcf->ssl = 1; #endif return NGX_CONF_OK; } if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) { add = 7; port = 80; } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) { #if (NGX_HTTP_SSL) plcf->ssl = 1; add = 8; port = 443; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "https protocol requires SSL support"); return NGX_CONF_ERROR; #endif } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix"); return NGX_CONF_ERROR; } ngx_memzero(&u, sizeof(ngx_url_t)); /* 去掉 "http://" 或 "https://" 后的长度 */ u.url.len = url->len - add; u.url.data = url->data + add; u.default_port = port; u.uri_part = 1; u.no_resolve = 1; /* 根据该 proxy_pass 指定的 url 名称,从 ngx_http_upstream_main_conf_t * 结构体的 upstreams 数组中找到该 url 对应的 upstream */ plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); if (plcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } plcf->vars.schema.len = add; plcf->vars.schema.data = url->data; plcf->vars.key_start = plcf->vars.schema; ngx_http_proxy_set_vars(&u, &plcf->vars); /* 当前 location{} 的名称 */ plcf->location = clcf->name; if (clcf->named #if (NGX_PCRE) || clcf->regex #endif || clcf->noname) { if (plcf->vars.uri.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_pass\" cannot have URI part in " "location given by regular expression, " "or inside named location, " "or inside \"if\" statement, " "or inside \"limit_except\" block"); return NGX_CONF_ERROR; } plcf->location.len = 0; } plcf->url = *url; return NGX_CONF_OK; }
static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v) { if (u->family != AF_UNIX) { if (u->no_port || u->port == u->default_port) { v->host_header = u->host; if (u->default_port == 80) { ngx_str_set(&v->port, "80"); } else { ngx_str_set(&v->port, "443"); } } else { v->host_header.len = u->host.len + 1 + u->port_text.len; v->host_header.data = u->host.data; v->port = u->port_text; } v->key_start.len += v->host_header.len; } else { ngx_str_set(&v->host_header, "localhost"); ngx_str_null(&v->port); v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1; } v->uri = u->uri; }
对于 http 链接事件,当监听的客户端链接请求并接受链接后,第一个调用的函数都为该 ngx_http_init_connection 函数。该函数构建了该服务器与客户端之间的链接 ngx_connection_t 结构体,并将读事件添加到定时器和 epoll 事件监控机制中。当监听到客户端发送的数据到达时,即会调用回调函数 ngx_http_wait_request_handler 进行处理。
void ngx_http_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_event_t *rev; struct sockaddr_in *sin; ngx_http_port_t *port; ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_http_in6_addr_t *addr6; #endif /* 为当前的 HTTP 链接建立一个 ngx_http_connection_t 结构体,该结构体 * 表明当前的 HTTP 链接 */ hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { ngx_http_close_connection(c); return; } /* 将 data 指针指向表示当前 HTTP 链接的 ngx_http_connection_t */ c->data = hc; /* find the server configuration for the address:port */ /* listening:这个链接对应的 ngx_listening_t 监听对象,此链接由 listening * 监听端口的事件创建. * servers: 对于 HTTP 模块,该指针指向 ngx_http_port_t 结构体,该结构体 * 实际保存着当前监听端口的地址信息. */ port = c->listening->servers; /* 若该端口对应主机上的多个地址 */ if (port->naddrs > 1) { /* * there are several addresses on this port and one of them * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() * is required to determine a server address */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_http_close_connection(c); return; } switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } hc->addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } hc->addr_conf = &addr[i].conf; break; } } else { /* 本机的监听端口对应的 sockaddr 结构体 */ switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; hc->addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; hc->addr_conf = &addr[0].conf; break; } } /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); return; } ctx->connection = c; ctx->request = NULL; ctx->current_request = NULL; c->log->connection = c->number; c->log->handler = ngx_http_log_error; c->log->data = ctx; c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; /* 链接对应的读事件 */ rev = c->read; /* 为该链接的读事件设置回调处理函数 */ rev->handler = ngx_http_wait_request_handler; /* 为该链接的写事件设置回调处理函数,该函数为一个空函数,什么也不作 */ c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; } #endif #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); if (sscf->enable || hc->addr_conf->ssl) { c->log->action = "SSL handshaking"; if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_http_close_connection(c); return; } hc->ssl = 1; rev->handler = ngx_http_ssl_handshake; } } #endif if (hc->addr_conf->proxy_protocol) { hc->proxy_protocol = 1; c->log->action = "reading PROXY protocol"; } /* * 标志位,为1时表示当前事件已经准备就绪,也就是说,容许这个事件的消费者模块 * 处理这个事件。在HTTP框架中,常常会检查事件的ready标志位以肯定是否能够接收 * 请求或者发送响应 */ if (rev->ready) { /* the deferred accept(), iocp */ /* 为 1,表示开启了负载均衡机制,此时不会马上执行该读事件,而是将当前的 * 读事件添加到 ngx_posted_events 延迟执行队列中 */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } /* 若没有开启负载均衡机制,则直接处理该读事件 */ rev->handler(rev); return; } /* 将读事件添加到定时器中,超时时间为 post_accept_timeout 毫秒 * post_accept_timeout 在配置文件中没有配置的话,默认为 60000 * 毫秒 */ ngx_add_timer(rev, c->listening->post_accept_timeout); /* 将该链接添加到可重用双向链表的头部 */ ngx_reusable_connection(c, 1); /* 将该读事件添加到事件驱动模块中,这样当该事件对应的 TCP 链接上 * 一旦出现可读事件(如接收到 TCP 链接的另外一端发送来的字节流)就会 * 回调该事件的 handler 方法 */ if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } }
当监听到服务器与客户端之间的套接字可读,即客户端发送数据给服务器时,即会调用该 ngx_http_wait_request_handler 函数进行处理。
static void ngx_http_wait_request_handler(ngx_event_t *rev) { u_char *p; size_t size; ssize_t n; ngx_buf_t *b; ngx_connection_t *c; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; /* 事件相关的对象。一般 data 都是指向 ngx_connection_t 链接对象。 * 开启文件异步 I/O 时,它可能会指向 ngx_event_aio_t 结构体 */ c = rev->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); /* 检查该读事件是否已经超时,若超时,则关闭该链接 */ if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); return; } /* 标志位,为 1 时表示链接关闭 */ if (c->close) { ngx_http_close_connection(c); return; } /* 由 ngx_http_init_connection 函数知,此时该 data 指针指向 * ngx_http_connection_t 结构体 */ hc = c->data; /* 获取该 server{} 对应的配置项结构体 */ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); size = cscf->client_header_buffer_size; /* 用于接收、缓存客户端发来的字节流,每一个事件消费模块可自由决定从链接池中 * 分配多大的空间给 buffer 这个接收缓存字段。例如,在 HTTP 模块中,它的大小 * 决定于 client_header_buffer_size 配置项 */ b = c->buffer; /* 若没有为当前链接的接收/发送缓存分配内存 */ if (b == NULL) { /* 分配一个 size 大小的临时缓存(表示该缓存中的数据在内存中且 * 该缓存中的数据能够被修改) */ b = ngx_create_temp_buf(c->pool, size); if (b == NULL) { ngx_http_close_connection(c); return; } c->buffer = b; } else if (b->start == NULL) { b->start = ngx_palloc(c->pool, size); if (b->start == NULL) { ngx_http_close_connection(c); return; } b->pos = b->start; b->last = b->start; b->end = b->last + size; } /* 调用接收字节流的回调函数 ngx_unix_recv 接收客户端发送的数据 */ n = c->recv(c, b->last, size); if (n == NGX_AGAIN) { if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); ngx_reusable_connection(c, 1); } if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } /* * We are trying to not hold c->buffer's memory for an idle connection. */ if (ngx_pfree(c->pool, b->start) == NGX_OK) { b->start = NULL; } return; } if (n == NGX_ERROR) { ngx_http_close_connection(c); return; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection"); ngx_http_close_connection(c); return; } /* last 指向缓存中有效数据的末尾 */ b->last += n; if (hc->proxy_protocol) { hc->proxy_protocol = 0; p = ngx_proxy_protocol_read(c, b->pos, b->last); if (p == NULL) { ngx_http_close_connection(c); return; } b->pos = p; if (b->pos == b->last) { c->log->action = "waiting for request"; b->pos = b->start; b->last = b->start; ngx_post_event(rev, &ngx_posted_events); return; } } c->log->action = "reading client request line"; /* 将该链接从 reusable_connections_queue 可重用双向链表中删除 */ ngx_reusable_connection(c, 0); /* 为当前客户端链接建立并初始化一个 ngx_http_request_t 结构体 * 并将 c->data 指向该结构体 */ c->data = ngx_http_create_request(c); if (c->data == NULL) { ngx_http_close_connection(c); return; } /* 设置该读事件的回调处理函数 */ rev->handler = ngx_http_process_request_line; /* 开始解析该客户端请求的请求行 */ ngx_http_process_request_line(rev); }
该函数主要是接收该客户端发送的数据,而后调用 ngx_http_create_request 函数为该客户端的请求建立一个 ngx_http_request_t 结构体,用于专门处理这次客户端的请求。
ngx_http_request_t * ngx_http_create_request(ngx_connection_t *c) { ngx_pool_t *pool; ngx_time_t *tp; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; /* 处理请求的次数加 1 */ c->requests++; /* 在该函数返回前,data 仍是指向 ngx_http_connection_t 结构体 */ hc = c->data; /* 获取当前 server{} 下的配置结构体 */ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); /* 为该客户端请求分配一个内存池 */ pool = ngx_create_pool(cscf->request_pool_size, c->log); if (pool == NULL) { return NULL; } /* 从内存池 pool 中为 ngx_http_request_t 结构体分配内存 */ r = ngx_pcalloc(pool, sizeof(ngx_http_request_t)); if (r == NULL) { ngx_destroy_pool(pool); return NULL; } /* 该请求的内存池,在 ngx_http_free_request 方法中销毁。 * 它与 ngx_connection_t 中的内存池意义不一样,当请求释放时,TCP 链接 * 可能并无关闭,这时请求的内存池会销毁,但 ngx_connection_t 的 * 内存池并不会销毁. */ r->pool = pool; /* 表明当前 HTTP 链接 */ r->http_connection = hc; r->signature = NGX_HTTP_MODULE; /* 指向这个请求对应的客户端链接 */ r->connection = c; /* 存放请求对应的存放 main 级别配置结构体的指针数组 */ r->main_conf = hc->conf_ctx->main_conf; /* 存放请求对应的存放 srv 级别配置结构体的指针数组 */ r->srv_conf = hc->conf_ctx->srv_conf; /* 存放请求对应的存放 loc 级别配置结构体的指针数组 */ r->loc_conf = hc->conf_ctx->loc_conf; /* 在接收完 HTTP 头部,第一次在业务上处理 HTTP 请求时,HTTP 框架提供的 * 处理方法是 ngx_http_process_request。但若是该方法没法一次处理完该 * 请求的所有业务,在归还控制权到 epoll 事件模块后,该请求回调时, * 将经过 ngx_http_request_handler 方法来处理,而这个方法中对于可读 * 事件的处理就是调用 read_event_handler 处理请求,也就是说,HTTP 模块 * 但愿在底层处理请求的读事件时,从新实现 read_event_handler 方法 */ r->read_event_handler = ngx_http_block_reading; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_set_connection_log(r->connection, clcf->error_log); /* header_in: 存储读取到的 HTTP 头部数据 */ r->header_in = hc->busy ? hc->busy->buf : c->buffer; /* headers_out: HTTP 模块会把想要发送的 HTTP 响应信息放到 headers_out 中, * 指望 HTTP 框架将 headers_out 中的成员序列化为 HTTP 响应包发送给用户 */ if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; } if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; } /* 存放指向全部的 HTTP 模块的上下文结构体的指针数组 */ r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); return NULL; } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { ngx_destroy_pool(r->pool); return NULL; } #if (NGX_HTTP_SSL) if (c->ssl) { r->main_filter_need_in_memory = 1; } #endif /* 当前请求既多是用户发来的请求,也多是派生出的子请求,而 main * 则标识一系列相关的派生子请求的原始请求,通常可经过 main 和当前 * 请求的地址是否相等来判断当前请求是否为用户发来的原始请求 */ r->main = r; /* 表示当前请求的引用次数。例如,在使用 subrequest 功能时,依附在 * 这个请求上的子请求数目会返回到 count 上,每增长一个子请求,count * 数就要加 1. 其中任何一个子请求派生出新的子请求时,对应的原始请求 *(main 指针指向的请求)的 count 值都要加 1。又如,当咱们接收 HTTP * 包体时,因为这也是一个异步调用,全部 count 上也须要加 1,这样在结束 * 请求时,就不会在 count 引用计数未清零时销毁请求。 * * 在 HTTP 模块中每进行一类新的操做,包括为一个请求添加新的事件,或者把 * 一些已经由定时器、epoll 中移除的事件从新加入其中,都须要把这个请求的 * 引用计数加 1,这是由于须要让 HTTP 框架知道,HTTP 模块对于该请求有 * 独立的异步处理机制,将由该 HTTP 模块决定这个操做何时结束,防止 * 在这个操做还未结束时 HTTP 框架却把这个请求销毁了 */ r->count = 1; tp = ngx_timeofday(); /* 当前请求初始化时的时间。start_sec是格林威治时间1970年1月1日0:0:0到当前时间的秒数。 * 若是这个请求是子请求,则该时间是子请求的生成时间;若是这个请求是用户发来的请求, * 则是在创建起TCP链接后,第一次接收到可读事件时的时间 */ r->start_sec = tp->sec; /* 与start_sec配合使用,表示相对于start_sec秒的毫秒偏移量 */ r->start_msec = tp->msec; r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10; /* ngx_http_process_request_headers 方法在接收、解析完 HTTP 请求的 * 头部后,会把解析完的每个HTTP头部加入到 headers_in 的 headers 链表中, * 同时会构造 headers_in 中的其余成员 */ r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; /* 表示使用 rewrite 重写 URL 的次数。由于目前最多能够更改 10 次, * 因此 uri_changes 初始化为 11,而每重写 URL 一次就把 uri_changes * 减 1,一旦 uri_changes 等于 0,则向用户返回失败 */ r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; /* 表示容许派生子请求的个数,当前最多可为 50,所以该值初始化为 51 */ r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; /* 设置当前请求的状态为正在读取请求的状态 */ r->http_state = NGX_HTTP_READING_REQUEST_STATE; ctx = c->log->data; ctx->request = r; ctx->current_request = r; r->log_handler = ngx_http_log_error_handler; #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif return r; }
当为该客户端的请求建立好 ngx_http_request_t 结构体后,调用 ngx_http_process_request_line 函数处理该请求的请求行。
static void ngx_http_process_request_line(ngx_event_t *rev) { ssize_t n; ngx_int_t rc, rv; ngx_str_t host; ngx_connection_t *c; ngx_http_request_t *r; /* rev->data 指向当前客户端链接对象 ngx_connection_t */ c = rev->data; /* 由前面知,当接收到客户端的请求数据并为该请求建立一个 * ngx_http_request_t 结构体后,c->data 就从新设置为指向 * 该结构体 */ r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request line"); /* 检测该读事件是否已经超时 */ if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } rc = NGX_AGAIN; for ( ;; ) { if (rc == NGX_AGAIN) { /* 读取客户端的请求数据到 header_in 指向的缓存中,若该缓存中 * 已有数据,则直接返回该缓存中数据的大小 */ n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } /* 该函数仅解析请求消息中的第一个行,即请求行 */ rc = ngx_http_parse_request_line(r, r->header_in); /* 解析请求行成功 */ if (rc == NGX_OK) { /* the request line has been parsed successfully */ /* 请求行的大小 */ r->request_line.len = r->request_end - r->request_start; /* 指向接收缓冲区中请求行的起始地址,注意,这里并无内存分配/拷贝 */ r->request_line.data = r->request_start; r->request_length = r->header_in->pos - r->request_start; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); /* 该请求的方法名,GET 或 POST 或其余 */ r->method_name.len = r->method_end - r->request_start + 1; r->method_name.data = r->request_line.data; /* http_protocol.data = "HTTP/1.1" */ if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } /* 解析该请求的 uri */ if (ngx_http_process_request_uri(r) != NGX_OK) { return; } if (r->host_start && r->host_end) { host.len = r->host_end - r->host_start; host.data = r->host_start; rc = ngx_http_validate_host(&host, r->pool, 0); if (rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid host in request line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } if (rc == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { return; } r->headers_in.server = host; } if (r->http_version < NGX_HTTP_VERSION_10) { if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR) { return; } ngx_http_process_request(r); return; } /* 初始化该 header_in.headers 链表 */ if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } c->log->action = "reading client request headers"; /* 上面解析完请求行后,开始处理请求消息的请求头部 */ rev->handler = ngx_http_process_request_headers; ngx_http_process_request_headers(rev); return; } if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]); if (rc == NGX_HTTP_PARSE_INVALID_VERSION) { ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); } else { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); } return; } /* NGX_AGAIN: a request line parsing is still incomplete */ /* ngx_http_parse_reqeust_line 方法返回NGX_AGAIN,则表示须要接收更多的字符流, * 这时须要对header_in缓冲区作判断,检查是否还有空闲的内存,若是还有未使用的 * 内存能够继续接收字符流,不然调用ngx_http_alloc_large_header_buffer方法 * 分配更多的接收缓冲区。究竟是分配多大?这有nginx.conf文件中的 * large_client_header_buffers 配置项指定 */ if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 1); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { r->request_line.len = r->header_in->end - r->request_start; r->request_line.data = r->request_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE); return; } } } }
当处理完请求消息的请求行后,就会调用 ngx_http_process_request_headers 函数开始处理请求消息的请求头部。
static void ngx_http_process_request_headers(ngx_event_t *rev) { u_char *p; size_t len; ssize_t n; ngx_int_t rc, rv; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; ngx_http_request_t *r; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; c = rev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request header line"); /* 检测该读事件是否超时 */ if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } /* 获取 main 级别的配置 */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); rc = NGX_AGAIN; /* 在该循环中,将 HTTP 请求头一个个的解析出来,并添加到 * headers_in.header 链表中 */ for ( ;; ) { if (rc == NGX_AGAIN) { /* 若当前 heder_in 指向的缓存已所有使用完,则须要分配更多的内存 */ if (r->header_in->pos == r->header_in->end) { /* 为该缓存分配更多的内存 */ rv = ngx_http_alloc_large_header_buffer(r, 0); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { p = r->header_name_start; r->lingering_close = 1; if (p == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too large request"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } len = r->header_in->end - p; if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; } ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } } /* 读取数据,若 header_in 指向的缓存中仍然有未处理的数据,则 * 直接返回,不然须要从 socket 中读取数据 */ n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } /* the host header could change the server configuration context */ /* 获取当前 server{} 下的配置 */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); /* 该函数是将头部数据解析出一行 */ rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\"", r->header_end - r->header_name_start, r->header_name_start); continue; } /* a header line has been parsed successfully */ /* 将解析出来的请求头存入到 headers_in.headers 链表中 */ h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } h->hash = r->header_hash; /* 头部名称,如 "Host" */ h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '\0'; /* 该头部对应的值,如 "192.168.56.101" */ h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (h->key.len == r->lowcase_index) { /* r->lowcase_header 存放的上面解析出来的 h->key.data * 的小写字符串 */ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } /* 在 headers_in_hash 指向的 hash 表中寻找是否与该 lowcase_key * 相同的项 */ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); /* 若能找到,则调用该头部对应的的处理方法 */ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value); continue; } /* 请求消息的请求头部解析完成 */ if (rc == NGX_HTTP_PARSE_HEADER_DONE) { /* a whole header has been parsed successfully */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; /* 对解析后的 HTTP 头部字段的一些处理,检测解析出来的请求头是否正确 */ rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { return; } /* 在解析并处理 HTTP 的头部数据后,开始处理该 HTTP 请求 */ ngx_http_process_request(r); return; } if (rc == NGX_AGAIN) { /* a header line parsing is still not complete */ continue; } /* rc == NGX_HTTP_PARSE_INVALID_HEADER */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } }
void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; c = r->connection; #if (NGX_HTTP_SSL) if (r->http_connection->ssl) { long rc; X509 *cert; ngx_http_ssl_srv_conf_t *sscf; if (c->ssl == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent plain HTTP request to HTTPS port"); ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS); return; } sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); if (sscf->verify) { rc = SSL_get_verify_result(c->ssl->connection); if (rc != X509_V_OK && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc))) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client SSL certificate verify error: (%l:%s)", rc, X509_verify_cert_error_string(rc)); ngx_ssl_remove_cached_session(sscf->ssl.ctx, (SSL_get0_session(c->ssl->connection))); ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR); return; } if (sscf->verify == 1) { cert = SSL_get_peer_certificate(c->ssl->connection); if (cert == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent no required SSL certificate"); ngx_ssl_remove_cached_session(sscf->ssl.ctx, (SSL_get0_session(c->ssl->connection))); ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); return; } X509_free(cert); } } } #endif /* 因为如今已经开始准备调用各 HTTP 模块处理请求了,再也不存在 * 接收 HTTP 请求头部超时的问题,所以须要从定时器中将当前 * 链接的读事件移除 */ if (c->read->timer_set) { ngx_del_timer(c->read); } #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; (void) ngx_atomic_fetch_add(ngx_stat_writing, 1); r->stat_writing = 1; #endif /* 设置该读、写事件的回调函数 */ c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; /* * 设置 ngx_http_request_t 结构体的 read_event_handler 方法为 * ngx_http_block_reading。当再次有读事件到来时,这个方法能够 * 认为不作任何事,它的意义在于,目前已经开始处理 HTTP 请求, * 除非某个 HTTP 模块从新设置了 read_event_handler 方法,不然 * 任何读事件都将得不处处理,也能够认为读事件被阻塞了 */ r->read_event_handler = ngx_http_block_reading; /* 该函数肯定 phase_handler 的值,即从 ngx_http_phase_engine_t 指定 * 数组的第几个回调函数开始执行,而后依次执行 HTTP 的各个阶段 */ ngx_http_handler(r); ngx_http_run_posted_requests(c); }
void ngx_http_handler(ngx_http_request_t *r) { ngx_http_core_main_conf_t *cmcf; r->connection->log->action = NULL; /* 若是 internal 标志位为 1,则表示当前须要作内部跳转,将要把 * 结构体中的 phase_handler 序号置为 server_rewrite_index. */ if (!r->internal) { /* 当 internal 标志位为 0 时,表示不须要重定向(如刚开始处理请求时), * 将 phase_handler 序号置为 0,意味着从 ngx_http_phase_engine_t 指定 * 数组的第一个回调方法开始执行 */ switch (r->headers_in.connection_type) { case 0: r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); break; case NGX_HTTP_CONNECTION_CLOSE: r->keepalive = 0; break; case NGX_HTTP_CONNECTION_KEEP_ALIVE: /* 标志位,为 1 表示当前请求是 keepalive 请求,即长链接 */ r->keepalive = 1; break; } /* 延迟关闭标志位,为 1 时表示须要延迟关闭。例如,在接收完 * HTTP 头部时若是发现包体存在,该标志位为设为 1,而放弃接收 * 包体时会设为 0 */ r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked); /* 置为 0,表示从 ngx_http_phase_engine_t 指定数组的第一个回调 * 方法开始执行 */ r->phase_handler = 0; } else { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /* 这里,把phase_handler序号设为server_rewrite_index,这意味着 * 不管以前执行到哪个阶段,立刻都要从新从NGX_HTTP_SERVER_REWRITE_PHASE * 阶段开始再次执行,这是Nginx的请求能够反复rewrite重定向的基础 */ r->phase_handler = cmcf->phase_engine.server_rewrite_index; } r->valid_location = 1; #if (NGX_HTTP_GZIP) r->gzip_tested = 0; r->gzip_ok = 0; r->gzip_vary = 0; #endif r->write_event_handler = ngx_http_core_run_phases; /* 开始执行 HTTP 请求的各个阶段 */ ngx_http_core_run_phases(r); }
/* ngx_http_phase_engine_t结构体就是全部的ngx_http_phase_handler_t组成的数组 */ typedef struct { /* handlers是由ngx_http_phase_handler_t构成的数组首地址,它表示一个请求可能 * 经历的全部ngx_http_handler_pt处理方法 */ ngx_http_phase_handler_t *handlers; /* 表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法 * 在handlers数组中的序号,用于在执行HTTP请求的任何阶段中快速跳转到 * NGX_HTTP_SERVER_REWRITE_PHASE阶段处理请求 */ ngx_uint_t server_rewrite_index; /* 表示NGX_HTTP_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法 * 在handlers数组中的序号,用于在执行HTTP请求的任何阶段中快速跳转到 * NGX_HTTP_REWRITE_PHASE阶段处理请求 */ ngx_uint_t location_rewrite_index; }ngx_http_phase_engine_t; void ngx_http_core_run_phases(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_phase_handler_t *ph; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /* handlers 是由 ngx_http_phase_handler_t 构成的数组首地址,它表示 * 一个请求可能经历的全部 ngx_http_handler_pt 处理方法 */ ph = cmcf->phase_engine.handlers; /* * 在处理到某一个 HTTP 阶段时,HTTP 框架将会在 checker 方法已实现的前提下 * 首先调用 checket 方法来处理请求,而不会直接调用任何阶段中的handler方法, * 只有在checket方法中才会去调用handler方法。所以,事实上全部的checker方法 * 都是由框架中的 ngx_http_core_module 模块实现的,且普通的 HTTP 模块没法 * 重定义 checket 方法 */ while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) { return; } } }
下面 HTTP 各个阶段的分析可参考 HTTP 阶段执行
执行 HTTP 各个阶段中,在 NGX_HTTP_CONTENT_PHASE 阶段以前的全部阶段均可参考 HTTP 阶段执行,当到了 NGX_HTTP_CONTENT_PHASE 阶段以后,就会开始进行与 upstream 相关的处理。
该阶段实现的 checker 方法为 ngx_http_core_content_phase:
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { size_t root; ngx_int_t rc; ngx_str_t path; /* r->content_handler 是在执行 NGX_HTTP_FIND_CONFIG_PHASE 阶段中找到匹配的 * location 后,调用 ngx_http_update_location_config 函数进行设置的,而该函数 * 中赋给 r->content_handler 的 clcf->handler 又是在解析 proxy_pass 设置的, * 指向的回调函数为 ngx_http_proxy_handler 函数 */ if (r->content_handler) { /* 指向的回调函数为一个空函数,什么也不处理 */ r->write_event_handler = ngx_http_request_empty_handler; /* 所以,这里会先调用 ngx_http_proxy_handler 函数进行处理 */ ngx_http_finalize_request(r, r->content_handler(r)); return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "content phase: %ui", r->phase_handler); rc = ph->handler(r); if (rc != NGX_DECLINED) { ngx_http_finalize_request(r, rc); return NGX_OK; } /* rc == NGX_DECLINED */ ph++; if (ph->checker) { r->phase_handler++; return NGX_AGAIN; } /* no content handler was found */ if (r->uri.data[r->uri.len - 1] == '/') { if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "directory index of \"%s\" is forbidden", path.data); } ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); return NGX_OK; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found"); ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; }