本文是对陶辉《深入理解Nginx》第5章内容的梳理以及实现,代码和注释基本出自此书。
(一)模块框架
首先要明确的是,这里是编写一个使用upstream的模块,而不是编写upstream模块。因此,和HelloWorld类似,模块结构体ngx_http_mytest_module、模块上下文结构体ngx_http_mytest_module_ctx、数组ngx_http_mytest_command[]、方法ngx_http_mytest()和ngx_http_mytest_handler()的框架是不可少而且又十分相似的。如果忘记了它们之间的关系,请回顾原书或《深入理解Nginx》阅读与实践(一):Nginx安装配置与HelloWorld。
模块处理的请求是ngx_http_request_t结构对象r,它包含了一个ngx_http_upstream_t类型的成员upstream。当upstream非NULL时,将会根据其中设置的内容定制访问第三方服务的方式。而这个请求的处理是由upstream模块来完成的。从这里开始,要注意区分请求r中的upstream成员和Nginx提供的upstream模块不是一回事,而是由前者来指导后者的工作。前者的设定与开启(即告知upstream模块需要进行处理,通过ngx_http_upstream_init()实现)是由我们编写的的第三方模块(本文中是mytest)来完成的。
(二)upstream设置
upstream工作方式的配置可以通过填写由ngx_http_upstream_create(ngx_http_request_t *r)所传入的请求r中的ngx_http_upstream_t结构体来完成。这个函数成功返回时,即把请求r中的upstream设置为非NULL。ngx_http_upstream_t结构体主要包含了一下几个成员,由于原书对这里模块编写所需要用到的成员已做详细介绍(暂时用不到的成员在12章介绍),这里只做一个部分的概括:
typedef ngx_http_upstream_s ngx_http_upstream_t; sturct ngx_http_upstream_s { ... ngx_chain_t request_bufs;//发给上游服务器的请求,由create_request()完成 ngx_http_upstream_conf_t conf;//超时时间等限制性参数 ngx_http_upstream_resolved_t resolved;//用于直接指定的上游服务器地址 //设定方法请见mytest模块的ngx_http_mytest_handler()方法 /* 3个必须实现的回调方法 */ ngx_int_t (*create_request)(ngx_http_request_t *r);//构造向上游服务器发送的请求内容。调用mytest时,只调用一次 ngx_int_t (*process_header)(ngx_http_request_t *r);//收到上游服务器后对包头进行处理的方法 void (*finalize_request) (ngx_http_request_t *r, ngx_int_t rc);//销毁upstream请求时调用 /* 5个可选的回调方法,本文中用不到*/ ngx_int_t (*input_filter_init)(void *data);//处理上游包体 ngx_int_t (*input_filter)(void *data,ssize_t bytes);//处理上游包体 ngx_int_t (*reinit_request)(ngx_http_request_t *r);//第一次向上游服务器建立连接失败时调用 void (*abort_request)(ngx_http_request_t *r); ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); //主要用于反向代理 ... }
可见,使用upstream功能时,除了需要按HelloWorld编写自己的模块和提供处理配置项的方法ngx_http_mytest_create_loc_conf()、ngx_http_mytest_merge_loc_conf()外,还需要填写ngx_http_upstream_t结构体并实现3个必备的回调方法。要注意的是,这些回调方法都是由模块的编写者提供、再由upstream模块来调用的。
(三)配置项获取
原书例子是将访问的URL请求/test?lumia转化成对www.google.com的搜索请求/search?q=lumia。为了简化,大部分参数采用硬编码的形式nginx.conf的添加的内容和以前一样:
location /test {
mytest;
}
typedef struct { ngx_http_upstream_conf_t upstream; } ngx_http_mytest_conf_t; static void* ngx_http_mytest_create_loc_conf(ngx_conf_t *cf) { ngx_http_mytest_conf_t *mycf; mycf = (ngx_http_mytest_conf_t *)ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t)); if(mycf == NULL) { return NULL; } mycf->upstream.connect_timeout = 60000; mycf->upstream.send_timeout = 60000; mycf->upstream.read_timeout = 60000; mycf->upstream.store_access = 0600; mycf->upstream.buffering = 0; mycf->upstream.bufs.num = 8; mycf->upstream.bufs.size =ngx_pagesize; mycf->upstream.buffer_size = ngx_pagesize; mycf->upstream.busy_buffers_size = 2*ngx_pagesize; mycf->upstream.max_temp_file_size = 1024*1024*1024; mycf->upstream.hide_headers = NGX_CONF_UNSET_PTR; mycf->upstream.pass_headers = NGX_CONF_UNSET_PTR; return mycf; } static char* ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_mytest_conf_t *prev = (ngx_http_mytest_conf_t *)parent; ngx_http_mytest_conf_t *conf = (ngx_http_mytest_conf_t *)child; //ngx_conf_merge_str_value(conf->my_str,prev->my_str,"defaultstr"); ngx_hash_init_t hash; hash.max_size = 100; hash.bucket_size = 1024; hash.name = "proxy_headers_hash"; if(ngx_http_upstream_hide_headers_hash(cf,&conf->upstream, &prev->upstream,ngx_http_proxy_hide_headers,&hash)!=NGX_OK) { return NGX_CONF_ERROR; } return NGX_CONF_OK; }