[TOC]
背景
偶然有一次机会使用nginx做灰度发布. 想到了nginx的map的功能, 于是使用map进行设置变量. 由于设置变量匹配后, 在反向代理过程中, 会丢失部分的$args及重写的$1/$2等自定义的参数. 这篇博客便是记录一下自己的解决方法.
灰度发布(又名金丝雀发布,英文一般称为GrayRelease
或Dark launch)是为了能够让用户逐步过渡到新功能一种发布方式。 一般是产品上线一个功能,希望在线上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。
我这边采用基于HEADER分流策略
- 获取灰度标识,使用的是
$http_version
(即$http_header名
)的写法;
采用map来实现
map $http_version $group_staging_be_gateway_service {
~^v2$ staging_be_gateway_service_svr_v2;
default staging_be_gateway_service_svr;
}
upstream staging_be_gateway_service_svr {
dynamic_resolve fallback=stale fail_timeout=30s;
server be-gateway-service.staging.svc.cluster.local:8000;
keepalive 512;
}
upstream staging_be_gateway_service_svr_v2 {
dynamic_resolve fallback=stale fail_timeout=30s;
server be-gateway-service-v2.staging.svc.cluster.local:8001;
keepalive 512;
}
server {
listen 80;
server_name api.fenghong.tech;
location ~ ^/v1/(.+)$ {
# add_header Access-Control-Allow-Origin *;
# add_header access-control-allow-methods "GET,PUT,POST,DELETE";
proxy_set_header Host $host;
# 添加http1.1声明和header中connection重写
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://$group_staging_be_gateway_service/__api/$1?$args;
}
}
问题描述
使用上述这套配置后. 进行api访问. 直接加入header version: v2
模拟灰度访问. 发现 $1和$args
全部丢失.
# louis @ LAPTOP-I2BJS52K in ~ [15:22:48]
$ curl 'https://api.fenghong.tech/v1/crm/member/agent_area/2/mappedByDistrictCode?contractId=1039' -H 'version: v2' \
> -H 'authority: api.fenghong.tech' \
> -H 'accept: application/json, text/plain, */*' \
> -H 'authorization: Bearer 3799fd1e-74e2-3b92-a109-52b32d3b8026' \
> -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' \
> -H 'content-type: application/json;charset=UTF-8' \
> -H 'origin: https://www.fenghong.tech' \
> -H 'sec-fetch-site: cross-site' \
> -H 'sec-fetch-mode: cors' \
> -H 'sec-fetch-dest: empty' \
> -H 'referer: https://www.fenghong.tech/' \
> -H 'accept-language: zh-CN,zh;q=0.9' \
> --data-binary '[440203]' \
> --compressed
{"timestamp":"2020-12-29T07:22:48.541+0000","status":404,"error":"Not Found","message":"No message available","path":"/__api/"}
模拟正常访问去掉version: v2
这个header, 即进入正常访问, 这个时候配置居然没有丢失 $1和$args
. 很纳闷.
# louis @ LAPTOP-I2BJS52K in ~ [15:25:48]
$ curl 'https://api.fenghong.tech/v1/crm/member/agent_area/2/mappedByDistrictCode?contractId=1039' \
-H 'authority: api.fenghong.tech' \
-H 'accept: application/json, text/plain, */*' \
-H 'authorization: Bearer 3799fd1e-74e2-3b92-a109-52b32d3b8026' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' \
-H 'content-type: application/json;charset=UTF-8' \
-H 'origin: https://www.fenghong.tech' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: https://www.fenghong.tech/' \
-H 'accept-language: zh-CN,zh;q=0.9' \
--data-binary '[440203]' \
--compressed
{"code":"0","message":"success","data":{"440203":null}}
在预发布环境测试出来后, 要解决这个问题, 得重新写策略来匹配.
方案一
使用rewrite last
, 这个会导致多请求一次匹配. 性能原始的proxy_pass
稍低. 关于nginx
的 rewrite
策略的last. 我以前写过一篇文章. 建议可以看看nginx rewrite的break和last
location ~ ^/v1/(.+)$ {
# add_header Access-Control-Allow-Origin *;
# add_header access-control-allow-methods "GET,PUT,POST,DELETE";
proxy_set_header Host $host;
# 添加http1.1声明和header中connection重写
proxy_http_version 1.1;
proxy_set_header Connection "";
# 重写后去匹配 location ~ ^/__api {} 相关策略.
rewrite ^/v1/(.*)$ /__api/$1?$args last;
#proxy_pass http://$group_staging_be_gateway_service/__api/$1?$args;
}
使用这个配置之后. 灰度环境, 访问立即正常.
# louis @ LAPTOP-I2BJS52K in ~ [15:28:48]
$ curl 'https://api.fenghong.tech/v1/crm/member/agent_area/2/mappedByDistrictCode?contractId=1039' -H 'version: v2' \
-H 'authority: api.fenghong.tech' \
-H 'accept: application/json, text/plain, */*' \
-H 'authorization: Bearer 3799fd1e-74e2-3b92-a109-52b32d3b8026' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' \
-H 'content-type: application/json;charset=UTF-8' \
-H 'origin: https://www.fenghong.tech' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: https://www.fenghong.tech/' \
-H 'accept-language: zh-CN,zh;q=0.9' \
--data-binary '[440203]' \
--compressed
{"code":"0","message":"success","data":{"440203":null}}
方案二
使用rewrite break
, 直接请求proxy_pass
.
location ~ ^/v1/(.+)$ {
# add_header Access-Control-Allow-Origin *;
# add_header access-control-allow-methods "GET,PUT,POST,DELETE";
proxy_set_header Host $host;
# 添加http1.1声明和header中connection重写
proxy_http_version 1.1;
proxy_set_header Connection "";
#重写后, 立即break, 然后进入 proxy_pass
rewrite ^/v1/(.*)$ /__api/$1?$args break;
proxy_pass http://$group_staging_be_gateway_service;
}
请求示例
# louis @ LAPTOP-I2BJS52K in ~ [15:31:40]
$ curl 'https://api.fenghong.tech/v1/crm/member/agent_area/2/mappedByDistrictCode?contractId=1039' -H 'version: v2' \
-H 'authority: api.fenghong.tech' \
-H 'accept: application/json, text/plain, */*' \
-H 'authorization: Bearer 3799fd1e-74e2-3b92-a109-52b32d3b8026' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' \
-H 'content-type: application/json;charset=UTF-8' \
-H 'origin: https://www.fenghong.tech' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: https://www.fenghong.tech/' \
-H 'accept-language: zh-CN,zh;q=0.9' \
--data-binary '[440203]' \
--compressed
{"code":"0","message":"success","data":{"440203":null}}
显然. 方案二比方案一更加优化. 因此, 我选择方案二作为线上版本.
总结
这三种配置, 在不加入header version: v2
的情况下. 使用curl
访问都不会丢失 $1和$args
. 只有在加入header version: v2
的情况下, 原始配置会丢 $1和$args
. 方案一和方案二的配置不会丢失 $1和$args
.