最近手头的项目进行了重构,改动比较大,直接上线的话风险较大,于是我采用了灰度上线的方式来将风险最小化。
我们系统包括自己的门户页面,也作为中台来给各个业务方调用,我们灰度上线的话,要兼容各个业务系统。
业务系统调用我们接口是前缀是特殊的,可以和我们的门户调用区分开,且业务系统调用所有接口都是有签名的,每个接口的参数格式都是一样的,会含有每个业务系统的appId
之前流量是经过nginx直接打到我们的服务商,这次在nginx和我们系统之间添加了网关,作为我们灰度的核心--流量分配
关于zuul网关可以参考https://www.cnblogs.com/jing99/p/11696192.html
核心流量控制代码如下:
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import java.net.URL;
import java.util.List;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.kuaishou.is.ea.clm.gateway.model.bo.ESignGrayConfigBO;
import com.kuaishou.is.fastjson.replace.JSON;
import com.kuaishou.is.fastjson.replace.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class RouteFilter extends ZuulFilter {
@Value("${esign.gray}")
private String esignGray;//流量配置
@Value("${esign.oldUrl}")
private String oldUrl;
@Value("${esign.newUrl}")
private String newUrl;
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER + 1;
}
public String filterType() {
return PRE_TYPE;
}
@Override
public boolean shouldFilter() {
return true;
}
@SneakyThrows
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String url = ctx.getRequest().getRequestURI();
if (!url.contains("/esignature/api/external")) {
return null;
}
if (url.contains("/esignature/api/external/timing/task")) {
ctx.setRouteHost(new URL(newUrl));
return null;
}
HttpServletRequest request = ctx.getRequest();
boolean matched = false;
if (!ctx.isChunkedRequestBody()) {
ServletInputStream inp = null;
try {
inp = ctx.getRequest().getInputStream();
String body = null;
List<ESignGrayConfigBO> eSignGrayConfigBOList = JSON.parseArray(esignGray, ESignGrayConfigBO.class);
if (inp != null) {
body = IOUtils.toString(inp);
JSONObject jsonObject = JSON.parseObject(body);
Integer appId = -1;
try {
appId = (Integer) jsonObject.get("appId");
} catch (Exception e) {
log.error("获取appId error,path:{}", request.getRequestURI());
}
String path = request.getRequestURI();
int random = (int) (Math.random() * 100);
for (ESignGrayConfigBO eSignGrayConfigBO : eSignGrayConfigBOList) {
if (random < eSignGrayConfigBO.getPersent()
&& (ObjectUtils.equals(appId, -1) || ObjectUtils.equals(appId, eSignGrayConfigBO.getAppId()))
&& (path.endsWith(eSignGrayConfigBO.getPath()) || StringUtils.equals(eSignGrayConfigBO.getPath(), ""))) {
matched = true;
ctx.setRouteHost(new URL(newUrl));
log.info("matched :{}", body);
}
}
}
} catch (Exception e) {
log.error("route error, e:{}", e.getMessage(), e);
ctx.setRouteHost(new URL(oldUrl));
}
}
if (!matched) {
ctx.setRouteHost(new URL(oldUrl));
}
return null;
}
}
|