Nginx:subrequest的使用方式

参考资料<深入理解Nginx>

subrequest是由HTTP框架提供的一种分解复杂请求的设计模式。

它可以把原始请求分解为许多子请求,使得诸多请求协同完成一个用户请求,并且每个请求只关注一个功能。

使用subrequest的方式只需完成以下4个步骤即可:

1.在nginx.conf文件中配置好子请求的处理方式

2.启动subrequest请求

3.实现子请求执行结束时的回调方法

4.实现父请求被激活时的回调方法

下面将以mytest模块为例来演示这4个步骤。

配置子请求的处理方式

子请求的处理过程与普通请求完全相同。子请求与普通请求的不同在于:子请求是由父请求生成的,而不是接受客户端发来的网络包再有HTTP框架解析出的。

下面会使用ngx_http_proxy_module反向代理模块来处理子请求(假设生成的子请求是以URI为/list开头的请求)。

location /list {
    proxy_pass http://hq.sinajs.com;
    proxy_set_header Accept-Encoding "";
}

我们还需要配置我们的mytest模块

location /test {
    mytest;
}

启动subrequest请求

在ngx_http_mytest_handler处理方法中,可以启动subrequest子请求。

首先调用ngx_http_subrequest方法建立subrequest子请求,在ngx_http_mytest_handler返回后,HTTP框架会自动执行子请求

先看以下ngx_http_subrequest的定义:

ngx_int_t
ngx_http_subrequest(ngx_http_request_t *r,
                    ngx_str_t *uri,ngx_str_t *args,ngx_http_request_t **psr,
                    ngx_http_post_subrequest_t *ps,ngx_uint_t flags);

1.ngx_http_request *r 是指当前请求,也就是父请求。

2.ngx_str_t *u 是子请求的URI。

3.ngx_str_t *args  是子请求的URI参数,如果没有参数,可以传送NULL指针。

4.ngx_http_request **psr  产生的子请求将通过这个参数传出去。

5.ngx_http_post_subrequest_t *ps  用来设置子请求处理完毕时的回调方法,下一节有其说明。

6.ngx_uint_t flags 一般设置为0。

下图是subrequest的启动过程序列图

实现子请求处理完毕时的回调方法

Nginx在子请求正常或者异常结束时,都会调用ngx_http_post_subrequest_pt回调方法,它的定义如下

typedef ngx_int_t (*ngx_http_post_subrequest_pt) (ngx_http_request_t *r,void *data,ngx_int_t rc);

在上一节中提到的ngx_http_post_subrequest_t结构如下

typedef struct {
    ngx_http_post_subrequest_pt handler;
    void *data;
} ngx_http_post_subrequest_t;

其中ngx_http_post_subrequest_pt回调方法执行时的data参数就是该结构体中的data成员指针。

该回调方法ngx_http_request_t类型的参数r指的的子请求。

在ngx_http_post_subrequest_pt回调方法内必须设置父请求激活后的处理方法,例如:

r->parent->write_event_handler=mytest_post_handler;

下图是子请求激活父请求过程的序列图

处理父请求被重新激活后的回调方法

mytest_post_handler是父请求重新激活后的回调方法,它对应于ngx_http_event_handler_pt指针

typedef void (*ngx_http_event_handler_pt) (ngx_http_request_t *r);struct ngx_http_request_s {    ...    ngx_http_event_handler_pt write_event_handler;    ...}

mytest模块完整代码

  1 #include <ngx_config.h>
  2 #include <ngx_core.h>
  3 #include <ngx_http.h>
  4
  5 //hq.sinajs.cn/list=s_sh000001
  6
  7 //请求上下文
  8 typedef struct {
  9     ngx_str_t stock[6];
 10 } ngx_http_mytest_ctx_t;
 11
 12 static char *
 13 ngx_http_mytest(ngx_conf_t *cf,ngx_command_t *cmd,void *conf);
 14 static ngx_int_t
 15 ngx_http_mytest_handler(ngx_http_request_t *r);
 16 static void
 17 mytest_post_handler(ngx_http_request_t *r);
 18
 19
 20 static ngx_command_t ngx_http_mytest_commands[]={
 21     {
 22         //配置项名称
 23         ngx_string("mytest"),
 24         //配置项类型(可出现的位置,参数的个数)
 25         NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
 26         //出现了name中指定的配置项后,将会调用该方法处理配置项的参数
 27         ngx_http_mytest,
 28         NGX_HTTP_LOC_CONF_OFFSET,
 29         0,
 30         NULL
 31     },
 32     ngx_null_command
 33 };
 34
 35 static char *
 36 ngx_http_mytest(ngx_conf_t *cf,ngx_command_t *cmd,void *conf)
 37 {
 38     //找到mytest配置项所属的配置块
 39     ngx_http_core_loc_conf_t *clcf;
 40     clcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);
 41     /*
 42         HTTP框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时
 43         如果请求的主机域名、URI与mytest配置项所在的配置块相匹配,
 44         就将调用我们事先的ngx_http_mytest_handler方法处理这个请求
 45     */
 46     clcf->handler=ngx_http_mytest_handler;
 47     return NGX_CONF_OK;
 48 }
 49
 50 static ngx_http_module_t ngx_http_mytest_module_ctx={
 51     NULL,
 52     NULL,
 53     NULL,
 54     NULL,
 55     NULL,
 56     NULL,
 57     NULL,
 58     NULL
 59 };
 60
 61 ngx_module_t ngx_http_mytest_module={
 62     NGX_MODULE_V1,
 63     //指向ngx_http_module_t结构体
 64     &ngx_http_mytest_module_ctx,
 65     //用来处理nginx.conf中的配置项
 66     ngx_http_mytest_commands,
 67     //表示该模块的类型
 68     NGX_HTTP_MODULE,
 69     NULL,
 70     NULL,
 71     NULL,
 72     NULL,
 73     NULL,
 74     NULL,
 75     NULL,
 76     NGX_MODULE_V1_PADDING
 77 };
 78
 79
 80 //子请求结束时的回调方法
 81 static ngx_int_t mytest_subrequest_post_handler(ngx_http_request_t *r,
 82     void *data,ngx_int_t rc)
 83 {
 84     //获取父请求
 85     ngx_http_request_t *pr=r->parent;
 86     //获取上下文
 87     ngx_http_mytest_ctx_t *myctx=ngx_http_get_module_ctx(pr,ngx_http_mytest_module);
 88
 89     pr->headers_out.status=r->headers_out.status;
 90     //查看返回码,如果为NGX_HTTP_OK,则意味访问成功,接着开始解析HTTP包体
 91     if(r->headers_out.status==NGX_HTTP_OK)
 92     {
 93         int flag=0;
 94         //上游响应会保存在buffer缓冲区中
 95         ngx_buf_t *pRecvBuf=&r->upstream->buffer;
 96         /*
 97             解析上游服务器的相应,并将解析出的值赋到上下文结构体myctx->stock数组中
 98             新浪服务器的返回大致如下:
 99             var hq_str_s_sh000009=" 上证 380,3356.355,-5.725,-0.17,266505,2519967"
100         */
101         for(;pRecvBuf->pos!=pRecvBuf->last;pRecvBuf->pos++)
102         {
103             if(*pRecvBuf->pos==‘,‘||*pRecvBuf->pos==‘\"‘)
104             {
105                 if(flag>0)
106                 {
107                     myctx->stock[flag-1].len=pRecvBuf->pos-myctx->stock[flag-1].data;
108                 }
109                 flag++;
110                 myctx->stock[flag-1].data=pRecvBuf->pos+1;
111             }
112             if(flag>6)
113                 break;
114         }
115     }
116     //设置父请求的回调方法
117     pr->write_event_handler=mytest_post_handler;
118     return NGX_OK;
119 }
120
121 //父请求的回调方法
122 static void
123 mytest_post_handler(ngx_http_request_t *r)
124 {
125     //如果没有返回200,则直接把错误码发送回用户
126     if(r->headers_out.status!=NGX_HTTP_OK)
127     {
128         ngx_http_finalize_request(r,r->headers_out.status);
129         return;
130     }
131     //取出上下文
132     ngx_http_mytest_ctx_t *myctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
133     //定义发给用户的HTTP包体内容
134     ngx_str_t output_format=ngx_string("stock[%V],Today current price:%V,volumn:%V");
135     //计算待发送包体的长度
136     int bodylen=output_format.len+myctx->stock[0].len+
137                 myctx->stock[1].len+myctx->stock[4].len-6;
138     r->headers_out.content_length_n=bodylen;
139     //在内存池上分配内存以保存将要发送的包体
140     ngx_buf_t *b=ngx_create_temp_buf(r->pool,bodylen);
141     ngx_snprintf(b->pos,bodylen,(char *)output_format.data,
142                  &myctx->stock[0],&myctx->stock[1],&myctx->stock[4]);
143     b->last=b->pos+bodylen;
144     b->last_buf=1;
145
146     ngx_chain_t out;
147     out.buf=b;
148     out.next=NULL;
149     //设置Content-Type
150     static ngx_str_t type=ngx_string("text/plain;charset=GBK");
151     r->headers_out.content_type=type;
152     r->headers_out.status=NGX_HTTP_OK;
153
154     r->connection->buffered|=NGX_HTTP_WRITE_BUFFERED;
155     ngx_int_t ret=ngx_http_send_header(r);
156     ret=ngx_http_output_filter(r,&out);
157
158     ngx_http_finalize_request(r,ret);
159 }
160
161 //启动subrequest
162 static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
163 {
164     //创建HTTP上下文
165     ngx_http_mytest_ctx_t *myctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
166     if(myctx==NULL)
167     {
168         myctx=ngx_palloc(r->pool,sizeof(ngx_http_mytest_ctx_t));
169         if(myctx==NULL)
170         {
171             return NGX_ERROR;
172         }
173         ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);
174     }
175     //ngx_http_post_subrequest_t结构体会决定子请求的回调方法
176     ngx_http_post_subrequest_t *psr=ngx_palloc(r->pool,sizeof(ngx_http_post_subrequest_t));
177     if(psr==NULL){
178         return NGX_HTTP_INTERNAL_SERVER_ERROR;
179     }
180     //设置子请求回调方法为mytest_subrequest_post_handler
181     psr->handler=mytest_subrequest_post_handler;
182     //将data设为myctx上下文,这样回调mytest_subrequest_post_handler时传入的data参数就是myctx
183     psr->data=myctx;
184     //子请求的URI前缀是/list
185     ngx_str_t sub_prefix=ngx_string("/list=");
186     ngx_str_t sub_location;
187     sub_location.len=sub_prefix.len+r->args.len;
188     sub_location.data=ngx_palloc(r->pool,sub_location.len);
189     ngx_snprintf(sub_location.data,sub_location.len,
190                  "%V%V",&sub_prefix,&r->args);
191     //sr就是子请求
192     ngx_http_request_t *sr;
193     //调用ngx_http_subrequest创建子请求
194     ngx_int_t rc=ngx_http_subrequest(r,&sub_location,NULL,&sr,psr,NGX_HTTP_SUBREQUEST_IN_MEMORY);
195     if(rc!=NGX_OK){
196         return NGX_ERROR;
197     }
198     return NGX_DONE;
199
200 }

配置好该模块后,利用telnet模拟HTTP请求得到下图

对比与直接访问hq.sinajs.cn/list=s_sh000001

该模块将客户的请求转换成子请求发送个hq.sinajs.cn,然后将其响应的信息修改之后发送给客户。

时间: 2024-11-05 06:13:48

Nginx:subrequest的使用方式的相关文章

nginx subrequest程序示例

仅仅简单的subrequest应用示例. nginx.conf文件: #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; defa

Nginx 四种分配方式——session处理

最近迷上了Nginx,真实麻雀虽小,五脏俱全..功能实在强大.. nginx不单可以作为强大的web服务器,也可以作为一个反向代理服务器,而且nginx还可以按照调度规则实现动态.静态页面的分离,可以按照轮询.ip哈希.URL哈希.权重等多种方式对后端服务器做负载均衡,同时还支持后端服务器的健康检查. 如果只有一台服务器时,这个服务器挂了,那么对于网站来说是个灾难.因此,这时候的负载均衡就会大显身手了,它会自动剔除挂掉的服务器. 下面简单的介绍下我使用Nginx做负载的体会 下载---安装Ngi

centos7中设置nginx的systemctl启动方式

centos7中设置nginx的systemctl启动方式 1.建立服务文件 (1)文件路径 vim /usr/lib/systemd/system/nginx.service (2)服务文件内容 [Unit] Description=nginx - high performance web server After=network.target remote-fs.target nss-lookup.target [Service] Type=forking ExecStart=/opt/ng

Dockerfile构建nginx镜像以及使用nginx -g &quot;daemon off;&quot;方式前台运行

编写安装nginx的shell脚本 #!/bin/bash yum install -y gcc gcc-c++ make pcre pcre-devel zlib zlib-devel cd /usr/local/nginx-1.16.0 ./configure --prefix=/usr/local/nginx && make && make install dockerfile FROM centos:7 ADD nginx-1.16.0.tar.gz /usr/lo

nginx 反向代理配置方式

参考网址 http://www.centoscn.com/nginx/2014/0509/2947.html 1.基于子目录的配置方式 location /name/ { proxy_pass http://127.0.0.1/remote/; } 2.基于域名的配置 TODO

Windows下让nginx以服务的方式启动运行

在windows下安装了nginx, 郁闷是发现它没有以服务方式运行, 也就是说当用户注销后,程序会终止.因此需要将nginx作为服务运行. 方法一: 假设nginx安装在c:\nginx\下: 1.下载微软服务注册工具srvany.exe, instsrv.exe, 存放到c:\nginx\目录下 http://eastedu.bl-changjiang.com/UploadFiles/2006-5/511179043.rar 2.安装NGINX服务, 将命令行切换到c:\nginx\,执行下

nginx和php-fpm调用方式

一.背景: 在开发中碰到一个问题,项目以nginx+php-fpm形式访问交互,结果访问项目时报错如下图: 二.分析: 提示很明确嘛,去看error.log(在nginx.conf或者vhost里头配置的,找到你对应路径即可) 错误信息如下: 1 2 3 2017/09/18 10:46:21 [error] 3880#0: *92 connect() failed (111: Connection refused)    while connecting to upstream, client

nginx 集群配置方式 (一)

我在本机装了3个虚拟机, 3个虚拟机都部署了相同的项目 地址分别为 192.168.20.133:8080 192.168.20.135:8080 192.168.20.136:8080 ngxin安装在133上, 重点配置如下: 因为我135 136 的权重为2, 133的权重为1 , 我开着3个日志, 每次访问到哪个机器,日志会有滚动来确定访问到哪台虚拟机了 一般顺序为 135 136 135 136 133   135 136 135 136 133    135 136 135 136

Nginx 用最快方式让缓存失效

陶辉103 一般让及时缓存失效针对nginx官方是收费的 我们可以用第三方模块 https://github.com/FRiCKLE/ngx_cache_purge proxy_cache_path /data/nginx/tmpcache levels=2:2 keys_zone=two:10m loader_threshold=300 loader_files=200 max_size=200m inactive=1m; server { server_name shop**.com.cn;