开发环境建设 特定服务feign优雅转发test环境

文章描述了一种在开发和本地环境中优雅地重定向Feign服务调用到特定环境的方法,通过自定义拦截器和客户端包装,实现了对LoadBalancerFeignClient和OkHttpClient的控制,避免了直接修改请求URL导致的问题。

目录

一、背景

二、分析

三、实现

1 组成

2 客户端

3 拦截器

4 配置类

5 为什么不直接在客户端execute方法中修改定向地址?


一、背景

公司提供的环境资源比较充足,分dev、test、uat、生产,以及最基本的本地idea。有这样一种场景,隔壁合作团队(后称M团队)的服务比较机密,服务在内网,并做了ip访问限制,除生产环境外,只允许test、uat访问,dev、本地则不允许接通。

涉及到M团队服务调用的内容单独抽了一个M服务模块。每次本地调试涉及到M服务调用时,只能手动改FeignClient注解配置url将请求转发到test环境的M服务,比较麻烦,还有误提交该操作配置的风险。Dev环境与前端或其他系统联调时,若涉及M服务调用则更是行不通,除非提交本地FeignClient注解的临时配置,这显然不合适。

二、分析

比较优雅的方式是配置本地和dev环境M服务的调用自动转发test环境,而其他环境这套配置完全不生效。

网上现有的方案有两种。第一种是配置feign自带的拦截器,修改定向的目标地址,这种方案对于url类型的访问是有效的,但是使用注册服务名对注册服务请求会失效。第二种是自定义实现Feign客户端,在请求执行方法中手动处理转发请求到其他http客户端进行请求,这种方法是有效的,唯一不足的就是自定义处理逻辑这种方式还不够优雅。

查看feign请求拦截器RequestInterceptor的调用链,发现在构造请求客户端请求对象Request时,先将拦截器执行了一遍,然后再构造请求对象:

Target.apply方法是直接使用拦截对象RequestTemplate对象的request方法构造Request对象,内部url构造用target参数做域名,所以RequestTemplate.target是主要拦截改造的参数:

 

 简单编写拦截逻辑,替换RequestTemplate.target:

 请求w服务报错:“Load balancer does not have available server for client: XXX.XXX.com”,明显走了注册列表负载请求,非http请求,所以单纯拦替换RequestTemplate.target是不可行的。

打断点调试发现,Feign执行注册列表服务请求和url的http请求使用的客户端是不同的,分别是LoadBalancerFeignClient和OkHttpClient,并且FeignClient接口是在bean加载阶段就绑定好执行客户端了。所以请求原注册名服务请求在feign拦截修改定向目标地址后,请求依旧是走LoadBalancerFeignClient客户端,导致请求失败。

 三、实现

1 组成

为实现优雅转发,需要的类组成有三个:支持LoadBalancerFeignClient和OkHttpClient双向执行的客户端、重定向请求的Feign拦截器、可以指定生效环境的重定向组件Bean配置类。

2 客户端

对LoadBalancerFeignClient和OkHttpClient两个客户端进行包装,简单通过判断url是否包含注册服务名决定走哪个客户端:

@Slf4j
public class DevFeignRedirectionClient implements Client {

    /**
     * 注册列表负载客户端
     */
    private final LoadBalancerFeignClient loadBalancerFeignClient;

    /**
     * http请求客户端
     */
    private final OkHttpClient okHttpClient;

    public DevFeignRedirectionClient(LoadBalancerFeignClient loadBalancerFeignClient, OkHttpClient okHttpClient) {
        this.loadBalancerFeignClient = loadBalancerFeignClient;
        this.okHttpClient = okHttpClient;
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        String serverName = request.requestTemplate().feignTarget().name();
        String url = request.requestTemplate().url();
        if (url.contains(serverName)) {
            return loadBalancerFeignClient.execute(request, options);
        }
        return okHttpClient.execute(request, options);
    }
}

这种方式最大程度保留了原有服务组件和保证了原有服务请求走向。

3 拦截器

具体重定向逻辑放拦截器处理:

@Slf4j
public class DevFeignRedirectionInterceptor implements RequestInterceptor {

    /**
     * 重定向服务:定向域名
     */
    private final static Map<String, String> REDIRECTION_SERVER = new HashMap<>();

    static {
        REDIRECTION_SERVER.put("M-server", "https://xxx.xxx.com/");
    }

    public DevFeignRedirectionInterceptor(Map<String, String> redirectionServer) {
        if (MapUtils.isNotEmpty(redirectionServer)) {
            REDIRECTION_SERVER.putAll(redirectionServer);
        }
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        String serverName = requestTemplate.feignTarget().name();
        if (!REDIRECTION_SERVER.containsKey(serverName)) {
            return;
        }

        String sourceDomain = requestTemplate.feignTarget().url();
        String targetDomain = REDIRECTION_SERVER.get(serverName);
        requestTemplate.target(targetDomain);
        log.info("原请求域名:{},重定向域名:{},定向地址:{}", sourceDomain, targetDomain, requestTemplate.url());
    }
}

4 配置类

Profile指定生效环境:

@Profile({"local", "dev"})
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
@EnableConfigurationProperties(DevFeignRedirectionProperties.class)
public class DevFeignRedirectionConfiguration {

    @Bean
    public Client getClient(CachingSpringLoadBalancerFactory cachingFactory,
                            SpringClientFactory clientFactory,
                            okhttp3.OkHttpClient client) {
        return new DevFeignRedirectionClient(
                new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory),
                new feign.okhttp.OkHttpClient(client)
        );
    }

    @Bean
    public DevFeignRedirectionInterceptor getDevFeignRedirectionInterceptor(@Autowired DevFeignRedirectionProperties properties) {
        return new DevFeignRedirectionInterceptor(properties.getRedirectionServer());
    }

}

5 为什么不直接在客户端execute方法中修改定向地址?

请求对象Request中定向地址参数url不支持修改。

补充说明:

open feign 13已简化,不用这么复杂配置。一个拦截器就够了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大迪吃小迪

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值