nginx根据后端返回状态进行upstream
需求
某系统,需在国外建立完全独立的一套业务。且国内已有用户还需继续维护。
由于带宽、响应时间等原因,通过db主从的方式全球同步数据存在一系列问题。
问题
- 放在前端的nginx不能判断用户归属国家,判断逻辑太重,nginx不适合做这个
- nginx后端的backend有自己的逻辑判断用户归属,用返回的status code来标识
解决方案
- nginx + lua + subrequest
- 要在content阶段完成
- 需在rewrite阶段做一点处理
- GET和POST要区分对待
- POST的body和GET的args、cookie等处理
- nginx根据后端的不同返回status code,进行逻辑判断,是直接返回给client还是重新proxy到其他国家
为什么是content阶段
前期找了很多方案,目标就是在nginx upstream到后端的时候,将后端的响应拦截下来。然后根据响应的status code加以区分对待。
上面响应
二字说明了问题。
引用网上找来的关于content阶段的说明,nginx 在content阶段生成产生响应,并返回给client。
所以猜想着在这个处理阶段,拦截下来后端的status code。然后过完我们自己的逻辑之后再次进行proxy或者返回给client。(没错,就是猜
,只因水平太菜^_^)
为什么GET和POST要区分对待
GET所有args都在URI中体现。而POST有body的概念,默认做转发等逻辑时是不带这个body的。
这里有一个指令需要注意,ngx.req.read_body
官方解释是Reads the client request body synchronously without blocking the Nginx event loop.
我理解为需要做一个读
的动作,才会将client POST的body取到。
当然如果使用lua_need_request_body
或者类似module也可以实现。
读取body要使用的函数是ngx.req.get_post_args
官方document的解释是Returns a Lua table holding all the current request POST query arguments
这里还有一句叫做Call ngx.req.read_body to read the request body first or turn on the lua_need_request_body directive to avoid errors
对应了上面使用ngx.req.read_body
的原因。
subrequest
关于subrequest直接看官方doc解释
Nginx's subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory or any other nginx C modules like ngx_proxy, ngx_fastcgi, ngx_memc, ngx_postgres, ngx_drizzle, and even ngx_lua itself and etc etc etc.
几个关键字,non-blocking
, internal
, to other locations
, ngx_proxy
,这些都符合我们的目标。原文下面还有一段话,Everything works internally, efficiently, on the C level.
subrequest的返回值是一个lua的table
Returns a Lua table with three slots (res.status, res.header, res.body, and res.truncated).
只看字面意思就知道了。注意这里res.header也是一个table,所以处理的时候需要loop
处理起来就差不多像下面的样子
|
|
注意request_uri
和uri
的区别,不细说了。
剩下的就是判断后端返回status code、各种if判断了。
至于GET请求,直接搞request_uri
就可以了。
|
|
args什么的,直接都带过去了。
GET和POST共同要处理就是header,上面说了是一个table,使用循环处理一下就可以了。
lua里面的for语法和其他脚本语言没什么区别
|
|
这里还有一个问题,如何将响应的body送回给client/backend。
我在官方doc中没找到对应的说明,但从各种例子看,直接使用ngx.print
即可
|
|
为什么rewrite阶段也需要介入
上文我们看到,subrequest是打到/fooPost这个location的。
为什么需要另起一个location,答案是避免死循环,例如在/
启用content_by_lua的逻辑,极可能引起死循环。这部分需要看下nginx如何处理多个location。
那我原来的request_uri是/login,打到backend变成了/fooPost/login,肯定就要使用rewrite了。
|
|
说到这里就差不多了。总之罗里罗嗦的说了一堆,连着思路带着实现都扔了出来。
看官自己理解吧^_^
-EOF-