本文是对陶辉《深入理解Nginx》第5章内容的梳理以及实现,代码和注释基本出自此书。

一、upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例

(一)模块框架

  首先要明确的是,这里是编写一个使用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;
}

原书的ngx_http_mytest_create_loc_conf()和ngx_http_mytest_merge_loc_conf()

  另外根据作者网页上的源码,需要补充上ngx_http_proxy_hide_headers作为默认设置:

static ngx_str_t  ngx_http_proxy_hide_headers[] =
{
ngx_string("Date"),
ngx_string("Server"),
ngx_string("X-Pad"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};

(四)3个必备的回调函数的实现

  首先定义ngx_http_mytest_ctx_t结构体用于保存process_header()方法的解析状态,注意结构体的第二个成员原书没有写,需要补充上。

typedef struct {
ngx_http_status_t status;
ngx_str_t backendServer;
} ngx_http_mytest_ctx_t;

  原书上3个回调函数的代码如下,详细的注释请参考原书:

mytest_upstream_create_request()构造请求

mytest_process_status_line()处理响应行,mytest_upstream_process_header()处理包头

mytest_upstream_finalize_request()释放资源

  值得注意的是mytest_upstream_create_request()中计算queryLineLen中有一项-2。这是因为格式控制符"%V"是会被替换成要输出的变量的,在len成员里计算了它的长度,需要减去。这种处理不要忽略,在subrequest的mytest_post_handler()中也出现了类似的处理。

(五)修改ngx_http_mytest_handler()

  完成的工作是关联HTTP上下文与请求、填写upstream配置结构体和调用ngx_http_upstream_init()启动upstream。

ngx_http_mytest_handler()

(六)测试

  启动nginx,在浏览器中输入http://localhost:8080/test?lumia,可以看到返回的是http://www.google.com.hk/search?q=lumia。

  (8080是作者提供的下载源码中nginx.conf设置的侦听端口号;使用hk是由于被重定向了)

(七)附注

  1.URL中的问号"?"代表什么?它在参数传递时有什么用?

  答:GET方法中的参数请求以问号开始。换句话说,这个"?"后面跟随的是GET方法的参数。

  2.HTTP响应行、HTTP头部、HTTP包体的区分

  (下面的请求和应答例子来自于维基百科)

  客户端请求:

GET / HTTP/1.1
Host:www.google.com

  (末尾有一个空行。第一行指定方法、资源路径、协议版本;第二行是在1.1版里必带的一个header作用指定主机)

  服务器应答:

HTTP/1.1 200 OK
Content-Length: 3059
Server: GWS/2.0
Date: Sat, 11 Jan 2003 02:44:04 GMT
Content-Type: text/html
Cache-control: private
Set-Cookie: PREF=ID=73d4aef52e57bae9:TM=1042253044:LM=1042253044:S=SMCc_HRPCQiqy
X9j; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Connection: keep-alive
...

  在这个包头中,第一行就是HTTP响应行,HTTP/1.1表示支持的版本,200是HTTP状态码,表示处理成功,OK是对状态码200的一个简短描述。

  根据RFC2616,可能使用“状态行”来描述会更好一些?毕竟本文中处理它的函数是mytest_process_status_line(),而且《TCP/IP详解(卷三)》也翻译为“状态行”。下面用“状态行”代替。

Status-Line

   The first line of a Response message is the Status-Line, consisting of the protocol version followed by a numeric status code and its associated textual phrase, with each element separated by SP characters. No CR or LF is allowed except in the final CRLF sequence.
    Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

  除了第一行,其余部分中有一部分是HTTP响应头部,即Response Header Fields,它们提供无法放入状态行的信息。RFC2616很清楚的表明,状态行和HTTP头部是两回事:

 The response-header fields allow the server to pass additional information about the response which cannot beplaced in the Status- Line. These header fields give information about the server and about further access to the resource identified by the Request-URI.
       response-header = Accept-Ranges           ; Section 14.5
| Age ; Section 14.6
| ETag ; Section 14.19
| Location ; Section 14.30
| Proxy-Authenticate ; Section 14.33
| Retry-After ; Section 14.37
| Server ; Section 14.38
| Vary ; Section 14.44
| WWW-Authenticate ; Section 14.47

  在响应头部后,可能还有实体(Entity),而它又分为实体头部(Entity Header Fields)和实体主体(Entity Body),这在RFC2616是进行区分的:

7.1 Entity Header Fields
Entity-header fields define metainformation about the entity-body or,if no body is present, about the resourceidentified by the request. Someof this metainformation is OPTIONAL; some might be REQUIRED by portions of 
this specification. entity-header = Allow ; Section 14.7
| Content-Encoding ; Section 14.11
| Content-Language ; Section 14.12
| Content-Length ; Section 14.13
| Content-Location ; Section 14.14
| Content-MD5 ; Section 14.15
| Content-Range ; Section 14.16
| Content-Type ; Section 14.17
| Expires ; Section 14.21
| Last-Modified ; Section 14.29
| extension-header extension-header = message-header

  阅读《深入理解Nginx》第3.6.3节可以看出,Nginx是将Response Header Fields和Entity Header Fields合称为HTTP头部一并处理的。

  可见,造成理解混乱的原因可能是RFC2616进行区分的Response Header Fields和Entity Header Fields两部分被Nginx一步处理所致。

  最后再看看实体主体,可以视之为HTTP传送的正文:

7.2 Entity Body

   The entity-body (if any) sent with an HTTP request or response is in a format and encoding defined by the entity-header fields.

  这样,就把这几个名词的脉络理清楚了。

二、subrequest:以向nginx发出请求转化为向新浪股票数据发出请求为例

(一)subrequest工作流程

  subrequest由HTTP框架提供,可以把原始请求分解为许多子请求。

  阅读原书5.4和5.5节,可以把使用subrequest的流程概括为:

[HTTP请求需要调用mytest模块处理] -> [mytest模块创建子请求] -> [发送并等待上游服务器处理子请求的响应]

-> (可选)[postpone模块将待转发相应包体放入链表并等待发送 ]

->[执行子请求处理完毕的回调方法ngx_http_post_subrequest_pt]

->[执行父请求被重新激活后的回调方法mytest_post_handler]

  这部分使用了代理模块,但在这里不做详细介绍。下面的代码中没有使用postpone。

(二)配置项设置

  由于子请求需要访问新浪的服务器,并且URL为http://hq.sinajs.cn/list=s_sh000001,因此设置为

location /list {
//上游服务器地址
proxy_pass http://hq.sinajs.cn;
//不希望第三方服务对HTTP包体进行gzip压缩
proxy_set_header Accept-Encoding "";
}

  同时,用户访问nginx服务器mytest模块的URI依然要进行配置

location /query {
mytest;
}

(三)请求上下文

  新浪服务器返回的数据格式是这样的:

var hq_str_s_sh000001="上证指数,2070.369,-15.233,-0.73,1023439,8503131";

  因此把只用来保存请求回调方法中的股票数据的请求上下文定义如下:

typedef struct {
ngx_str_t stock[6];
} ngx_http_mytest_ctx;

(四)回调方法的实现

mytest_subrequest_post_handler()子请求结束时的回调方法

mytest_post_handler()父请求的的回调方法

mytest_post_handler()用于创建子请求

  mytest_post_handler()中的-6的含义与上文upstream部分mytest_upstream_create_request()中计算queryLineLen的-2类似,不再重述。

(五)测试

  模块安装后并开启nginx后,输入下面的内容即可看到返回的内容(nginx.conf设置侦听端口号为8080):

http://localhost:8080/query?s_sh000001

  另外,如果发现没有响应,请在当前环境(如虚拟机)中尝试直连http://hq.sinajs.cn/list=s_sh000001以保证网络连通性。

  本文完整源代码请到《Nginx深入理解》作者陶辉提供的支持页面下载。

作者:五岳 
出处:http://www.cnblogs.com/wuyuegb2312 
对于标题未标注为“转载”的文章均为原创,其版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

 
分类: nginx网络编程
标签: nginx

使用upstream和subrequest访问第三方服务的更多相关文章

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

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

  2. Nginx模块开发(4)————使用subrequest访问第三方服务

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

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

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

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

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

  5. Nginx:访问第三方服务

    参考资料<深入理解Nginx> Nginx可以当做一个强大的反向代理服务器,其反向代理模块是基于upstream方式实现的. upstream的使用方式 HTTP模块在处理任何一个请求时都 ...

  6. SAP云平台上的ABAP编程环境里如何消费第三方服务

    在ABAP On-Premises环境下,使用ABAP编程消费第三方服务,相信很多ABAP顾问都已经非常熟悉了,无非就是使用CL_HTTP_CLIENT或者CL_REST_HTTP_CLIENT来发送 ...

  7. Hexo+NextT第三方服务调用【4】

    该系列博客列表请访问:http://www.cnblogs.com/penglei-it/category/934299.html 摘要        静态站点与动态站点有很大的不一样,它拥有一定的局 ...

  8. Java 后端开发常用的 10 种第三方服务

    请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题. 严格意义上 ...

  9. Java 10 种常用第三方服务

    严格意义上说,所有软件的第三方服务都可以自己开发,不过从零到一是需要时间和金钱成本的.就像我们研发芯片,投入了巨大的成本,但仍然没有取得理想的成绩,有些事情并不是一朝一夕,投机取巧就能完成的. Jav ...

随机推荐

  1. [使用]Git--命令行

    如何利用终端命令将文件上传到github远程服务器 (1) git status 命令查看下状态. (2) git pull 更新代码,确保代码是库上最新代码,防止覆盖其他人的提交. (3) git ...

  2. 用python 爬取网页图片

    import re import string import sys import os import urllib url="http://tieba.baidu.com/p/252129 ...

  3. 01. SQL Server 如何读写数据

    原文:01. SQL Server 如何读写数据 一. 数据读写流程简要SQL Server作为一个关系型数据库,自然也维持了事务的ACID特性,数据库的读写冲突由事务隔离级别控制.无论有没有显示开启 ...

  4. 2014Esri国际用户大会ArcGIS Online

    1.基于什么是新的ArcGISOnline? ArcGISOnline不断更新.大约每四个月就会把新的增强的功能公布到各部分中.有新的空间分析的应用程序,如 Explorer forArcGIS,ap ...

  5. a:focus{outline: none;} 如何去掉点击链接时周围的虚线框outline属性

    1. CSS方式 在IE下是使用html属性:hideFoucs,在HTML标签中加上hidefocus=”true”属性即可,但这个属性是IE私有的,Firefox是不认的. 加了hidefocus ...

  6. 成C++应用程序世界------异常处理

    一. 概述 C++自身有着很强的纠错能力,发展到现在,已经建立了比較完好的异常处理机制.C++的异常情况无非两种,一种是语法错误,即程序中出现了错误的语句,函数,结构和类,致使编译程序无法进行.还有一 ...

  7. unbuntu下的root 用户和 sudo 命令

    参考: http://james23dier.iteye.com/blog/721246 http://blog.csdn.net/shichexixi/article/details/5969993 ...

  8. 在 InstantRails 环境下,安装使用 redMine

    在 InstantRails 环境下,安装使用 redMine 分类: Redmine2009-06-01 10:35 732人阅读 评论(0) 收藏 举报 characterrailsencodin ...

  9. ASP.NET MVC中Area的另一种用法

    ASP.NET MVC中Area的另一种用法 [摘要]本文只是为一行代码而分享 context.MapRoute("API", "api/{controller}/{ac ...

  10. myeclipse搭建svn插件

    在网上查了一下,安装的方法有几种,这里给大家推荐一种快速安装的方法. //第一步 : 下载 site-1.6.5.zip //===================================== ...