RestTemplate今天过去将来

  • RestTemplate 是一个 HTTP 客户端,由 Spring 团队按照 RestFul 风格约束进行进一步抽象封装,便于开发者调用。
  • 在 spring 发展过程中,在 spring5 里,spring 团队提出并推介了一个新的 Http 客户端: webClient, 并说明 RestTemplate 将在未来的版本中弃用,并且未来不会添加主要的新功能
  • 本文会讲解 RestTemplate如何使用,以及如何使用 Apache HttpClient 和 OKHttp去替换其内部实现

本文适合阅读对象:项目中依然使用了 RestTemplate 作为客户端的开发人员。或者有需要使用 Apache HttpClient 或者 OKHttp 这一类 HTTP 工具库的开发人员。
在这里插入图片描述

RestTemplate 使用

内部实现:RestTemplate 默认使用 JDK 自带的 HttpURLConnection 作为底层 HTTP 客户端实现。它是一个同步阻塞库,每一个 Http 请求都会创建一个线程

RestTemplate 使用: 使用 发送 Http 请求非常简单。需要三步,引入依赖,设置配置 Bean,然后使用

  1. 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 设置配置类 Bean
@Configuration
public class ApplicationContextConfig {

    @Bean
    @ConditionalOnMissingBean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  1. 使用
@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
    public static String PAYMENT_URL = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/payment/insert")
    public AjaxResult<Void> callPaymentInsert() {

        PaymentEntity payment = new PaymentEntity();
        payment.setId(1L);
        payment.setSerial("123456");

        log.info("执行前-----------");
        restTemplate.postForObject(PAYMENT_URL + "/payment/insertOne", payment, AjaxResult.class);
        log.info("执行后-----------");
        return new AjaxResult<>(200, "success");
    }
}

webClient 使用

既然 spring 团队已经不推介使用 RestTemplate, 那么在 spring5 中力推的 webClient HTTP 客户端应该如何使用?

内部实现: webClient 使用 Spring Reactive 框架内部提供的异步,非阻塞解决方案。

webClient 使用: 使用需要两步,引入依赖,替换 RestTemplate 内部 HTTP 实现,然后使用

webClient 作为新一代的 HTTP 客户端,不光支持非阻塞方法,也支持与 RestTemplate 类似的阻塞方案。

  1. 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  1. 使用 webClient(非阻塞方式)
@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
    public static String PAYMENT_URL = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/payment/insert2")
    public AjaxResult<Void> callPaymentInsert() {

        PaymentEntity payment = new PaymentEntity();
        payment.setId(1L);
        payment.setSerial("123456");

        log.info("执行前-----------");
        Flux<AjaxResult> paymentEntityFlux = WebClient.create()
                .method(HttpMethod.POST)
                .uri(PAYMENT_URL + "/payment/insertOne")
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(payment))
                .retrieve()
                .bodyToFlux(AjaxResult.class);
        log.info("执行后-----------");
        paymentEntityFlux.subscribe(System.out::println);
        return new AjaxResult<>(200, "success");
    }
}

当然 webClient 也可以使用阻塞方式, 区别在于 body 方法 bodyToMono(),以及返回值

@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
    public static String PAYMENT_URL = "http://localhost:8001";

    @PostMapping("/payment/insert3")
    public AjaxResult<Void> callWebClientMono() {

        PaymentEntity payment = new PaymentEntity();
        payment.setId(1L);
        payment.setSerial("123456");

        log.info("执行前-----------");
        Mono<AjaxResult> resp = WebClient.create()
                .method(HttpMethod.POST)
                .uri(PAYMENT_URL + "/payment/insertOne")
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(payment))
                .retrieve()
                .bodyToMono(AjaxResult.class);

        System.out.println(resp.block());
        log.info("执行后-----------");

        return new AjaxResult<>(200, "success");
    }
}

okhttp

技术发展真的挺快,仅仅几年时间过去,项目中必用的 RestTemplate 竟然已经有点渐渐不再被人问津。好在其封装非常的抽象,非常方便我们去替换内部实现。
作为开发人员可以依旧使用熟悉的 restTemplate 去调用,而其内部实现被我们悄悄替换为更为高效的实现.

从开发人员的反馈,和网上的各种 HTTP 客户端性能以及易用程度评测来看.

  • OkHttp3 优于 Apache HttpClient 4
  • Apache HttpClient 4 优于 HttpURLConnection

而 RestTemplate 内部由 HttpURLConnection 实现,效率不如 OkHttp , 所以我们可以使用 OkHttp 替换其 RestTemplate 的内部实现 HttpURLConnection,来优化 Http 效率

  • 使用 OkHttpClient 作为底层客户端
  1. 引入依赖
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>4.10.0</version>
</dependency>
  1. 替换 RestTemplate 内部实现
@Configuration
public class ApplicationContextConfig {

  @Bean
  @ConditionalOnMissingBean
  public RestTemplate getRestTemplate() {
      return new RestTemplate(getOkHttpRequestFactory());
  }

  private ClientHttpRequestFactory getOkHttpRequestFactory() {
      return new OkHttp3ClientHttpRequestFactory();
  }
}
  • 上下对比一下,使用 OKHttp 替换 RestTemplate 内部实现是不是非常简单?
  • 仅仅需要向 RestTemplate 构造函数中传入 OkHttp 实例即可。Spring 的抽象封装就是如此强大。
  • 当然我们还可以对 Okttp 额外做一些其他的设置,比如使用线程池,设置超时时间,设置代理,设置监听器等等。你可以继续探索。
@Configuration
public class ApplicationContextConfig {

  @Bean
  @ConditionalOnMissingBean
  public RestTemplate getRestTemplate() {
      return new RestTemplate(getOkHttpRequestFactory());
  }

  private ClientHttpRequestFactory getOkHttpRequestFactory() {
    // 线程池
    ConnectionPool pool = new ConnectionPool(30, 300L, TimeUnit.MINUTES);

    OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
            .connectionPool(pool)
            .connectTimeout(10L, TimeUnit.SECONDS)
            .readTimeout(10L, TimeUnit.SECONDS)
            .writeTimeout(10L, TimeUnit.SECONDS)
            // .hostnameVerifier((hostname, session) -> true)
            // 设置代理
            // .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 9000)))
            // 拦截器
            // .addInterceptor()
            .build();

    return new OkHttp3ClientHttpRequestFactory(okHttpClient);
  }
}

Apache HttpClient 5

  • Apache HttpClient 的版本在 4+ 和 5+ 版本上有非常大的区别。目前 4+ 版本已经停止更新,网上相关文章基本都是使用的 4+版本。文末有关于 4 的阅读资料

  • 我们使用 4+ 最新版本 4.5.13,因为 5+ 版本无法替换,2 年多过去了,官方很可能不再打算支持

  • 在常见的 4+版本中,替换 restTemplate 内核必须用到一个类 HttpComponentsClientHttpRequestFactory,而它是在 Common HttpClient 的基础上封装而成,官方文档已经强烈不建议使用《4.3 ,所以市面上上关于 Apache HttpClient 版本低于 4.3 的 的实现都可以抛弃了。

  • 由于它也是 HTTP 的标准实现,所以与 OKHttp 一样,替换 RestTemplate 内部实现,就像喝水一样简单,

  1. 引入 4.5.13 依赖
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1.2</version>
</dependency>
  1. 替换 RestTemplate 内部 HTTP 实现
@Configuration
public class ApplicationContextConfig {

  @Bean
  @ConditionalOnMissingBean
  public RestTemplate getRestTemplate() {
      return new RestTemplate(getApacheHttpClientRequestFactory());
  }
    private ClientHttpRequestFactory getApacheHttpClientRequestFactory() {

        BasicHttpClientConnectionManager connectionManager =
                new BasicHttpClientConnectionManager();

        HttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager).build();

        return  new HttpComponentsClientHttpRequestFactory(httpClient);
    }
}

中间遇到的挑战

在查阅 httpclient5 实现 RestTemplate 的过程中耗费了较长时间。

  1. 要实现 RestTemplate ,必须实现 RestTemplate 的 ClientHttpRequestFactory 接口 ,在 httpclient4 中 ,对这个接口的实现类是HttpComponentsClientHttpRequestFactory
  2. 在 httpclient5 中,官方团队 2 年过去了,还没有实现这个类 HttpComponentsClientHttpRequestFactory, 因此导致了 httpclient5 无法实现 RestTemplate.
  3. 目前看来官方并不打算重新实现这个类了,所以只能使用 4.5.13(4+最新版本)来实现
资料阅读
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值