WebClient异步调用-超时时间配置

SpringBoot 2.x版本提供了 Spring WebFlux 模块,支持了请求的异步调用。

在微服务中,使用服务A调用服务B时,也是可以进行异步调用的,Spring5 封装了WebClient来进行这项操作,这里创建两个项目来模拟项目之间的这种调用。

 

首先,创建SpringBoot项目,这里使用时下最新版本 2.3.1.RELEASE进行说明。我使用的是Maven,首先要添加相应的web组件依赖,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

这里创建好SpringBoot项目后,创建所需Controller,用来支撑后续的测试操作

@RestController
@RequestMapping("/getName")
class FindNameController(val webClientBuilder: WebClient.Builder) {

    //使用get请求
    @GetMapping("/getName/{word}")
    fun getName(@PathVariable word: String): Mono<String> {
        //得到进入方法的时间
        val start = System.currentTimeMillis()
        //使用webClient进行服务间的异步调用
        return webClientBuilder
                .build()
                .method(HttpMethod.POST)
                //指定要访问项目的Url
                .uri("http://localhost:1234/timeout/getName")
                //指定要传输的参数
                .bodyValue(word)
                //进行http调用
                .retrieve()
                //结果使用Mono进行接收
                .bodyToMono(String::class.java)
                //如果出错进行错误打印处理
                .onErrorResume {
                    it.printStackTrace()
                    //可以查看是否真的超时
                    println("错误时长---" + (System.currentTimeMillis() - start) + "----" + Thread.currentThread().name)
                    Mono.just("错了" + Thread.currentThread().name)
                }


    }
}

在上述代码中,WebClient.Builder是使用配置文件定义的Bean,我们在WebClientConfig 类中进行配置,具体代码如下所示

@Component
class WebClientConfig {


    @Bean
    fun webClientBuilder(): WebClient.Builder {
        return WebClient.builder()

    }
}

这样可以把WebClient交由Spring管理,方面统一进行配置处理,这里后后续的超时时间配置就在这里进行。

以上,是为项目A。

项目B我们简单创建,只需要指定相应端口后,因为我是在同一台机器上测试,所以A,B项目的端口要需要进行区分。

项目B和项目A的创建没什么不同,这里我只创建一个Controller供A进行调用。

@RestController
@RequestMapping("/timeout")
class TimeoutController {

    @PostMapping("/getName")
    fun getName(@RequestBody word: String): Mono<String> {
        return Mono.just(word)
                .map { "$it---name is Bird" }

    }
}

我们可以在浏览器上进行直接输入地址调用。结果如下:

下边重点到了,我们在项目B的Controller加上Thread.sleep(10000),让线程睡10秒钟

@RestController
@RequestMapping("/timeout")
class TimeoutController {

    @PostMapping("/getName")
    fun getName(@RequestBody word: String): Mono<String> {
        Thread.sleep(10000)
        return Mono.just(word)
                .map { "$it---name is Bird" }

    }
}

再次调用,等了10秒结果才出来。假设10秒时间太长了,我们调用最多会等5秒钟,过了5秒钟就放弃调用该怎么做呢,我们来修改一下WebClientConfig 类。

改写后的类如下所示:

@Component
class WebClientConfig {

    private val connectionProvider = ConnectionProvider.builder("tcp-client-pool")
            //允许的最大连接数
            .maxConnections(3)
            //没有连接可用时,请求等待的最长时间
            .pendingAcquireTimeout(Duration.ofMillis(60000))
            //没有连接时,最多有多少个请求等待
            .pendingAcquireMaxCount(18)
            .build()

    //自定义一个loop来进行http线程调用管理
    private val loopResources = LoopResources.create("tcp-client-loop")

    val theTcpClient = TcpClient
            .create(connectionProvider)
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
            .runOn(loopResources)
            .doOnConnected {
                //设置读取超时时间
                it.addHandlerLast(ReadTimeoutHandler(5))
                //设置写入超时时间
                it.addHandlerLast(WriteTimeoutHandler(5))
            }


    @Bean
    fun webClientBuilder(): WebClient.Builder {
        return WebClient.builder().clientConnector(ReactorClientHttpConnector(HttpClient.from(theTcpClient)))

    }
}

如上,使用指定的HttpClient取代了默认的,并设置了读取时间和写入时间都是5秒。我们再次重启进行调用。程序打印出如下错误

因为B项目中需要10秒钟才可以把事务处理完毕,这边5秒钟后直接放弃等待,抛出请求超时。

下边我们再对

private val connectionProvider = ConnectionProvider.builder("tcp-client-pool")
            //允许的最大连接数
            .maxConnections(3)
            //没有连接可用时,请求等待的最长时间
            .pendingAcquireTimeout(Duration.ofMillis(60000))
            //没有连接时,最多有多少个请求等待
            .pendingAcquireMaxCount(2)
            .build()

进行测试,假设pendingAcquireMaxCount改为2,maxConnections为3(超时时间这里改为15秒, 大于项目B的10秒,使调用可以成功)

我们启用10个线程进行请求,按照配置,3个会直接获取到连接池分配的连接。因为最多的只允许存在3个连接,所以剩下的7个请求会被暂时搁置,因为最多请求等待个数为2个,所以剩下7个请求有两个放入到了请求等待队列,而剩下的5个会直接失败。

为了验证想法,这里使用jmeter进行线程模拟调用。

根据错误信息可知,等待队列最多有2个,多于的会抛出异常。jmeter信息如下,因为程序中catch住了异常,所以请求是成功的,但是从返回信息可以看出来,前5个请求是因为请求到达上限,直接返回了错误的提示结果。

好了,这里先说到这里。这里具体的实现源码,后边我们再说。这里的参数需要根据自己的系统进行选择配置,并不是越大越好,合适才是最重要的。 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WebClientSpring框架中的一个用于进行HTTP请求的非阻塞式客户端。它可以用来发送异步的POST请求。 为了使用WebClient发送异步的POST请求,你需要进行以下几个步骤: 1. 创建WebClient对象:使用`WebClient.create()`方法创建一个WebClient对象。 2. 设置请求URL和参数:使用`.uri(url)`方法设置请求的URL,使用`.body(BodyInserters.fromObject(params))`方法设置请求的参数。在这个例子中,`params`是一个包含请求参数的Map对象。 3. 设置请求头信息:使用`.contentType(MediaType.APPLICATION_FORM_URLENCODED)`方法设置请求的内容类型。在这个例子中,使用的是表单形式的请求。 4. 发送请求并获取响应数据:使用`.retrieve().bodyToFlux(Map.class)`方法发送请求并获取响应数据。这个方法返回一个`Flux<Map>`对象,你可以对这个对象进行订阅来处理异步返回的响应。 下面是一个示例代码,展示了如何使用WebClient发送异步的POST请求并处理响应: ```java String url = configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN); Map<String, String> params = new HashMap<String, String>(); params.put("suite_id", suiteId); params.put("suite_secret", suiteSecret); params.put("suite_ticket", suiteTicket); WebClient client = WebClient.create(); Flux<Map> mapFlux = client.post() .uri(url) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(BodyInserters.fromObject(JSON.toJSONString(params))) .retrieve() .bodyToFlux(Map.class); mapFlux.subscribe(map -> System.out.println(map.toString()), Throwable::printStackTrace); ``` 在这个示例中,`mapFlux.subscribe()`方法用来订阅异步返回的响应,当响应到达时,执行对应的回调函数。你可以在回调函数中处理响应数据或执行后续操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Spring WebClient,异步POST请求代码段](https://blog.csdn.net/sbin456/article/details/109615201)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [SilverLight学习笔记--WebClient异步请求](https://blog.csdn.net/starcrm/article/details/84969094)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值