惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
D
DataBreaches.Net
D
Docker
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
J
Java Code Geeks
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
Martin Fowler
Martin Fowler
U
Unit 42
Engineering at Meta
Engineering at Meta
IT之家
IT之家
Vercel News
Vercel News
B
Blog RSS Feed
人人都是产品经理
人人都是产品经理
博客园 - Franky
博客园 - 【当耐特】
Stack Overflow Blog
Stack Overflow Blog
G
Google Developers Blog
MongoDB | Blog
MongoDB | Blog

博客园 - nuccch

在Cursor中读取飞书文档 使用GIMP去除水印的有效方法 如何基于VSCode打造Java开发环境 在IDEA中配置注释模板 在Windows中使用Linux系统 树形层级结构的数据库表设计方案 如何理解和认识设计模式 申请Let's Encrypt免费HTTPS证书的方法 为GIT仓库项目设置独立配置参数 DBeaver设置不断开连接 构建工具Gradle入门实践 如何在Maven中排除依赖传递 87键键盘的数字键对应快捷键含义 关于Java JSON库的选择 解决mybatis批量更新慢问题 Spring Boot框架中在Controller方法里获取Request和Response对象的2种方式 解读Spring Boot框架中不同位置抛出异常的处理流程 探究Spring Boot框架中访问不存在的接口时触发对error路径的访问 Spring Cloud工程中使用Nacos配置中心的2种方式 Swagger开启账号验证访问
解决Spring Cloud Gateway中使用CompletableFuture.supplyAsy...
nuccch · 2025-12-03 · via 博客园 - nuccch

报错背景描述

组件版本信息:

  • Spring Cloud:2021.0.5
  • Spring Cloud Alibaba:2021.0.5.0
  • Nacos:2.2.3

项目采用基于Spring Cloud Alibaba + Nacos的微服务架构,生产环境部署时服务部署到阿里云ACK容器集群中,并使用阿里云MSE云原生网关作为接入层。
出于成本考虑,在测试环境并未直接采购阿里云ACK集群和MSE云原生网关,所以使用Spring Cloud Gateway作为测试环境的网关服务。生产环境部署时,在MSE云原生网关处对请求接口做了全局鉴权,因此也需要在Spring Cloud Gateway服务做相同的事情,如下所示:
接口请求流程

当网关接收到客户端请求服务A的接口时,先要到鉴权服务校验请求参数是否正确(如:对于需要登录才能访问的接口,必须携带正确的token参数)。如果鉴权失败,在网关处直接将错误信息响应给客户端;只有鉴权成功后,网关再将请求转发给服务A,并将服务A的响应结果返回给客户端。

由于网关和服务A都注册到了同一个Nacos注册中心,因此网关可以通过Feign框架发起基于HTTP协议的RPC调用,添加如下依赖配置:

<!--配置feign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!-- 为Feign提供负载均衡 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

另外,在发起对鉴权服务的HTTP接口调用时,还需要使用“异步转同步”的思路:

// 异步转同步:调用feign服务接口
RpcResult resultVO = this.syncInvoke(CompletableFuture.supplyAsync(() -> authRemoteClient.validateToken(os, token, appVersion)));

/**
 * 将异步调用转换为同步调用
 * @param future {@link CompletableFuture}
 * @return 同步返回结果
 */
private RpcResult syncInvoke(CompletableFuture<ResponseEntity<String>> future) {
    if (future == null) {
        return RpcResult.error();
    }
    try {
        ResponseEntity<String> response = future.get();
        String body = response.getBody();
        RpcResult result = JsonUtil.fromJson(body, RpcResult.class);
        if (result.isError()) {
            // 如果token校验不通过,直接返回校验失败的信息
            return result;
        }

        // 校验token通过,将用户uid返回
        HttpHeaders headers = response.getHeaders();
        String uid = headers.getFirst("uid");
        RpcResult rpcResult = RpcResult.success();
        rpcResult.setData(uid);
        return rpcResult;
    } catch (Exception e) {
        logger.info("同步调用执行出错:{}", e.getMessage(), e);
        throw new RuntimeException(e);
    }
}

运行时调用鉴权服务会发生如下报错:

Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration]; nested exception is java.lang.IllegalArgumentException: Could not find class [org.springframework.boot.autoconfigure.condition.OnPropertyCondition]  

问题解决

经过检索后得知,这是一个已知的BUG,详见:Use classloader from class

解决办法:

将调用方法CompletableFuture.supplyAsync(Supplier<U> supplier)改为调用CompletableFuture.supplyAsync(Supplier<U> supplier,Executor executor),主动传递一个线程池参数即可。

// 异步执行线程池
private Executor asynchronousExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(EXECUTOR_QUEUE_SIZE),
                                new AbortPolicy());

// 异步转同步:调用feign服务接口
CompletableFuture future = CompletableFuture.supplyAsync(
        () -> authRemoteClient.validateToken(os, token, appVersion), asynchronousExecutor);
RpcResult resultVO = this.syncInvoke(future);

【参考】
Spring Cloud 疑难杂症之 CompletableFuture 与 Openfeign 一起使用的问题