基于Feign实现异步调用

 更新时间:2021年05月14日 09:35:11   作者:努力的小星星  
近期,需要对之前的接口进行优化,缩短接口的响应时间,但是springcloud中的feign是不支持传递异步化的回调结果的,因此有了以下的解决方案,记录一下,需要的朋友可以参考下

一、背景

希望将http的调用由同步等待改为异步,仍使用feign的便捷。

二、使用feign理由

本质上其实feign就是将httpclient常用的操作进行简单封装,且屏蔽底层的httpclient,无感知具体的client实现,轻松完成具体client的替换

三、解决方案

feign在10.8版本后提供了Async接口,如下:

四、demo代码实现

4.1 接口编写

public interface OriginFeignClient {

    @RequestLine("POST /async/server/api")

    CompletableFuture<String> api(@Param("param") String param);

}

4.2 接口发布

@Configuration

public class OriginFeignClientConfig {

    @Bean

    public SpringEncoder springEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {

        return new SpringEncoder(messageConverters);

    }

    @Bean

    public SpringDecoder springDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {

        return new SpringDecoder(messageConverters);

    }

    @Bean

    public OriginFeignClient originFeignClient(SpringEncoder springEncoder, SpringDecoder springDecoder) {

        return AsyncFeign.asyncBuilder()

                .encoder(springEncoder)

                .decoder(springDecoder)

                .target(OriginFeignClient.class, "http://localhost.charlesproxy.com:8090");

    }

}

4.3 调用

@GetMapping("testApi")

    public String testAsyncClient() throws ExecutionException, InterruptedException {

        List<CompletableFuture<String>> results = new ArrayList<>();

        for(int i = 0; i < 10; i++) {

            results.add(originFeignClient.api(i+""));

        }

        Thread.sleep(3000);

        int index = 0;

        for (CompletableFuture<String> result : results) {

            String str = result.get();

            log.info(String.format("%d, result:%s, ", index, str));

            index++;

        }

        return "success";

    }

4.4 结果(很明显,是异步调用)

2021-05-11 14:31:29.989 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 0, result:receive: {"param":"0"} at 2021-05-11 02:31:27.243,

2021-05-11 14:31:29.993 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 1, result:receive: {"param":"1"} at 2021-05-11 02:31:27.243,

2021-05-11 14:31:29.995 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 2, result:receive: {"param":"2"} at 2021-05-11 02:31:27.244,

2021-05-11 14:31:29.995 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 3, result:receive: {"param":"3"} at 2021-05-11 02:31:27.245,

2021-05-11 14:31:29.995 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 4, result:receive: {"param":"4"} at 2021-05-11 02:31:27.243,

2021-05-11 14:31:29.996 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 5, result:receive: {"param":"5"} at 2021-05-11 02:31:27.243,

2021-05-11 14:31:29.996 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 6, result:receive: {"param":"6"} at 2021-05-11 02:31:27.244,

2021-05-11 14:31:29.997 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 7, result:receive: {"param":"7"} at 2021-05-11 02:31:27.243,

2021-05-11 14:31:29.997 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 8, result:receive: {"param":"8"} at 2021-05-11 02:31:27.244,

2021-05-11 14:31:29.998 - INFO [TraceId: , SpanId : ] 24745 --- [nio-8091-exec-5] c.m.a.c.controller.ClientController : 9, result:receive: {"param":"9"} at 2021-05-11 02:31:27.245,

 五、问题

feign增加的async实现是10.8版本,我们找到Spring boot 2.2.13和相应cloud最新版Hoxton.SR11(当然不是最新,是该小版本下最新,因为目前项目中使用的就是该版本),排查思路就是以@EnableFeignClients为入口

1.EnableFeignClients内容如下

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}

引入了FeignClientsRegistrar

2.FeignClientsRegistrar功能如下:

(1)扫描@FeignClient,并将其下入到BeanDefinition中

(2)调用registerFeignClient,构造FeignClientFactoryBean(创建feign的工厂类,关键在此

(3)进入FeignClientFactoryBean内部看下,构建feign的方法:getTarget():

从context中去找feign.Feign.Builder的实现(即@Bean),看下当前项目中有哪几种实现:

1.org.springframework.cloud.openfeign.FeignCircuitBreaker.Builder Spring Cloud定义的通用断路器

2.feign.hystrix.HystrixFeign.Builder 断路器的原生实现

先不管哪种builder,最终执行:loadBalance,方法内容如下:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
		HardCodedTarget<T> target) {
	Client client = getOptional(context, Client.class);
	if (client != null) {
		builder.client(client);
		Targeter targeter = get(context, Targeter.class);
		return targeter.target(this, builder, context, target);
	}
 
	throw new IllegalStateException(
			"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

 好了!!看到我们想要的了,Client的创建,不对,等等!!Client,我们先看下他的实现类是不是包含我们的AsyncClient,

一切幻想破灭

好了,所以目前的Spring cloud openfeign中仍未支持该特性,先这样用吧

到此这篇关于基于Feign实现异步调用的文章就介绍到这了,更多相关Feign异步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java读取zip/jar包中文件的几种方式

    java读取zip/jar包中文件的几种方式

    这篇文章主要给大家介绍了关于java读取zip/jar包中文件的几种方式,在我们日常使用中压缩文件是非常常用的,文中通过示例代码将java读取zip/jar包中文件的方法介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Springboot接收文件与发送文件实例教程

    Springboot接收文件与发送文件实例教程

    最近工作中遇到个需求,springboot简单的上传文档或者图片,并且进行操作,操作完后进行保存指定路径,下面这篇文章主要给大家介绍了关于Springboot接收文件与发送文件的相关资料,需要的朋友可以参考下
    2023-05-05
  • SpringBoot参数校验之@Validated的使用详解

    SpringBoot参数校验之@Validated的使用详解

    这篇文章主要通过示例为大家详细介绍一下介绍了SpringBoot参数校验中@Validated的使用方法,文中的示例代码讲解详细,需要的可以参考一下
    2022-06-06
  • Mybatis Limit实现分页功能

    Mybatis Limit实现分页功能

    这篇文章主要介绍了Mybatis Limit实现分页功能,使用Limit实现分页可以减少数据的处理量,本文通过代码讲解的非常详细,需要的朋友可以参考下
    2021-04-04
  • SpringSecurity 自定义表单登录的实现

    SpringSecurity 自定义表单登录的实现

    这篇文章主要介绍了SpringSecurity 自定义表单登录的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • 基于Java实现抽奖系统

    基于Java实现抽奖系统

    这篇文章主要为大家详细介绍了基于Java实现抽奖系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • MyBatis逆向工程的创建和使用

    MyBatis逆向工程的创建和使用

    这篇文章主要介绍了MyBatis逆向工程的创建和使用,需要的朋友可以参考下
    2017-08-08
  • Java中进程与线程的区别

    Java中进程与线程的区别

    这篇文章主要介绍了Java进程与线程的区别,进程(Process)是操作系统分配资源的基本单位,线程(Thread)是操作系统能够进行运算调度的基本单位,下文更多两者区别。需要的小伙伴可以参考一下
    2022-05-05
  • java使用ajax完成上传文件

    java使用ajax完成上传文件

    这篇文章主要为大家详细介绍了java使用ajax完成上传文件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Java中方法作为参数传递的方式

    Java中方法作为参数传递的方式

    这篇文章主要介绍了Java如何让方法作为参数传递,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05

最新评论