该模块可以完成如下的功能,当我们输入http://你的ip/lcw?s_sh000001时,会使用subrequest方式得到新浪服务器上的上证指数,代码如下:

//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:979951191@qq.com
//@brief: 使用subrequest方式访问第三方服务 #include <ngx_config.h>//包含必要的头文件
#include <ngx_core.h>
#include <ngx_http.h>
//请求上下文,用于保存子请求回调方法中解析出来的股票数据
typedef struct
{
ngx_str_t stock[6];
} ngx_http_lcwsubrequest_ctx_t;
//先声明函数
static char *ngx_http_lcwsubrequest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_lcwsubrequest_handler(ngx_http_request_t *r);
static ngx_int_t lcwsubrequest_subrequest_post_handler(ngx_http_request_t *r,void *data, ngx_int_t rc);
static void lcwsubrequest_post_handler(ngx_http_request_t * r);
//ngx_command_t定义模块的配置文件参数
static ngx_command_t ngx_http_lcwsubrequest_commands[] =
{
{
//配置项名称
ngx_string("lcwsubrequest"),
//配置项类型,将指定配置项可以出现的位置
//例如出现在server{}或location{}中,以及他可以携带的参数个数
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
//ngx_command_t结构体中的set成员,
//当在某个配置块中出现lcwsubrequest配置项时,Nginx将会调用ngx_http_lcwsubrequest方法
//ngx_http_lcwsubrequest方法将在下面实现
ngx_http_lcwsubrequest,
//在配置文件中的偏移量conf
NGX_HTTP_LOC_CONF_OFFSET,
//offset通常用于使用预设的解析方法解析配置项,需要与conf配合使用
0,
//配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
NULL
},
//ngx_null_command是一个空的ngx_command_t结构,用来表示数组的结尾
ngx_null_command
};
//ngx_http_module_t的8个回调方法,因为目前没有什么工作是必须在HTTP框架初始化
//时完成的,所以暂时不必实现ngx_http_module_t的8个回调方法
static ngx_http_module_t ngx_http_lcwsubrequest_module_ctx =
{
NULL, // preconfiguration解析配置文件前调用
NULL, // postconfiguration 完成配置文件解析后调用 NULL, // create main configuration当需要创建数据结构用于存储main级别的
//(直属于http{}块的配置项)的全局配置项时
NULL, // init main configuration常用于初始化main级别的配置项 NULL, // create server configuration当需要创建数据结构用于存储srv级别的
//(直属于server{}块的配置项)的配置项时
NULL, // merge server configuration用于合并main级别和srv级别下的同名配置项 NULL, // create location configuration 当需要创建数据结构用于存储loc级别的
//(直属于location{}块的配置项)的配置项时
NULL // merge location configuration 用于合并srv和loc级别下的同名配置项
};
//定义lcwsubrequest模块
//lcwsubrequest模块在编译时会被加入到ngx_modules全局数组中
//Nginx在启动时,会调用所有模块的初始化回调方法
//HTTP框架初始化时会调用ngx_http_module_t中的8个方法
//HTTP模块数据结构
ngx_module_t ngx_http_lcwsubrequest_module =
{
NGX_MODULE_V1,//该宏为下面的ctx_index,index,spare0,spare1,spare2,spare3,version变量
//提供了初始化的值:0,0,0,0,0,0,1
//ctx_index表示当前模块在这类模块中的序号
//index表示当前模块在所有模块中的序号,Nginx启动时会根据ngx_modules数组设置各模块的index值
//spare0 spare系列的保留变量,暂未使用
//spare1
//spare2
//spare3
//version模块的版本,便于将来的扩展,目前只有一种,默认为1
&ngx_http_lcwsubrequest_module_ctx, //ctx用于指向一类模块的上下文结构
ngx_http_lcwsubrequest_commands, //commands将处理nginx.conf中的配置项
NGX_HTTP_MODULE, //模块的类型,与ctx指针紧密相关,取值范围是以下5种:
//NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE
//以下7个函数指针表示有7个执行点会分别调用这7种方法,对于任一个方法而言,如果不需要nginx在某个是可执行它
//那么简单地将他设为空指针即可
NULL, //master进程启动时回调init_master
NULL, //init_module回调方法在初始化所有模块时被调用,在master/worker模式下,
//这个阶段将在启动worker子进程前完成
NULL, //init_process回调方法在正常服务前被调用,在master/worker模式下,
//多个worker子进程已经产生,在每个worker子进程的初始化过程会调用所有模块的init_process函数
NULL, //由于nginx暂不支持多线程模式,所以init thread在框架代码中没有被调用过
NULL, // exit thread,也不支持
NULL, //exit process回调方法将在服务停止前调用,在master/worker模式下,worker进程会在退出前调用它
NULL, //exit master回调方法将在master进程退出前被调用
NGX_MODULE_V1_PADDING //这里是8个spare_hook变量,是保留字段,目前没有使用,Nginx提供了NGX_MODULE_V1_PADDING宏来填充
};
/******************************************************
函数名:lcwsubrequest_subrequest_post_handler(ngx_http_request_t *r,void *data, ngx_int_t rc)
参数:
功能:子请求结束时的处理方法
*******************************************************/
static ngx_int_t lcwsubrequest_subrequest_post_handler(ngx_http_request_t *r,void *data, ngx_int_t rc)
{
//当前请求r是子请求,它的parent成员就指向父请求
ngx_http_request_t *pr = r->parent;
//注意,上下文是保存在父请求中的,所以要由pr中取上下文。
//其实有更简单的方法,即参数data就是上下文,初始化subrequest时
//我们就对其进行设置了的,这里仅为了说明如何获取到父请求的上下文
//上下文是全局数据结构,应该也可以直接取吧?
ngx_http_lcwsubrequest_ctx_t* myctx = ngx_http_get_module_ctx(pr, ngx_http_lcwsubrequest_module);
pr->headers_out.status = r->headers_out.status;
//如果返回NGX_HTTP_OK(也就是200)意味着访问新浪服务器成功,接着将
//开始解析http包体
if (r->headers_out.status == NGX_HTTP_OK)
{
int flag = 0;
//在不转发响应时,buffer中会保存着上游服务器的响应。特别是在使用
//反向代理模块访问上游服务器时,如果它使用upstream机制时没有重定义
//input_filter方法,upstream机制默认的input_filter方法会试图
//把所有的上游响应全部保存到buffer缓冲区中
ngx_buf_t* pRecvBuf = &r->upstream->buffer;
//以下开始解析上游服务器的响应,并将解析出的值赋到上下文结构体
//myctx->stock数组中
//新浪服务器返回的大致如下
//var hq_str_s_sh000001="上证指数,3954.556,68.236,1.76,4300733,57868551";
for (; pRecvBuf->pos != pRecvBuf->last; pRecvBuf->pos++)
{
if (*pRecvBuf->pos == ',' || *pRecvBuf->pos == '\"')
{
if (flag > 0)
{
myctx->stock[flag - 1].len = pRecvBuf->pos - myctx->stock[flag - 1].data;
}
flag++;
myctx->stock[flag - 1].data = pRecvBuf->pos + 1;
}
if (flag > 6)
break;
}
}
//这一步很重要,设置接下来父请求的回调方法
pr->write_event_handler = lcwsubrequest_post_handler;
return NGX_OK;
}
/******************************************************
函数名:lcwsubrequest_post_handler(ngx_http_request_t * r)
参数:
功能:父请求的回调方法
*******************************************************/
static void lcwsubrequest_post_handler(ngx_http_request_t * r)
{
//如果没有返回200则直接把错误码发回用户
if (r->headers_out.status != NGX_HTTP_OK)
{
ngx_http_finalize_request(r, r->headers_out.status);
return;
}
//当前请求是父请求,直接取其上下文
ngx_http_lcwsubrequest_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_lcwsubrequest_module);
//定义发给用户的http包体内容,格式为:
//stock[…],Today current price: …, volumn: …
ngx_str_t output_format = ngx_string("Hello,are you OK?stock[%V],Today current price: %V, volumn: %V");
//计算待发送包体的长度
//减去6是因为格式控制符"%V"是会被替换成要输出的变量的,在len成员里计算了它的长度,需要减去
int bodylen = output_format.len + myctx->stock[0].len+ myctx->stock[1].len + myctx->stock[4].len - 6;
r->headers_out.content_length_n = bodylen;
//在内存池上分配内存保存将要发送的包体
ngx_buf_t* b = ngx_create_temp_buf(r->pool, bodylen);
//将取到的三个参数写到输出的数组里面
ngx_snprintf(b->pos, bodylen, (char*)output_format.data,&myctx->stock[0], &myctx->stock[1], &myctx->stock[4]);
//设置last指针
b->last = b->pos + bodylen;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
//设置Content-Type,注意汉字编码新浪服务器使用了GBK
static ngx_str_t type = ngx_string("text/plain; charset=GBK");
r->headers_out.content_type = type;
r->headers_out.status = NGX_HTTP_OK;
r->connection->buffered |= NGX_HTTP_WRITE_BUFFERED;
ngx_int_t ret = ngx_http_send_header(r);
ret = ngx_http_output_filter(r, &out);
//注意,这里发送完响应后必须手动调用ngx_http_finalize_request
//结束请求,因为这时http框架不会再帮忙调用它
ngx_http_finalize_request(r, ret);
}
/******************************************************
函数名:ngx_http_lcwsubrequest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
参数:
功能:lcwtest方法的实现
*******************************************************/
static char* ngx_http_lcwsubrequest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
//首先找到lcwsubrequest配置项所属的配置块,clcf貌似是location块内的数据
//结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个
//http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
//http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果
//请求的主机域名、URI与lcwsubrequest配置项所在的配置块相匹配,就将调用我们
//实现的ngx_http_lcwsubrequest_handler方法处理这个请求
//ngx_http_lcwsubrequest_handler将在下面实现
clcf->handler = ngx_http_lcwsubrequest_handler;
return NGX_CONF_OK;
}
/******************************************************
函数名:ngx_http_lcwsubrequest_handler(ngx_http_request_t *r)
参数:ngx_http_request_t结构体
功能:创建subrequest子请求
*******************************************************/
static ngx_int_t ngx_http_lcwsubrequest_handler(ngx_http_request_t *r)
{
//创建http上下文
ngx_http_lcwsubrequest_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_lcwsubrequest_module);
if (myctx == NULL)
{
myctx = ngx_palloc(r->pool, sizeof(ngx_http_lcwsubrequest_ctx_t));
if (myctx == NULL)
{
return NGX_ERROR;
}
//将上下文设置到原始请求r中
ngx_http_set_ctx(r, myctx, ngx_http_lcwsubrequest_module);
}
// ngx_http_post_subrequest_t结构体会决定子请求的回调方法
//typedef struct{
// ngx_http_post_subrequest_pt handler;//回调方法
// void *data;//ngx_http_post_subrequest_pt回调方法执行时的参数
//}
ngx_http_post_subrequest_t *psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
if (psr == NULL)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//设置子请求回调方法为lcwsubrequest_subrequest_post_handler
psr->handler = lcwsubrequest_subrequest_post_handler;
//data设为myctx上下文,这样回调lcwsubrequest_subrequest_post_handler
//时传入的data参数就是myctx
psr->data = myctx;
//子请求的URI前缀是/list,这是因为访问新浪服务器的请求必须是类似/list=s_sh000001这样的URI,
//这与在nginx.conf中配置的子请求location中的URI是一致的
ngx_str_t sub_prefix = ngx_string("/list=");
ngx_str_t sub_location;
sub_location.len = sub_prefix.len + r->args.len;
sub_location.data = ngx_palloc(r->pool, sub_location.len);
ngx_snprintf(sub_location.data, sub_location.len,"%V%V", &sub_prefix, &r->args);
//sr就是子请求
ngx_http_request_t *sr;
//调用ngx_http_subrequest创建子请求,它只会返回NGX_OK
//或者NGX_ERROR。返回NGX_OK时,sr就已经是合法的子请求。注意,这里
//的NGX_HTTP_SUBREQUEST_IN_MEMORY参数将告诉upstream模块把上
//游服务器的响应全部保存在子请求的sr->upstream->buffer内存缓冲区中
//ngx_http_subrequest函数的参数:
//当前的请求(父请求),子请求的url,子请求的url的参数,sr为输出参数(指向以及建立好的子请求),psr指出子请求结束时必须回调的处理方法
ngx_int_t rc = ngx_http_subrequest(r, &sub_location, NULL, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY);
if (rc != NGX_OK)
{
return NGX_ERROR;
}
//必须返回NGX_DONE,理由同upstream
return NGX_DONE;
}

执行结果如下:

Nginx模块开发(4)————使用subrequest访问第三方服务的更多相关文章

  1. 《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务

    本文是对陶辉<深入理解Nginx>第5章内容的梳理以及实现,代码和注释基本出自此书. 一.upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例 (一)模块框 ...

  2. 使用upstream和subrequest访问第三方服务

    本文是对陶辉<深入理解Nginx>第5章内容的梳理以及实现,代码和注释基本出自此书. 一.upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例 (一)模块框 ...

  3. Nginx模块开发(3)————使用upstream访问第三方服务

    该模块可以完成如下的功能,当我们输入http://你的ip/lcwupstream时,会使用upstream方式访问淘宝搜索,打开淘宝搜索的主页面,代码如下: //start from the ver ...

  4. 【转】Nginx模块开发入门

    转自: http://kb.cnblogs.com/page/98352/ 结论:对Nginx模块开发入门做了一个helloworld的示例,简单易懂.也有一定的深度.值得一看. Nginx模块开发入 ...

  5. Nginx模块开发入门

    前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并 ...

  6. [转] Nginx模块开发入门

    前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并 ...

  7. Nginx模块开发入门(转)

    前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并 ...

  8. Nginx模块开发入门(转)

    前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并 ...

  9. nginx 访问第三方服务(1)

    nginx提供了两种全异步方式来与第三方服务通信,分别是upstream和subrequest. upstream:nginx为代理服务器,作消息透传.将第三方服务的内容原封不动的返回给用户. sub ...

随机推荐

  1. Linux 磁盘管理篇, 内存交换空间

    swap是在系统内存不足的情况下,以硬盘暂时来储存内存中的一些数据来继续程序的执行 查看内存使用情况            free 格式化为swap格式            mkswap 启动sw ...

  2. win下youtube-dl 【ERROR: requested format not available】选下载视频质量的坑--【值得一看】

    需求说明(bug出处): 简单说:下载youtube的视频,嵌入翻译好的中文字幕. 详细说(可略过): 阿里的海外服务器需要布一个Web Service---用以接收国内(本地服务器)的请求, 然后用 ...

  3. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(七)之Access Control

    Access control ( or implementation hiding) is about "not getting it right the first time." ...

  4. springboot 项目使用阿里云短信服务发送手机验证码

    1.注册阿里云账户进行账号实名认证 2.申请短信签名和模板 3.创建access_key和access_secret 4.然后就是代码编写 一.找到产品与服务里面的云通信模块,然后找到短信服务,开通短 ...

  5. 使用snapjs实现svg路径描边动画

    一,snap.svg插件在近几天,突然接到一个需求,内容是要在网页上写一个路径的动画,还需要可以随意控制动画的速度,开始于结束,本来是一个图片可以解决的问题,结果就这样变难了呀,在网上查一会之后,突然 ...

  6. 五分钟!用python绘制漂亮的系统架构图

    Diagrams 是一个基于Python绘制云系统架构的模块,它能够通过非常简单的描述就能可视化架构,并支持以下6个云产品的图标: AWS.Azure.GCP.K8s.阿里云 和 Oracle 云 基 ...

  7. mysql定期任务

    进来开发项目时遇到一个问题,就是每一周需要清理服务器数据库数据.现在我就来记录一下用Navicat for MySQL 来实现定时任务. 1.启动Navicat for MySQL,新建数据库连接,打 ...

  8. CTR学习笔记&代码实现3-深度ctr模型 FNN->PNN->DeepFM

    这一节我们总结FM三兄弟FNN/PNN/DeepFM,由远及近,从最初把FM得到的隐向量和权重作为神经网络输入的FNN,到把向量内/外积从预训练直接迁移到神经网络中的PNN,再到参考wide& ...

  9. Pie 杭电1969 二分

    My birthday is coming up and traditionally I'm serving pie. Not just one pie, no, I have a number N ...

  10. [GO] mac安装GO 初次尝试

    其实试了好多方法,我用的是下面这种方法,简单快捷! 安装homebrew 在终端输入命令 ruby -e "$(curl -fsSL https://raw.githubuserconten ...