发布了两年多的文章今天发现被CSDN关了, 理由如下:
现在迁移到看看是否有问题。
背景
最近做个项目需要暴露API给其他系统进行对接,为了方便第三方系统对接和不暴露后台服务,就不把具体的服务暴露出来。
所有的请求只有一个入口,如:http://xxxxxxx/api,就设计了如下的请求参数:
{
"BIZ_TYPE":"userInfo",
"REQ_TIME":"2018-06-06 10:08:00",
"REQ_ID":"20171121162959347258",
"AUTH_ID":"GZ_SIGNKEY",
"PARAM":{
"AREA":"12121",
"SOCIAL_CREDIT_CODE":"91110302100026582U",
"INDUSTRY_CODE":"JHDF43"
}
其中主要关注BIZ_TYPE,这个就用来动态选择服务的,Zuul拦截之后拿BIZ_TYPE去数据库里查询对应的service_id和URL,最后进行动态更换。数据库表如下
DROP TABLE IF EXISTS `trace_route`;
CREATE TABLE `trace_route` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`service_id` varchar(255) NOT NULL COMMENT '微服务id',
`biz_type` varchar(255) DEFAULT NULL COMMENT '数据类型',
`url` varchar(255) DEFAULT NULL COMMENT '请求地址',
`category` varchar(255) DEFAULT NULL COMMENT '所属类别',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`create_date` datetime DEFAULT NULL,
`update_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `BIZ_TYPE_UNIQUE` (`biz_type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
示例如下:
实现
实现ZuulFilter
这个不用多说,主要为了在Zuul进行路由之前动态修改service_id和和Url,代码示例如下:
Component
@Slf4j
public class AccessFilter extends ZuulFilter {
private ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private TraceRouteService traceRouteService;
@Autowired
private RouteLocator routeLocator;
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;//PreDecorationFilter会对服务做封装,故在它之后修改
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("请求url:{}",request.getRequestURI());
Object originalRequestPath = ctx.get(FilterConstants.REQUEST_URI_KEY);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
String str;
String wholeBody = "";
while((str = reader.readLine()) != null){
wholeBody += str;
}
Gson gson = new Gson();
RequestBodyHeader requestBodyHeader = gson.fromJson(jsonBody,RequestBodyHeader.class);
//重点:动态设置转发的服务
String bizType = requestBodyHeader.getBIZ_TYPE();
TraceRoute route = traceRouteService.findByBizType(bizType);
ctx.put(FilterConstants.SERVICE_ID_KEY, route.getServiceId());
if (route.getUrl().startsWith("/")){
ctx.put(FilterConstants.REQUEST_URI_KEY, route.getServiceId() + route.getUrl());
}else {
ctx.put(FilterConstants.REQUEST_URI_KEY, route.getServiceId() + "/" + route.getUrl());
}
ctx.put(FilterConstants.PROXY_KEY,route.getServiceId());
} catch (IOException e) {
e.printStackTrace();
doNotTransmit(ctx,Constant.UNKNOW_ERR_CODE,Constant.UNKNOW_ERR_MSG);
}
return null;
}
}
重点
上面的代码的重点部分如下:
String bizType = requestBodyHeader.getBIZ_TYPE();
TraceRoute route = traceRouteService.findByBizType(bizType);
ctx.put(FilterConstants.SERVICE_ID_KEY, route.getServiceId());
if (route.getUrl().startsWith("/")){
ctx.put(FilterConstants.REQUEST_URI_KEY, route.getServiceId() + route.getUrl());
}else {
ctx.put(FilterConstants.REQUEST_URI_KEY, route.getServiceId() + "/" + route.getUrl());
}
ctx.put(FilterConstants.PROXY_KEY,route.getServiceId());
很简单,通过bizType 去数据库查找对应的service_id和url,然后进行替换。
1.替换service_id
ctx.put(FilterConstants.PROXY_KEY,route.getServiceId());
2.替换请求的URL
ctx.put(FilterConstants.REQUEST_URI_KEY, route.getServiceId() + route.getUrl());
示例
发起请求 http://xxxxxx/api(用PostMan的选raw)
{
"BIZ_TYPE":"userInfoList",
"REQ_TIME":"2018-06-06 10:08:00",
"REQ_ID":"20171121162959347258",
"AUTH_ID":"GZ_SIGNKEY",
"PARAM":{
"AREA":"12121",
"SOCIAL_CREDIT_CODE":"91110302100026582U",
"INDUSTRY_CODE":"JHDF43"
}
通过解析BIZ_TYPE,获得service_id为user,url为user/list,替换service_id和URL后就会路由到具体的服务里面。
彩蛋
需要的yml文件中配置如下
zuul:
routes:
dataservice:
path: /api/**
serviceId: api