nginx模块开发入门(七)-3.2 Anatomy of an Upstream (a.k.a Proxy) Handler
3.2. 剖析Upstream(又称 Proxy) Handler
Anatomy of an Upstream (a.k.a Proxy) Handler
我已经帮你了解了如何让你的handler来产生响应。有些时候你可以用一小段C代码就可以得到响应,但是通常情况下你需要同另外一台server打交道(比如你正在写一个用来实现某种网络协议的模块)。你当然可以自己实现一套网络编程的东东,但是如果你只收到部分的响应,需要等待余下的响应数据,你会怎么办?你不会想阻塞整个事件处理循环吧?这样会毁掉Nginx的良好性能!幸运的是,Nginx允许你在它处理后端服务器(叫做"upstreams")的机制上加入你的回调函数,因此你的模块将可以和其他的server通信,同时还不会妨碍其他的请求。这一节将介绍模块如何和一个upstream(如 Memcached, FastCGI,或者其它HTTP server)通信。
3.2.1. Upstream 回调函数概要
与其他模块的回调处理函数不一样,upstream模块的处理函数几乎不做“实事”。它压根不调用ngx_http_output_filter。它仅仅是告诉回调函数什么时候可以向upstream server写数据了,以及什么时候能从upstream server读数据了。实际上它有6个可用的钩子:
create_request 生成发送到upstream server的request buffer(或者一条缓冲链)
reinit_request 在与后端服务器连接被重置的情况下(在create_request 被第二次调用之前)被调用
process_header 处理upstream 响应的第一个bit,通常是保存一个指向upstream "payload"的指针
abort_request 在客户端放弃请求时被调用
finalize_request 在Nginx完成从upstream读取数据后调用
input_filter 这是一个消息体的filter,用来处理响应消息体(例如把尾部删除)
这些钩子是怎么勾上去的呢?下面是一个例子,简单版本的代理模块处理函数:
static ngx_int_tngx_http_proxy_handler(ngx_http_request_t *r){ ngx_int_t rc; ngx_http_upstream_t *u; ngx_http_proxy_loc_conf_t *plcf; plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);/* set up our upstream struct */ u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); if (u == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module; u->conf = &plcf->upstream;/* attach the callback functions */ 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; r->upstream = u; rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_DONE;}
static ngx_int_tngx_http_character_server_create_request(ngx_http_request_t *r){/* make a buffer and chain */ ngx_buf_t *b; ngx_chain_t *cl; b = ngx_create_temp_buf(r->pool, sizeof("a") - 1); if (b == NULL) return NGX_ERROR; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) return NGX_ERROR;/* hook the buffer to the chain */ cl->buf = b;/* chain to the upstream */ r->upstream->request_bufs = cl;/* now write to the buffer */ b->pos = "a"; b->last = b->pos + sizeof("a") - 1; return NGX_OK;}
static ngx_int_tngx_http_character_server_process_header(ngx_http_request_t *r){ ngx_http_upstream_t *u; u = r->upstream; /* read the first character */ switch(u->buffer.pos[0]) { case '?': r->header_only; /* suppress this buffer from the client */ u->headers_in.status_n = 404; break; case ' ': u->buffer.pos++; /* move the buffer to point to the next character */ u->headers_in.status_n = 200; break; } return NGX_OK;}
ngx_http_character_server_ctx_t *p; /* my custom context struct */ p = ngx_pcalloc(r->pool, sizeof(ngx_http_character_server_ctx_t)); if (p == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, p, ngx_http_character_server_module);
ngx_http_proxy_ctx_t *p; p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);