微服务架构师封神之路08-微服务Rest接口调用客户端性能比较,RestTemplate vs WebClient

目前最新版本Springboot(2.4.2)下两大HTTP客户端体系,两种HTTP调用使用了不同的底层设计:

  • RestTemplate。阻塞式客户端,在底层,RestTemplate 使用了基于每个请求对应一个线程模型(thread-per-request)。
  • WebClient。WebClient 使用 Spring Reactive Framework 所提供的异步非阻塞解决方案。Reactive 框架使用事件驱动的体系结构。它提供了通过 Reactive Streams API 组合异步逻辑的方法。因此,与同步/阻塞方法相比,Reactive 可以使用更少的线程和系统资源来处理更多的逻辑。

后面在同等条件下(线程池最大500线程)对他们的性能进行对比。

RestTemplate Config

pom中添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        ......
        <!-- httpcomponents, for restTemplate -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

初始化RestTemplate并设置线程池,

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(500);
        //connectionManager.setDefaultMaxPerRoute(150);

        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(500)  //返回数据的超时时间
                .setConnectTimeout(10000) //连接上服务器的超时时间
                .setConnectionRequestTimeout(5000) //从连接池中获取连接的超时时间
                .build();

        HttpClient httpClient = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager).build();
        return builder.requestFactory(()->new HttpComponentsClientHttpRequestFactory(httpClient)).build();
    }

}

WebClient Config

在pom中添加依赖

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

初始化WebClient并配置线程池,

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(){
        //配置动态连接池
        //ConnectionProvider provider = ConnectionProvider.elastic("elastic pool");
        //配置固定大小连接池,如最大连接数、连接获取超时、空闲连接死亡时间等
        ConnectionProvider provider = ConnectionProvider
                .builder("webClient-connection-pool")
                .maxConnections(500)
                .maxIdleTime(Duration.ofMillis(10000))
                .pendingAcquireTimeout(Duration.ofMillis(100))
                .build();
        HttpClient httpClient = HttpClient.create(provider).keepAlive(true);
        return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    }

}

性能对比

统计1000次同步请求所花费的时间。目标Rest接口就部署在本地,所以network所花费的时间可以说非常短。
代码示例:

RestTemplate

        long t0 = System.currentTimeMillis();
        for(int i=0; i<no;i++){
            ResponseEntity<Booking[]> responseEntity =
                    restTemplate.postForEntity(Constant.URL_BOOK_TICKETS,request,Booking[].class);
            Booking[] bookings = responseEntity.getBody();

            log.info("Result: {}",Arrays.toString(bookings));
        }
        long t1 = System.currentTimeMillis();
        long time = t1 - t0;
        log.info("RestTemplate http call: {}, take: {}",no,time);

WebClient

        URI uri = URI.create(Constant.URL_BOOK_TICKETS);
        long t0 = Instant.now().toEpochMilli();
        for(int i=0; i<no; i++){
            Mono<Booking[]> resp = webClient.post()
                    .uri(uri)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(Mono.just(bookingList),List.class)
                    .retrieve()
                    .bodyToMono(Booking[].class);
            log.info("Result: {}", Arrays.toString(resp.block()));
        }
        long t1 = Instant.now().toEpochMilli();
        long time = t1 - t0;
        log.info("WebClient http call: {}, take: {}",no,time);

测试结果

对每种请求方式运行多次,将最初几次结果去掉,以避免系统在初始化阶段资源还没有被充分使用的情况影响测试结果。

  • RestTemplate: 稳定在321ms左右
  • WebClent: 稳定在221ms左右

结论:

  • 即使在使用同步调用的情况下,WebClient也要好于RestTemplate。
  • 如果我们可以将业务设计成异步的方式,使用WebClient的优势将更加明显。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值