公司使用Spring Cloud做了一个微服务的工程建设管理系统。微服务框架的好处大家都是知道的,这里就不再啰嗦。

        项目越来越多,除了一些基础服务通用性较强外,发现很多业务服务都是貌似而已,同样的质量管理,项目A需要这么处理,项目B需要那么处理。我们传统搞法,肯定是在业务代码里加if-else。但是太多定制后,到处都是这样的代码,一个服务可能很多项目组都在改,这个结果可能导致相互影响,失去了微服务解耦的初衷。

        那现在我们把各个业务服务的项目定制版本单独发布呢?貌似可以,但这里又出现另外一个问题:有个公用的业务服务A,需要对接多个项目定制的业务服务B\C\D。通常微服务之间的接口调用都是通过FeignClient来实现,这个技术经过了大量的实践,肯定没有啥问题。但我们不是A到B的问题,我们需要在A到B/C/D的时候,根据预先的配置,让A知道应该是调A还是B还是C。

        使用FeignClient的人都知道Feign的用法,@FeignClient(name="provider", path="test")。服务名通常是写死的,那怎么来实现name的动态调整呢。

        这几天看了不少人的想法:

        一种是通过Feign.builder().encoder(encoder).decoder(decoder).client(feignClient).contract(contract).target(ISpecailRouterClient.class, url)这种方式来调整目标,参考https://baijiahao.baidu.com/s?id=1633128626497116063&wfr=spider&for=pc

        一种是直接注入Feign的实现,类似HystrixFeign,参考https://blog.csdn.net/zhouhao88410234/article/details/99982043,看着也能实现,但过于复杂。

        看了半天,还是决定自己从Feign的源码看起,看怎么搞更加方便。

 

 

        看到RequestInterceptor,眼前一亮,对啊,拦截器这么好用的东西为什么不用呢。仔细一看接口中的RequestTemplate参数,一切事情就很简单了。下面列几个方法,大家就知道怎么玩了。

        1、RequestTemplate.feignTarget().name()获得FeignClient注解中的name

        2、RequestTemplate.queries()获取请求参数

        3、RequestTemplate.url()获取访问的全路径地址(含参数)

        4、RequestTemplate.path()获取访问的全路径地址(不含参数)

        5、RequestTemplate.target(String target)设置服务的地址(全路径),或RequestTemplate feignTarget(Target<?> feignTarget)

 

        于是自建一个拦截器,即可实现所有FeignClient的请求拦截,并根据需要去改变目标服务。如下:

@Service

public class MyInterceptor implements RequestInterceptor {

    private static Map<String, String> routerMap = new HashMap<String, String>();

    public void apply(RequestTemplate template) {

    template.header("Content-Type", "application/json");

    String serviceName = template.feignTarget().name();

    Map<String, Collection<String>> map = template.queries();

    //根据请求参数进行路由获取,这个配置可以自己决定放在什么地方

    String target = routerMap.get(map.get("projectId").toArray()[0]+":"+serviceName);

    if (target != null) {

        template.target(target);

    }

    System.out.println("这是自定义请求拦截器,"+template.path());

}

上述是对微服务中的所有Feign请求进行统一拦截,因为他注册成了全局的拦截器。如果只需要对某些FeignClient生效,可以见到改造一下

1、先去掉@Service

2、创建一个Config类

public class MyFeignConfig {

    @Bean

    public RequestInterceptor myRequestInterceptor(){

        return new MyInterceptor();

    }

}

3、修改FeignClient的声明:

@FeignClient(name="provider", path="test", configuration = MyFeignConfig.class)

相信不用多说,大家都清楚了。

 

总结一下:要想用的好,还是要多看源码。

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐