Openresty 反向代理返回非200进行retry到自定义upstream,openrestyupstream


利用 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

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之后的自定义逻辑。

请先理解自己的需求,再做选择。

相关内容

    暂无相关文章