SpringCloud 2021.0.1 SpringCloudGateway 3.1.1新版中GlobalFilter使用OpenFeign失败(503)的问题

SpringCloud 2021.0.1 SpringCloudGateway 3.1.1新版中GlobalFilter使用OpenFeign失败的问题

简单说下好了,这个问题其实在springCloud移除ribbon之后就出现的
之前我用的版本是SpringCloud Hoxton.SR8,具体这个版本里还有没有ribbon也没有去看了,反正这会在gateway里使用feign是没有任何问题的
也可能依赖中单独添加了ribbon没注意,这不因为springCloudGateway爆出来的漏洞嘛(某台生产服务器已经被黑过,
直接往redis里搞了一条gateway的路由信息,导致整个应用进不去,还好这个大哥只是试试水,别的并没有干什么),所以无奈必须升级

各依赖版本

SpringCloud : 2021.0.1 对应gateway是3.1.1
alibabaNacos: 2021.1
springBoot: 2.6.6 (这个也是因为漏洞问题,虽然没说非war包会不会咋样,一并升了吧,之前用的2.5.1版本并不低,升级不算太困难,如果是2.3.0以下的小伙伴可就有的折腾了)

这里只说明升级之后SpringCloudGateway的使用openFeign(3.1.1)的问题
首先,可以完全确认,上面版本中无论是springCloud还是nacos,都已经没有集成任何默认的负载均衡组件,所以升级后第一件事就是添加一个负载均衡组件,否则启动不了或者是无法路由服务,ribbon已被抛弃,直接引入spring-cloud-loadbalancer即可(每个服务都要依赖)

		<dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-loadbalancerartifactId>
             <version>3.1.1version>
        dependency>

然后就是openFeign在网关过滤器中的使用问题,我想应该大多数人用gateway处理鉴权应该都是实现的GlobalFilter这个接口来做的过滤器吧,问题就出在这里了,其他版本我不清楚,没有一一尝试了

但是3.1.1这个版本,无论你用webClient也好,还是把feign单独用异步Future包裹一道也罢你不管作何处理,在GlobalFilter的实现类中,要么一直503,要么每次启动第一次可以正常使用feign调取到其他服务数据,后面继续503,没有任何出路,不用费心研究添加Decoder的bean,改写服务接口,@Autowired的时候加@Lazy,甚至使用冷门的reactive-feign组件,以上现象并不会得到任何改变。虽然官方的git的issure里好像是提过一下webClient可以解决第一次可用,后续不可用的问题,但是亲身实践,并不可行,别说用webClient取代feign去调用服务,就算你服务也是web-flux的,都不行。

唯一解法只有一个,修改实现接口,改成WebFilter,这个接口和GlobalFilter基本一致,除了参数有一个不一样,还有ServerWebExchange中某些gateway封装的请求头不及GlobalFilter全面以外,其他暂时没有发现太大区别

// 这是GlobalFiter的filter方法
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
// 这是WebFilter的filter方法
Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);
// 基本没什么变化,内部代码也几乎不用更改,除了部分固定请求头,比如获取一些固定请求头在WebFilter里可能不存在
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
// 这两个可能比较常用一点,这两个反正是获取不到的,没有挨个试过,暂且理解为ServerWebExchangeUtils里的几个常量请求头都没法获取吧
// 毕竟GlobalFiter和WebFilter是异步两条线程在执行的,这些请求头多数是默认GlobalFilter里定义的

这里改了之后,也还是不能直接@Autowired一个Feign来使用,倒是没出现其他文章里说的无法启动网关之类的问题,但是调用的时候会直接报下图异常
在这里插入图片描述
所以还差一步,把需要使用的feign,包裹一层Future。

@Component
public class FeignHolder {

    @Autowired
    @Lazy
    private AuthFeign authFeign;
    @Async
    public Future<AuthenDTO> isLogin(String token){
        AuthenDTO dto = FeignReturnDataGzip.Unzip(authFeign.isLogin(token), AuthenDTO.class);
        return new AsyncResult<>(dto);
    }

    @Async
    public Future<CurrentUser> getLoginUserInfo(String token){
        CurrentUser currentUser = FeignReturnDataGzip.Unzip(authFeign.getLoginUserInfo(token), CurrentUser.class);
        return new AsyncResult<>(currentUser);
    }
}

然后可以在网关过滤器中直接注入FeignHolder

@Component
public class GatewayAuthorizationFilter implements WebFilter, Ordered {
	
	@Autowired
    private FeignHolder feignHolder;
    
	@Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    	...
    	Future<AuthenDTO> authFuture = feignHolder.isLogin(token);
        AuthenDTO authenDTO;
        try {
            authenDTO = authFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            authenDTO = new AuthenDTO();
            log.error("[鉴权中心]调用鉴权中心Feign失败,失败原因:{},失败信息:{}", e.getClass().getSimpleName(), e.getMessage());
        }
    	...
    }
}

这样来处理之后,即可正常使用,网关建议都单独添加一个类似FeignHolder的配置类来统一处理openFeign调用,当然也可以直接使用
com.playtika.reactivefeign这个组件(国内外范围内参考资料都超级少,官方git说明也不是很全面,上手难度很高.),这个版本的网关中凡是使用openFeign的地方,必须要异步处理,不能同步调用feign接口,否则都会抛出异常

java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, 
which is not supported in thread xxxx-xxxx-xxxx

另外不要相信使用SpringApplicationContext来获取bean/service就能不使用异步,只要你请求了其他服务,并且使用的是spring-cloud-loadbalancer作负载均衡,就必须异步调用,使用ribbon情况会不会有所不同就说不准了,有兴趣的自己试下吧

你可能感兴趣的:(spring,java)