基于SpringMVC的API灰度方案

ba223a58a6cbd25376faee47a7eb7aac.jpeg

一、背景

在微服务重构时,我们常遇到这个业务场景:同样是/api/test,我们实现了新逻辑和老逻辑,然后根据定制的灰度策略,通常灰度API和老API两者都需要支持用户使用。

那么是否有比较好解决方案,协助我们完成同名同方法同参数列表的API灰度动态路由的方案呢?

我们就基于SpringMVC,通过对底层RequestMappingInfo的参数定制化,实现了methodHandler的动态路由决策,从而达到API灰度动态路由目的。

7154a1674c3c43149ed152909378e6cb.png

二、实现原理

我们总的来说,干了两件事情:

339f3833b483dccb573a22d0a5a76b8b.png

  • 第一件事

    服务启动时,在initMethodHandler执行时,对RequestMappingInfo初始化时,就将灰度决策器RouterDecisionMaker,以@PathRouterDecisionMaker决策器注解的形式,预加载到customInfo里

  • 第二件事

    服务运行期,在路由匹配器PathMatcher里,会解析RequestMappingInfo,最终执行灰度决策器RouterDecisionMaker,并挑选最合适的RequestMappingInfo映射的methodHandler去执行响应逻辑

三、实现方案

1、定义决策器注解:@PathRouterDecisionMaker

@Target({ElementType.TYPE, ElementType.PACKAGE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PathRouterDecisionMaker {
    Class<? extends RouterDecisionMaker> decision();
    String resourceCondition() default "";
    int order() default 0;
}

分析:

  • decision():决策属性,指向路由决策器类,即RouterDecisionMaker接口

注解(Annotation):仅提供附加元数据支持,并 不能实现任何操作,需要另外的 Scanner 根据元数据执行相应操作。

2、路由决策器:RouterDecisionMaker接口

RouterDecisionMaker接口,定义了决策器的匹配策略方法

public interface RouterDecisionMaker {


    /**
     * 路由决策器的最终决策方法
     * @param pathPartRequest
     * @return 匹配返回的资源类型
     */
    boolean matches(PathPartRequest pathPartRequest);
}

2.1 灰度决策器-ApiGrayDecisionMaker(返回true)

@Component(value = "ApiGrayDecisionMaker")
public class ApiGrayDecisionMaker implements RouterDecisionMaker {


    @Override
    public boolean matches(PathPartRequest pathPartRequest) {
        return Boolean.TRUE;
    }


}

代码分析:

  • 为了方便实践,我们写死一个决策匹配策略为TRUE

2.2 灰度决策器-ApiNotGrayDecisionMaker(返回false)

@Component("ApiNotGrayDecisionMaker")
public class ApiNotGrayDecisionMaker implements RouterDecisionMaker {


    /**
     * 取反,跟 ApiGrayDecision#matches 互斥
     * @param pathPartRequest
     * @return
     */
    @Override
    public boolean matches(PathPartRequest pathPartRequest) {
        return Boolean.FALSE;
    }
}

代码分析:

  • 为了方便实践,我们写死一个决策匹配策略为FALSE

2.3 决策信息

RouterPathRequest,提供数据给决策器

public class RouterPathRequest {


    private final String pattern;
    private final String url;
    private final Map<String, String> pathVariables;
    private final RouterPatternKey routerPatternKey;
    private final String routeCondition;
    private final HttpServletRequest request;


    public RouterPathRequest(HttpServletRequest request, String pattern, String url, Map<String, String> pathVariables,
            RouterPatternKey routerPatternKey, String routeCondition) {
        this.request = request;
        this.pattern = pattern;
        this.pathVariables = pathVariables;
        this.url = url;
        this.routerPatternKey = routerPatternKey;
        this.routeCondition = routeCondition;
    }


    public static RouterPathRequest build(HttpServletRequest request, String pattern, String url,
            Map<String, String> pathVariables, RouterPatternKey routerPatternKey, String routeCondition) {
        return new RouterPathRequest(request, pattern, url, pathVariables, routerPatternKey, routeCondition);
    }
    //...getter&setter
}

代码分析:

  • 实际是对HttpServletRequest的二次封装,并提取了一些常用上下文数据到属性

2.4 决策注解探测器

WebRouterDecisionMakerDetection

/**
 *  PathRouterDecisionMaker注解提取器
 *   - 从方法注释提取注解
 */
public class WebRouterDecisionMakerDetection {
    public PathRouterDecisionMaker detect(Method handlerMethod) {
        if (Objects.isNull(handlerMethod)) {
            return null;
        }
        return AnnotatedElementUtils.findMergedAnnotation(handlerMethod, PathRouterDecisionMaker.class);
    }
}

2.5 自定义-路由匹配策略

WebRouterDecisionCondition 

继承了 AbstractRequestCondition,会在创建RequestMappingInfo的填入customCondition条件时,被回调使用。

  • 抽象类AbstractRequestCondition实现了 RequestConditi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后台技术汇

对你的帮助,是对我的最好鼓励。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值