当前位置: 首页 > news >正文

SpringBoot RestTemplate详解

参考自:http://events.jianshu.io/p/477e7a3179c6

大家都知道在SpringBoot中一般适用RestTemplate来进行远程调用,那么SpringBoot中如何默认配置RestTemplate,以及如何自定义配置自己的RestTemplate,RestTemplate异步请求如何实现等

1、什么是RestTemplate?

  • RestTemplate是Spring提供的进行远程调用客户端
  • RestTemplate提供了很多远程调用的方法,能够大大提高客户端的编写效率。
    调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求

可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式
ClientHttpRequestFactory接口主要提供了三种实现方式

  • 一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层的Http请求连接
  • 一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息
  • 第三种方式是使用OkHttp3ClientHttpRequestFactory方式,底层使用OkHttp访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息

2、RestTemplate的优缺点

优点:连接池、超时时间设置、支持异步、请求和响应的编解码
缺点:依赖别的spring版块、参数传递不灵活

RestTemplate默认是使用SimpleClientHttpRequestFactory,内部是调用jdk的HttpConnection,默认超时为-1
在这里插入图片描述

3、RestTemplate使用httpclient

1)引入依赖

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

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

2) yaml中添加HttpClientConnection连接池的配置

http:
  maxTotal: 100         #最大连接数
  defaultMaxPerRoute: 20  #并发数
  connectTimeout: 1000   #创建连接的最长时间
  connectionRequestTimeout: 500  #从连接池中获取到连接的最长时间
  socketTimeout: 10000 #数据传输的最长时间
  staleConnectionCheckEnabled: true  #提交请求前测试连接是否可用
  validateAfterInactivity: 3000000   #可用空闲连接过期时间,重用空闲连接时会先检查是否空闲时间超过这个时间,如果超过,释放socket重新建立

3)注入RestTemplate

@Configuration
public class RestTemplateConfig {

    /*@Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }*/

    @Value("${http.maxTotal}")
    private Integer maxTotal;

    @Value("${http.defaultMaxPerRoute}")
    private Integer defaultMaxPerRoute;

    @Value("${http.connectTimeout}")
    private Integer connectTimeout;

    @Value("${http.connectionRequestTimeout}")
    private Integer connectionRequestTimeout;

    @Value("${http.socketTimeout}")
    private Integer socketTimeout;

    @Value("${http.staleConnectionCheckEnabled}")
    private boolean staleConnectionCheckEnabled;

    @Value("${http.validateAfterInactivity}")
    private Integer validateAfterInactivity;


    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(httpRequestFactory());
    }

    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

    @Bean
    public HttpClient httpClient() {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(maxTotal); // 最大连接数
        connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);    //单个路由最大连接数
        connectionManager.setValidateAfterInactivity(validateAfterInactivity); // 最大空间时间

        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)        //服务器返回数据(response)的时间,超过抛出read timeout
                .setConnectTimeout(connectTimeout)      //连接上服务器(握手成功)的时间,超出抛出connect timeout
                .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled) // 提交前检测是否可用
                .setConnectionRequestTimeout(connectionRequestTimeout)//从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
                .build();

        //headers
        List<Header> headers = new ArrayList<>();
        headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
        headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
        headers.add(new BasicHeader("Accept-Language", "zh-CN"));
        headers.add(new BasicHeader("Connection", "Keep-Alive"));
        headers.add(new BasicHeader("Content-type", "application/json;charset=UTF-8"));

        return HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .setDefaultHeaders(headers)
                // 保持长连接配置,需要在头添加Keep-Alive
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                //重试次数,默认是3次,没有开启
                .setRetryHandler(new DefaultHttpRequestRetryHandler(2, true))
                .build();
    }
}

【一些配置解释】

  • 设置请求配置的超时时间:为了防止请求时间过长而引起资源的过渡浪费。如果在超过设置的timeout还没有数据返回,就直接断开连接
  • headers:添加默认的请求头,这里设置了传送的格式为json,语言为中-英等等属性。HttpClientBuilder.create设置请求头到HttpClient,然后在设置保持的时间,重试的次数,注入给httpClient进行封装。

4、RestTemplate使用okhttp

懒得写了:http://events.jianshu.io/p/477e7a3179c6

5、HttpClient和okhttp的使用以及区别

6、RestTemplate异步调用

参考:
https://blog.csdn.net/qq_38622452/article/details/81874483
https://blog.csdn.net/Carson073/article/details/108147005

1、通过 AsyncRestTemplate

AsyncRestTemplate是在Spring4.0中对RestTemplate进行扩展产生的新类,通过返回ListenableFuture对象生成回调机制,以达到异步非阻塞发送http请求

缺点:

  • AsyncRestTemplate已经过期
  • AsyncRestTemplate调用服务时,被调用服务不可达的时候,会抛出异常
  • AsyncRestTemplate不能携带traceId
@GetMapping("/sync/hello")
    public String syncHello() {
        AsyncRestTemplate template = new AsyncRestTemplate();
        String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
        //调用完后立即返回(没有阻塞)
        ListenableFuture<ResponseEntity<String>> forEntity = template.getForEntity(url, String.class);
        //异步调用后的回调函数
        forEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
            //调用失败
            @Override
            public void onFailure(Throwable ex) {
                log.error("远程调用faliure");
            }
            //调用成功
            @Override
            public void onSuccess(ResponseEntity<String> result) {
                log.info("res---->"+result.getBody());
            }
        });
        log.info("调用结束");
        return "ok";
    }

在这里插入图片描述

2、通过 CompletableFuture 进行异步请求

CompletableFuture 学习见我博客:

添加链接描述

  1. 首先需要自自定义一个线程池
@Component
@ConfigurationProperties(prefix = "my.thread")
@Data
public class ThreadPoolConfigProperties {

    private Integer coreSize;

    private Integer maxSize;

    private Integer keepAliveTime;
}


/**
 * @author houChen
 * @date 2021/12/11 10:35
 * @Description:   自定义线程池配置类
 */
@Configuration
public class MyThreadConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties properties){

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                properties.getCoreSize(),
                properties.getMaxSize(),
                properties.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        return threadPoolExecutor;
    }
}

2)通过CompletableFuture 发起异步请求

 /**
     * @Description: 通过CompletableFuture异步发起请求
     * @author houChen
     * @date 2022/9/24 20:18
     */
    @GetMapping("/sync/hello1")
    public String syncHello1() {
        CompletableFuture.supplyAsync(() -> {
            String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
            return restTemplate.getForEntity(url, String.class);
        }, threadPoolExecutor)
                .thenAccept(res -> {
                    log.info("res: {}", res.getBody());
                }).exceptionally(e -> {
            log.error("err:{}", e);
            return null;
        });
        return "ok";
    }

请求: http://localhost:8082/sync/hello1
在这里插入图片描述

3、通过@async注解来进行异步调用

 @GetMapping("/sync/hello2")
    public String syncHello2() {
        helloWorldService.asyncRequest();
        return "ok";
    }

@Slf4j
@Service
public class HelloWorldService {

    @Autowired
    RestTemplate restTemplate;

    // 指定使用自己定义的线程池中的线程就行调用
    @Async("threadPoolExecutor")
    public void asyncRequest() {
        String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
        String res = restTemplate.getForEntity(url, String.class).getBody();
        log.info("res :{}", res);
    }
}

在这里插入图片描述

7、总结

相关文章:

  • [代码审计] beecms 4.0 漏洞总结
  • 计算机笔试面试题记录
  • 基于量子计算的无收益标的资产欧式看涨期权定价和delta风险分析
  • 【PCB软件技巧】OrCAD与PADS相互搭配使用的相关要点
  • 精通MySQL之Explain执行计划
  • Docker学习
  • Kubernetes—k8s中Service实例出现污点
  • Chapter4.2:线性系统的根轨迹法
  • kvm快照和克隆
  • 【元胞自动机】基于元胞自动机模拟晶体生长附matlab代码
  • Unity-- Gfx.WaitForPresentOnGfxThread占用CPU过高导致帧率低
  • opencv--GrabCut
  • IT计算机企业如何使用科技虚拟员工规避人工操作风险
  • 【Android】-- 数据存储(一)(共享参数SharePreferences、数据库SQLite)
  • 文件包含漏洞——实例
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • Java 内存分配及垃圾回收机制初探
  • java8 Stream Pipelines 浅析
  • js对象的深浅拷贝
  • Js基础——数据类型之Null和Undefined
  • Kibana配置logstash,报表一体化
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Lsb图片隐写
  • ng6--错误信息小结(持续更新)
  • Promise面试题2实现异步串行执行
  • Python - 闭包Closure
  • react-native 安卓真机环境搭建
  • unity如何实现一个固定宽度的orthagraphic相机
  • Vue ES6 Jade Scss Webpack Gulp
  • 理解在java “”i=i++;”所发生的事情
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 微信小程序:实现悬浮返回和分享按钮
  • ​低代码平台的核心价值与优势
  • ​如何在iOS手机上查看应用日志
  • ​一些不规范的GTID使用场景
  • (+4)2.2UML建模图
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (12)Linux 常见的三种进程状态
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (Note)C++中的继承方式
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (论文阅读40-45)图像描述1
  • (一)为什么要选择C++
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .Net 6.0 处理跨域的方式
  • .net mvc部分视图
  • .net 反编译_.net反编译的相关问题
  • //解决validator验证插件多个name相同只验证第一的问题
  • /etc/sudoer文件配置简析
  • @NestedConfigurationProperty 注解用法
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...