Spring Cloud Gateway 2020.x 版本无法注入 Feign 服务和 RestTemplate 的问题

本贴最后更新于 1207 天前,其中的信息可能已经事过境迁

问题描述

Spring Cloud Gateway 2020.x 版本无法注入 Feign 服务和 RestTemplate,注入要么报错,要么 IDEA 无法启动项目,一直转圈

问题相关代码

Feign 服务代码如下:

@FeignClient(value = "oauth")
@Component
public interface TokenService {


    /**
     * 检查令牌
     *
     * @param token 令牌
     * @return {@link String}
     */
    @RequestMapping("/oauth/check_token")
    String checkToken(@RequestParam String token);
}

Gateway 代码如下:

@Order(1)
@Component
public class Oauth2Filter implements GlobalFilter {

    @Autowired
    GatewayConfig gatewayConfig;   //这个是其他的bean 可以注入成功

    @Autowired
    TokenService tokenService; //这个是Feign服务的bean 无法注入,IDEA启动的时候也不报错,就是一只在启动循环当中

    @Autowired
    RestTemplate restTemplate; //同上
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
//         1 判断请求路径是否需要放行
        if (whiteList(path)) {
            chain.filter(exchange);
        }
        // 2 不是白名单的需要鉴权
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(token)) {
            // 存在token 校验token合法性
           String checkToken = tokenService.checkToken(token);
        } else {
            // 不存在 就报错返回啦
        }
        return chain.filter(exchange);
    }

    private boolean whiteList(String path) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<String> ignoreUrl = gatewayConfig.getIgnoreUrl();
        if (ignoreUrl.size() > 0) {
            return ignoreUrl.stream().anyMatch(n -> antPathMatcher.match(n, path));
        }
        return false;
    }

}

问题原因

网上有普遍说是 filter 是在 bean 创建前被创建的所无法注入,但是我这里只是特定的 bean 无法注入,所以我不认同这个说法。
强烈怀疑是 Gateway 2020 版的对 Feign 支持不友好导致的,然后就想到了,在 2020 版的 Spring Cloud Gateway 是移除了之前的 robbion,并且在官方的 issue 里面找到了相关的问题:Failed to invoke Feign and RestTemplate in Spring Cloud 2020's Gateway

处理方式(并没有处理方式)

尽管找到了原因,但是官方并没有给出一个解决方式,很难受!!!
发现 ServerWebExchange 可以获取到 ApplicationContext ,那么就用 ApplicationContext 获取 bean 试试!!! 发现可以获取到
但是获取到之后又出现了新的问题,

@Order(1)
@Component
public class Oauth2Filter implements GlobalFilter {

    @Autowired
    GatewayConfig gatewayConfig;

    TokenService tokenService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
       //  1 判断请求路径是否需要放行
        if (whiteList(path)) {
            chain.filter(exchange);
        }
        // 2 不是白名单的需要鉴权
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        try {
            if (null == tokenService) {
                ApplicationContext applicationContext = exchange.getApplicationContext();
                assert applicationContext != null;
                tokenService = applicationContext.getBean(TokenService.class);
            }
        } catch (BeansException e) {
            e.printStackTrace();
        }
        if (StringUtils.hasText(token)) {
            // 存在token 校验token合法性
            String checkToken = tokenService.checkToken(token);
        } else {
            // 不存在 就报错返回啦
        }
        return chain.filter(exchange);
    }

    private boolean whiteList(String path) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        List<String> ignoreUrl = gatewayConfig.getIgnoreUrl();
        if (ignoreUrl.size() > 0) {
            return ignoreUrl.stream().anyMatch(n -> antPathMatcher.match(n, path));
        }
        return false;
    }

}

上述完成之后,调用网关服务进行鉴权,发现第一次可以成功,后续同样的接口直接报错:

2021-06-01 16:30:28.770  WARN 26788 --- [     parallel-2] o.s.c.l.core.RoundRobinLoadBalancer      : No servers available for service: oauth
2021-06-01 16:30:28.771  WARN 26788 --- [oundedElastic-8] .s.c.o.l.FeignBlockingLoadBalancerClient : Load balancer does not contain an instance for the service oauth
2021-06-01 16:30:28.774 ERROR 26788 --- [oundedElastic-8] a.w.r.e.AbstractErrorWebExceptionHandler : [78a0055a-1]  500 Server Error for HTTP GET "/api/user/getName"

feign.FeignException$ServiceUnavailable: [503] during [GET] to [http://oauth/oauth/check_token?token=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJpZFwiOlwiMTQwMjMyMTYzNDgxMzkxMTA0XCIsXCJ1c2VybmFtZVwiOlwiZ3JpZFwiLFwibGVzc2VlSWRcIjpcIjEyOTc1NDgyMzc1NjM5ODU5MlwiLFwidW5pdElkXCI6XCIxMjQ3NTUxNTAyOTk3NDIyMDNcIixcIndlY2hhdFwiOnRydWUsXCJ1c2VyVHlwZVwiOjUsXCJhY2NvdW50Tm9uRXhwaXJlZFwiOnRydWUsXCJjcmVkZW50aWFsc05vbkV4cGlyZWRcIjp0cnVlLFwiYWNjb3VudE5vbkxvY2tlZFwiOnRydWV9IiwiZXhwIjoxNjE5MDkyNjc1LCJpYXQiOjE2MTkwNzgyNzV9.CgLRkP5dgfRrnWXqK2gbRDbUeXqq7hC9r7jl_Ge9GRI0Al1stXHTvg5emPydha4i36fM5QXoGIoJJYmUX1-wVA1] [TokenService#checkToken(String)]: [Load balancer does not contain an instance for the service oauth]
	at feign.FeignException.serverErrorStatus(FeignException.java:237) ~[feign-core-10.10.1.jar:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	|_ checkpoint ⇢ HTTP GET "/api/user/getName" [ExceptionHandlingWebHandler]

哎,头大,放弃了,本来是打算在 gateway 统一调用 oauth2 鉴权服务进行鉴权的,还是让具体的服务自己调用 oauth2 鉴权服务进行鉴权吧!!!

2021-09-02 处理方式

抱着不死心的态度,发现在官方的 issue 里面已经给出了处理方式,当时没有仔细阅读,结果走了很多弯路!!!

最终处理方式: 使用 webclient 替换 feign

代码如下:

//第一步 注入webclident   
    @Bean
    @LoadBalanced     // 如果不添加,无法通过服务名进行调用,只能通过ip调用
    public WebClient.Builder webBuilder(){
        return WebClient.builder();
    }

//第二步 在gateway当中注入
  @Autowired
    WebClient.Builder webBuilder;

//第三步 具体调用方式    这里的“lb”也可以换成http
   Mono<Object> toMono = webBuilder.baseUrl("lb://服务名/").build().get().uri(uriBuilder ->
                uriBuilder.path("/api/oauth/check_token").queryParam("参数名称", "参数值").build()
        ).header(HttpHeaders.AUTHORIZATION, token).exchangeToMono(clientResponse -> clientResponse.bodyToMono(Object.class));
	// 不调用subscribe或者block是不会调用服务的
        Object block = toMono.subscribe();

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    943 引用 • 1460 回帖 • 3 关注
  • Gateway
    5 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...