自定义路由服务版本
平台有多种路由策略,实现效果大同小异,这里以请求头pd为例
注意内容:并不是每个路由策略都会走,当其中一个符合,后面的路由策略是不会执行,执行顺序需要设置的order,数字越小优先级越高
代码解析 UrlHeaderProductEdgeDispatcher
UrlHeaderProductEdgeDispatcher继承serviceComb的AbstractEdgeDispatcher
注册方式,将自定义开发的dispatcher写到service配置文件中,这个文件名称是固定的,属于service Comb内置
- 启动项目时,会初始化dispatcher,然后执行init方法
- router.routeWithRegex(pattern) 加载路由配置文件,pattern是文件的名称
- failureHandler用于处理成功或者失败回调
- .handler添加handler,处理的顺序是按加载顺序执行的
- preCheck,用于判断请求头是否存在pd,如果不存在直接设置一个属性BYPASS_BODY_HANDLER,用于判断下个hanlder是否执行
- createBodyHandler 设置uploadConfig
- onRequest 方法解析
获取pd,创建EdgeInvocation,设置路由的服务、context、url和httpServerFilters集合
执行edgeInvocation.edgeInvoke(),做路由转发
- 添加路由服务版本,实现自定义服务版本路由
第一种方式: 在请求头中添加产品版本 pdv(产品version),然后设置版本号
第二种方式: 在配置文件中设置版本
详细代码如下,该案例属于demo演示级别代码,只为演示具体流程,代码中使用的UrlDetail用于快速演示,如果自己实现需要更换eneity,xml的格式也需要根据自身需求更改,这里演示的xml只是复制其他的xml,类的属性并不符合业务规范,请熟知
- UrlHeaderProductEdgeDispatcher
package com.je.router.dispatcher;
import com.je.common.base.util.StringUtil;
import com.je.router.GlobalRouterParams;
import com.je.router.model.urlRouter.UrlDetail;
import com.netflix.config.DynamicPropertyFactory;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher;
import org.apache.servicecomb.edge.core.EdgeInvocation;
import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 请求头路由器--产品
*/
public class UrlHeaderProductEdgeDispatcher extends AbstractEdgeDispatcher {
private static final Logger logger = LoggerFactory.getLogger(UrlHeaderProductEdgeDispatcher.class);
private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.headerRouter.product.enabled";
public static final String CONFIGURATION_ITEM = "HeaderRoutedProductConfigurationItem";
public UrlHeaderProductEdgeDispatcher() {
loadConfigurations();
}
@Override
public int getOrder() {
return 3;
}
@Override
public void init(Router router) {
String pattern = GlobalRouterParams.GLOBAL_HEADER_ROUTER.getPath();
router.routeWithRegex(pattern).failureHandler(this::onFailure)
.handler(this::preCheck)
.handler(createBodyHandler())
.handler(this::onRequest);
}
protected void preCheck(RoutingContext context) {
String microServiceName = context.request().getHeader("pd");
String url = context.request().path();
if (StringUtil.isEmpty(microServiceName) || StringUtil.isEmpty(url)) {
context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.TRUE);
context.next();
return;
}
UrlDetail urlDetail = findConfigurationItems(context.request().path());
context.put(CONFIGURATION_ITEM, urlDetail);
context.next();
}
private UrlDetail findConfigurationItems(String path) {
for (UrlDetail item : GlobalRouterParams.PD_VERSION_ROUTER.getUrlDetails()) {
if (item.getPattern().matcher(path).matches()) {
return item;
}
}
return null;
}
protected void onRequest(RoutingContext context) {
String microServiceName = context.request().getHeader("pd");
String url = context.request().path();
Boolean bypass = context.get(RestBodyHandler.BYPASS_BODY_HANDLER);
if (Boolean.TRUE.equals(bypass)) {
// clear flag
context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.FALSE);
context.next();
return;
}
EdgeInvocation edgeInvocation = createEdgeInvocation();
UrlDetail urlDetail = context.get(CONFIGURATION_ITEM);
if (urlDetail.getVersionRule() != null) {
edgeInvocation.setVersionRule(urlDetail.getVersionRule());
}
edgeInvocation.init(microServiceName, context, url, httpServerFilters);
logger.info("Matching with header dispatcher,the path {} pattern with {} is router to {} begin,the real path is {}! headeKey is {} headeValue is {}", context.request().path(), url, microServiceName);
edgeInvocation.edgeInvoke();
logger.info("Router url {} to {} complete", context.request().path(), microServiceName);
}
@Override
public boolean enabled() {
return DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, true).get();
}
private void loadConfigurations() {
GlobalRouterParams.loadPdVersionValues();
}
}
- PdVersionResourceLoader
package com.je.router.loader;
import com.google.common.base.Strings;
import com.je.router.model.pdversion.PdVersionRouter;
import com.je.router.model.urlRouter.UrlDetail;
import com.je.router.model.urlRouter.UrlRouter;
import com.je.xml.AbstractXmlConfigResourceLoader;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class PdVersionResourceLoader extends AbstractXmlConfigResourceLoader<PdVersionRouter> {
private static final Logger logger = LoggerFactory.getLogger(PdVersionResourceLoader.class);
private static final String DEFAULT_CONFIG_PAHT = "pdVersionRouter.xml";
/**
* pattern属性标签
*/
private static final String ROUTER_PATTERN_ATTRIBUTE_KEY = "pattern";
/**
* enable属性标签
*/
private static final String ROUTER_ENABLE_ATTRIBUTE_KEY = "enable";
/**
* suffix path属性
*/
private static final String ROUTER_PATTERN_URLDETAIL_PATH_KEY = "path";
/**
* real path属性
*/
private static final String ROUTER_PATTERN_URLDETAIL_REALPATH_KEY = "realPath";
/**
* suffix microServiceName属性
*/
private static final String ROUTER_PATTERN_URLDETAIL_MICROSERVICENAME_KEY = "microServiceName";
/**
* suffix versionRule属性
*/
private static final String ROUTER_PATTERN_URLDETAIL_VERSIONRULE_KEY = "versionRule";
/**
* suffix prefixSegmentCount属性
*/
private static final String ROUTER_PATTERN_URLDETAIL_PREFIXSEGEMENTCOUNT_KEY = "prefixSegmentCount";
public PdVersionResourceLoader() {
this(DEFAULT_CONFIG_PAHT);
}
public PdVersionResourceLoader(String resourcePath) {
super(resourcePath);
}
@Override
public PdVersionRouter parse(Element rootElement) {
//获取pattern配置
String pattern = rootElement.attributeValue(ROUTER_PATTERN_ATTRIBUTE_KEY);
String enable = rootElement.attributeValue(ROUTER_ENABLE_ATTRIBUTE_KEY);
PdVersionRouter pdVersionRouter;
if(Strings.isNullOrEmpty(pattern)){
pdVersionRouter = new PdVersionRouter();
}else{
pdVersionRouter = new PdVersionRouter(pattern);
}
if(!Strings.isNullOrEmpty(enable)){
if("true".equals(enable)){
pdVersionRouter.setEnable(true);
}else{
pdVersionRouter.setEnable(false);
}
}
List<Element> elements = rootElement.elements();
if(elements == null || elements.isEmpty()){
return pdVersionRouter;
}
for (Element eachElement: elements) {
pdVersionRouter.addUrl(parseUrlDetail(eachElement));
}
return pdVersionRouter;
}
private UrlDetail parseUrlDetail(Element urlDetailElement){
UrlDetail urlDetail = new UrlDetail();
String path = urlDetailElement.attributeValue(ROUTER_PATTERN_URLDETAIL_PATH_KEY);
String realPath = urlDetailElement.attributeValue(ROUTER_PATTERN_URLDETAIL_REALPATH_KEY);
String microServiceName = urlDetailElement.attributeValue(ROUTER_PATTERN_URLDETAIL_MICROSERVICENAME_KEY);
String versionRule = urlDetailElement.attributeValue(ROUTER_PATTERN_URLDETAIL_VERSIONRULE_KEY);
String prefixSegementCount = urlDetailElement.attributeValue(ROUTER_PATTERN_URLDETAIL_PREFIXSEGEMENTCOUNT_KEY);
if(Strings.isNullOrEmpty(path) || Strings.isNullOrEmpty(microServiceName)
|| Strings.isNullOrEmpty(versionRule) || Strings.isNullOrEmpty(prefixSegementCount)){
logger.error("Parse xml configuration urlRouter.xml error!");
System.exit(0);
}
urlDetail.setPath(path);
urlDetail.setRealPath(realPath);
urlDetail.setMicroServiceName(microServiceName);
urlDetail.setVersionRule(versionRule);
urlDetail.setPrefixSegmentCount(Integer.valueOf(prefixSegementCount));
return urlDetail;
}
}
- GlobalRouterParams
package com.je.router;
import com.je.router.loader.*;
import com.je.router.model.headerRouter.HeaderRouter;
import com.je.router.model.paramRouter.ParamRouter;
import com.je.router.model.paramValue.ConfigParamValues;
import com.je.router.model.pdversion.PdVersionRouter;
import com.je.router.model.urlRouter.UrlRouter;
import com.je.xml.ConfigResourceLoader;
import java.util.HashMap;
import java.util.Map;
/**
* @program: jecloud-gateway
* @author: LIULJ
* @create: 2021/7/4
* @description:
*/
public class GlobalRouterParams {
/**
* 缓存同步KEY
*/
public static final String GLOBAL_ROUTER_SYNC_KEY = "routerSyncCache";
/**
* 同步KEY存活时间,默认为60s
*/
public static final Long GLOBAL_ROUTER_SYNC_EXPIRE_TIME = 60L;
/**
* 资源参数加载器
*/
private static final ConfigResourceLoader<Map<String, ConfigParamValues>> configParamValuesResourceLoader = new ConfigParamValuesResourceLoader();
/**
* 请求头路由加载器
*/
private static final ConfigResourceLoader<HeaderRouter> xmlUrlHeaderConfigResourceLoader = new UrlHeaderRouterResourceLoader();
/**
* 请求参数路由加载器
*/
private static final ConfigResourceLoader<ParamRouter> xmlUrlParamsConfigResourceLoader = new UrlParamRouterResourceLoader();
/**
* URL请求参数路由加载器
*/
private static final ConfigResourceLoader<UrlRouter> xmlUrlConfigResourceLoader = new UrlRouterResourceLoader();
/**
* pd
*/
private static final ConfigResourceLoader<PdVersionRouter> pdVersionResourceLoader = new PdVersionResourceLoader();
/**
* 全局请求头路由
*/
public static HeaderRouter GLOBAL_HEADER_ROUTER;
/**
* 全局消息参数路由
*/
public static ParamRouter GLOBAL_PARAM_ROUTER;
/**
* 全局Url路由
*/
public static UrlRouter GLOBAL_URL_ROUTER;
/**
* pd参数版本路由
*/
public static PdVersionRouter PD_VERSION_ROUTER;
/**
* 全局配置化参数,用于路由
*/
public static Map<String, ConfigParamValues> GLOBAL_CONFIG_PARAM_VALUES = new HashMap<>();
public static void loadConfigValues() {
GLOBAL_CONFIG_PARAM_VALUES = configParamValuesResourceLoader.load();
}
public static void loadUrlHeaderValues() {
GLOBAL_HEADER_ROUTER = xmlUrlHeaderConfigResourceLoader.load();
}
public static void loadUrlParamValues() {
GLOBAL_PARAM_ROUTER = xmlUrlParamsConfigResourceLoader.load();
}
public static void loadUrlValues() {
GLOBAL_URL_ROUTER = xmlUrlConfigResourceLoader.load();
}
public static void loadPdVersionValues() {
PD_VERSION_ROUTER = pdVersionResourceLoader.load();
}
public static void reload() {
loadConfigValues();
loadUrlHeaderValues();
loadUrlParamValues();
loadUrlValues();
}
}
- PdVersionRouter
```java
package com.je.router.model.pdversion;
import com.je.router.model.urlRouter.UrlDetail;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* PdVersionRouter配置实体
*/
public class PdVersionRouter implements Serializable {
private String path;
private Pattern pattern = Pattern.compile("/(.*)");
private boolean enable = false;
private List<UrlDetail> urlDetails = new ArrayList<>();
public PdVersionRouter() {
}
public PdVersionRouter(String pattern) {
this.path = pattern;
this.pattern = Pattern.compile(pattern);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
this.pattern = Pattern.compile(path);
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public Pattern getPattern() {
return pattern;
}
public List<UrlDetail> getUrlDetails() {
return urlDetails;
}
public void setUrlDetails(List<UrlDetail> urlDetails) {
this.urlDetails = urlDetails;
}
public void addUrl(UrlDetail urlDetail){
this.urlDetails.add(urlDetail);
}
public void removeUrl(UrlDetail urlDetail){
this.urlDetails.remove(urlDetail);
}
}
- pdVersionRouter.xml
**microServiceName属性是没用的,这里快速演示,直接复制其他的xml修改的请熟知**
```java
<?xml version="1.0" encoding="utf-8"?>
<!-- 路由配置文件 -->
<!-- path 为请求路径,/* 为匹配 -->
<!-- microServiceName 服务的标识 -->
<!-- versionRule 服务的版本 0.0.0+为全量 -->
<!-- prefixSegmentCount 默认为0 -->
<pdVsersionRouter pattern="/(.*)" enable="true">
<url path="/je/meta/v1/.*" microServiceName="meta" realPath="" versionRule="1.0.0+" prefixSegmentCount="0"/>
<url path="/je/meta/v2/.*" microServiceName="meta" realPath="" versionRule="2.0.0+" prefixSegmentCount="0"/>
</pdVsersionRouter>
最后编辑: 于春辉 文档更新时间: 2024-11-08 17:00 作者:于春辉