自定义路由服务版本

平台有多种路由策略,实现效果大同小异,这里以请求头pd为例

注意内容:并不是每个路由策略都会走,当其中一个符合,后面的路由策略是不会执行,执行顺序需要设置的order,数字越小优先级越高

代码解析 UrlHeaderProductEdgeDispatcher

  1. UrlHeaderProductEdgeDispatcher继承serviceComb的AbstractEdgeDispatcher

  2. 注册方式,将自定义开发的dispatcher写到service配置文件中,这个文件名称是固定的,属于service Comb内置

  1. 启动项目时,会初始化dispatcher,然后执行init方法
  • router.routeWithRegex(pattern) 加载路由配置文件,pattern是文件的名称
  • failureHandler用于处理成功或者失败回调
  • .handler添加handler,处理的顺序是按加载顺序执行的
  • preCheck,用于判断请求头是否存在pd,如果不存在直接设置一个属性BYPASS_BODY_HANDLER,用于判断下个hanlder是否执行
  • createBodyHandler 设置uploadConfig

  1. onRequest 方法解析

获取pd,创建EdgeInvocation,设置路由的服务、context、url和httpServerFilters集合

执行edgeInvocation.edgeInvoke(),做路由转发

  1. 添加路由服务版本,实现自定义服务版本路由

第一种方式: 在请求头中添加产品版本 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   作者:于春辉