利用
error_page
指令对反向代理返回的404, 302...
等状态码,做子请求的二次处理。
client -[1]-> nginx proxy <--[2]--> 站点A(返回404,302)
|[3]
V
站点B (返回200)
- 比如作为CDN的服务时候,站点A返回302,利用子请求提取
location
的url访问站点B,拿到结果返回给客户端 - 比如作为下载镜像站点,客户端请求下载文件,在站点A无法找到返回404,利用子请求请求B站点
测试环境
- macos
- openresty/1.13.6.1
方案一 error_page
下面是个404状态码,跳转到备用站点的模拟例子
server {
listen 8008;
...
# 测试upstream, proxy_pass 错误吗,二次请求,比如说cdn
# 404,302的时候怎么做子请求跳转
location = /test_404 {
proxy_pass http://127.0.0.1:9996; # 返回404状态码
# uwsgi
# uwsgi_intercept_errors on;
# fastcgi
# fastcgi_intercept_errors on;
proxy_intercept_errors on;
error_page 404 403 = @error_page_404;
}
location @error_page_404 {
content_by_lua_block {
ngx.say('404 => 200')
ngx.exit(200)
}
}
server {
listen 9996;
location /test_404 {
content_by_lua_block {
ngx.exit(404)
}
}
}
注意 proxy_intercept_errors 这个配置要加开启,它的作用是对于 >=300
的状态码转到 error_page
处理流程中。 对应的 uwsgi, fastcgi 方向代理都有类似的指令。
测试
$ curl -i http://127.0.0.1:9996/test_404
HTTP/1.1 404 Not Found
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 02:59:12 GMT
Content-Type: text/html
Content-Length: 175
Connection: keep-alive
$ curl -i 127.0.0.1:8008/test_404
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:02:45 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
404 => 200
方案二 proxy_next_upstream
主要配置
upstream backend_502_testing {
server 127.0.0.1:9998; # 502, 后端就没有服务
server 127.0.0.1:9996;
}
server {
listen 8008;
...
# 502为例子,测试 proxy_next_upstream,失败转移
location = /test_502 {
proxy_next_upstream error timeout http_502;
proxy_pass http://backend_502_testing;
}
}
server {
listen 9996;
location /test_502 {
content_by_lua_block {
ngx.say('502 => 200')
ngx.exit(200)
}
}
}
测试结果
$ curl -i 127.0.0.1:9998/test_502
curl: (7) Failed to connect to 127.0.0.1 port 9998: Connection refused
$ curl -i 127.0.0.1:9996/test_502
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:06:17 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
502 => 200
$ curl -i 127.0.0.1:8008/test_502
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:12:02 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
Connection: keep-alive
502 => 200
此时的errlog
2018/04/22 11:12:02 [error] 80096#0: *108 kevent() reported that connect() failed (61: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET /test_502 HTTP/1.1", upstream: "http://127.0.0.1:9998/test_502", host: "127.0.0.1:8008"
多次请求 127.0.0.1:8008/test_502
就会发现,upstream不是针对每次请求先访问 9998端口的服务,在访问 9996端口的服务,而是当发现 127.0.0.1:9998
服务不可用的时候,有一段时间请求会自动发送到 127.0.0.1:9996
这个健康的后端上,过一段时间才会继续尝试 127.0.0.1:9998
总结
两种方案比较
- 状态码范围,
error_page
方式(方案一)可以捕获的状态码更多 - 场景区别,针对于每个请求,都要先尝试
站点1
,如果得到非200状态码,在访问站点2
(这里也可以增加lua处理), 这种逻辑只能使用方案一。方案二显然是为负载均衡设计的,针对的是后端的健康状态,并且也无法做到切换upstream之后的自定义逻辑。
请先理解自己的需求,再做选择。