概述
nginx通过proxy_pass url; 来指定一组上游服务器,来实现7层http的反向代理功能。
通过URL指定一组上游服务器,URL可以是变量、域名、upstream的配置名称。
server { ...... set $ups "127.0.0.1:8990"; location /test1/ { proxy_pass http://$ups/; } location /test2/ { proxy_pass http://$ups; } location /test3/ { proxy_pass http://127.0.0.1:8991/; } location /test4/ { proxy_pass http://127.0.0.1:8991; } location /test5/ { proxy_pass http://127.0.0.1:8991/www/; } }
对照上述4种配置的测试结果:
# curl -v 'http://127.0.0.1:8080/test1/hello/liwq' $nc -l 127.0.0.1 8990 GET / HTTP/1.0 # curl -v 'http://127.0.0.1:8080/test2/hello/liwq' $nc -l 127.0.0.1 8990 GET /test2/hello/liwq HTTP/1.0 #curl -v 'http://127.0.0.1:8080/test3/hello/liwq' $nc -l 127.0.0.1 8991 GET /hello/liwq HTTP/1.0 #curl -v 'http://127.0.0.1:8080/test4/hello/liwq' $nc -l 127.0.0.1 8991 GET /test4/hello/liwq HTTP/1.0 #curl -v 'http://127.0.0.1:8080/test5/hello/liwq' $nc -l 127.0.0.1 8991 GET /www/hello/liwq HTTP/1.0
小结:
根据proxy_pass后的URL中是否有变量和是否有uri,转到上游的url不同:
- 有变量有uri:转到上游的请求url是proxy_pass 的URL中的uri。
- 有变量无uri:转到上游的请求url是原来请求的url。
- 无变量有uri:转到上游的请求url是proxy_pass 的URL中的uri + 原来请求去掉location 的name剩下的url。
- 无变量无uri:转到上游的请求url是原来请求的url。
代码解析
配置解析
下面通过解析proxy模块的proxy_pass指令来看看,proxy模块是如何选择上游服务器的,又是如何拼接请求把请求发往上游服务器的。下面介绍proxy_pass指令对应的代码ngx_http_proxy_pass函数。
- 通过检测plcf->upstream.upstream || plcf->proxy_lengths 来判断是否在一个location重复配置proxy_pass指令。
- 赋值该location下content阶段的处理函数为ngx_http_proxy_handler。
- 判断URL是否有变量:
- 有变量则调用ngx_http_script_compile函数把变量的回调函数添加到plcf->proxy_lengths和plcf->proxy_values数组中。
- 没有变量,则调用ngx_http_upstream_add查找一组上游配置。
通过配置指令proxy_set_header设置一组发到上游的header,nginx代码是通过ngx_conf_set_keyval_slot函数把key val插入到plcf->headers_source数组中,数组的元素是ngx_keyval_t。
在merge_loc_conf 回调函数中,通过调用ngx_http_proxy_init_headers函数初始header。
ngx_http_proxy_init_headers 函数解析:
- 分配了bucket,说明已经初始化完成了,不用再次初始化了。
- 初始化headers_names为hash表,headers_merged为header 的key val结构。
- 合并headers_source和default_headers到headers_merged数组中,两个数组中有相同的元素,取headers_source。
- 遍历合并后的headers_merged数组,初始化hash表初始化所用的headers_names 数组。同时把合并数组中的header的长度以及回调函数存到headers->lengths数组中,内容存以及回调函数存到headers->values数组中,因涉及到变量,所以比较复杂。lengths数组中的元素是ngx_http_script_copy_code_t结构体,values数组中的元素是ngx_http_script_copy_code_t结构体,和该结构体后边紧跟的内容。如果header的val没有变量,则一个数组元素对应一个header,否则多个数组元素才对应一个header,两个header在数组中会用null隔开。val中变量会调用ngx_http_script_compile函数编译。
- 编译headers->hash。
请求解析
下游请求到达时,解析完请求头后调用11个阶段,当调用到content阶段的时候执行ngx_http_proxy_handler回调函数。
- 调用ngx_http_upstream_create函数创建一个upstream的结构体,赋值给r->upstream;
- 创建proxy模块的请求上下文,其类型为ngx_http_proxy_ctx_t结构体。
- 判断plcf->proxy_lengths数组是否为null
- 如果为null,则说明proxy_pass的url没有变量。
- 如果不为null,则说明url有变量,调用ngx_http_proxy_eval。
- 赋值upstream结构体回调函数和变量。
- 调用ngx_http_read_client_request_body函数读取完body后,再调用ngx_http_upstream_init函数初始化上游链接。
全部读取完下游请求后调用ngx_http_upstream_init初始化上游链接,实际上是调用的ngx_http_upstream_init_request函数。
- 如果不保存上游结果且不忽略客户端异常且post_action为空,则赋值request的读写回调:
r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
- 客户端请求有body,则赋值:
u->request_bufs = r->request_body->bufs;
- 调用u->create_request回调函数创建发送到上游的请求体。其回调函数为:ngx_http_proxy_create_request
- 计算发往上游请求的长度(方法长度+http版本长度+回车换行长度+uri的长度+[配置的body的长度]+ 配置的请求头的长度+原请求请求头的长度),注意:如果没有配置proxy_set_body,且proxy_pass_request_body为on,发到上游的body是原请求的body,原请求的body在r->request_body->bufs中,已经赋值给u->request_bufs。
- 分配请求长度的内存,用来构建发往上游的请求。
- plcf->proxy_lengths && ctx->vars.uri.len proxy_pass的URL有变量也有uri。则发到上游的uri为URL配置的uri。
- URL中没有配置uri,解析客户端的请求uri有效且不是子请求,则发到上游的uri为原客户端的uri。
- 是有效的location且proxy_pass的URL有uri,则发往上游的请求uri是proxy_pass的URL的uri+原请求的uri减去location name前缀再拼上原请求的参数。否则,发往上游请求的url是原客户端的url。
- 请求头中,val为空的key val会被丢弃。
- 如果没有配置proxy_set_body,且proxy_pass_request_body为on,且原请求有body,则把body挂到组装好的请求的内存链后边。
- u->request_bufs指向发往上游的请求头+请求体。
- uscf->peer.init
- 调用ngx_http_upstream_connect函数向上游发起链接请求
转载请注明:爱开源 » nginx proxy 模块请求发往上游