poxy_pass
nginx中两个模块有proxy_pass
指令,用于反向代理配置项。
分别是:
ngx_http_proxy_module
ngx_stream_proxy_module
ngx_http_proxy_module
的proxy_pass
句法: | proxy_pass URL; |
---|---|
上下文: | location, if in location, limit_except |
说明: | 设置后端代理服务器的协议(protocol)和地址(address),以及location中可以匹配的一个可选的URI。协议可以是"http"或"https"。地址可以是一个域名或ip地址和端口,或者一个 unix-domain socket 路径。 详见官方文档: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass |
ngx_stream_proxy_module
的proxy_pass
句法: | proxy_pass address; |
---|---|
上下文: | server |
说明: | 设置后端代理服务器的地址。这个地址(address)可以是一个域名或ip地址和端口,或者一个 unix-domain socket路径。 详见官方文档: http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_pass |
upstream
upstream为nginx负载均衡配置项
句法: | upstream |
---|---|
上下文: | http |
说明: | 定义一组服务器。服务器可以侦听不同的端口。此外,侦听 TCP 和 UNIX 域套接字的服务器可以混合使用。默认情况下,使用加权轮询负载均衡方法在服务器之间分配请求。 |
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 backup1.example.com backup;
}
在上面的示例中,每7个请求将按如下方式分配:5 个请求转到backend1.example.com
第二个和第三个服务器中的每一个。
如果在与服务器通信期间发生错误,则请求将传递到下一个服务器,依此类推,直到尝试所有正在运行的服务器。如果无法从任何服务器获得成功响应,则客户端将收到与最后一个服务器通信的结果。
配置基本的格式为在server{},proxy_pass到upstream。
server { listen 10.10.55.66:8888 backlog=20480; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $remote_port; limit_req zone=req10k burst=1000; location / { proxy_pass http://rp_10_10_55_66_8888; } } upstream rp_10_10_55_66_8888 { check type=http default_down=false timeout=4000 rise=5 fall=4 interval=5000; check_http_send "HEAD /healthCheck HTTP/1.0\r\nHOST:10.10.55.66\r\n\r\n"; server 10.10.55.67:8080 weight=20; } |
源码分析
ngx_http_proxy_pass函数
static char * ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ... // 这里loc对应的conf的handler置为要转发的handler clcf->handler = ngx_http_proxy_handler; // 这里u保存了对应upstream的信息 //调用add函数把upstream加入到umcf->implicit_upstreams plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
... } |
ngx_http_upstream_add函数
ngx_http_upstream_srv_conf_t * ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) { ... //find upstream是否在红黑树中 umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module); uscfp = umcf->upstreams.elts; uscf = ngx_http_upstream_rbtree_lookup(umcf, &u->host); // 开始找不到,进行创建和implicit_upstreams part = &umcf->implicit_upstreams.part; uscfp = part->elts; uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t)); // 赋值...... // 把指针赋值到umcf->upstreams 、umcf->implicit_upstreams uscfp = ngx_array_push(&umcf->upstreams); *uscfp = uscf; uscfp = ngx_list_push(&umcf->implicit_upstreams); *uscfp = uscf; return uscf ... } |
遇到upstream指令的时候会调用http_upstream,继续调用http_upstream_add。这次带有创建标志。
把放在implicit不确定的upstream加入真正的红黑树里,同时从implicit中删除。
而后返回进行接下来upstream块中指令的填充。
ngx_http_upstream_srv_conf_t * ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) { ... // 遍历寻找 for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { part = part->next; uscfp = part->elts; i = 0; } // 找到 uscf = uscfp[i]; ngx_rbtree_insert(&umcf->rbtree, &uscfp[i]->node); ngx_list_delete(&umcf->implicit_upstreams, &uscfp[i]); return uscf; } ... } |
ngx_http_proxy_handler函数
qproxy_handler设置了create request的方法
在接收请求body逻辑中调用upstream init的方法
static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) { ... if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } u->create_request = ngx_http_proxy_create_request; u->reinit_request = ngx_http_proxy_reinit_request; u->process_header = ngx_http_proxy_process_status_line; u->abort_request = ngx_http_proxy_abort_request; u->finalize_request = ngx_http_proxy_finalize_request; rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); ... } |
ngx_http_upstream_init函数
调用了init request
static void ngx_http_upstream_init_request(ngx_http_request_t *r) { ... // 设置init初始化函数 if (uscf->peer.init(r, uscf) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } u->peer.start_time = ngx_current_msec; if (u->conf->next_upstream_tries && u->peer.tries > u->conf->next_upstream_tries) { u->peer.tries = u->conf->next_upstream_tries; } // 发起连接 ngx_http_upstream_connect(r, u); ... } void ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) { ... rc = ngx_event_connect_peer(&u->peer); ... } |
ngx_http_upstream函数
static char * ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { ... uscf = ngx_http_upstream_add ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf; uscf->srv_conf = ctx->srv_conf; pcf = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_UPS_CONF; //解析upstream内部块的指令 rv = ngx_conf_parse(cf, NULL); ... } |
upstream块中的server指令
{ ngx_string("server"), NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, ngx_http_upstream_server, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL }, |
static char * ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ... if (uscf->servers == NULL) { uscf->servers = ngx_array_create(cf->pool, 4,sizeof(ngx_http_upstream_server_t)); us = ngx_array_push(uscf->servers); ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); value = cf->args->elts; vni = -1; us->hp_addr = NULL; // 获得参数 us->name = u.url; us->addrs = u.addrs; us->naddrs = u.naddrs; us->host = u.host; us->weight = weight; us->max_fails = max_fails; us->fail_timeout = fail_timeout; us->id = id; ... } |
结束
一个完整的nginx proxy_pass和upstream配置、源码分析就是这样的。