前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【SpringBoot WEB 系列】AsyncRestTemplate 之异步非阻塞网络请求介绍篇

【SpringBoot WEB 系列】AsyncRestTemplate 之异步非阻塞网络请求介绍篇

原创
作者头像
一灰灰blog
修改2020-07-07 10:24:02
5.3K0
修改2020-07-07 10:24:02
举报
文章被收录于专栏:小灰灰小灰灰

【SpringBoot WEB 系列】AsyncRestTemplate 之异步非阻塞网络请求介绍篇

AsyncRestTemplate 发起异步网络请求,由 Spring4.0 引入,但是在 5.0 就被表上了删除注解,官方推荐使用基于 React 的 WebClient 来代替。

虽然官方已经不推荐使用AsyncRestTemplate,但是如果你的 web 项目,并不想引入 react 相关的包,使用AsyncRestTemplate来实现异步网络请求也不失为一个选择,本文将主要介绍它的基本使用姿势

<!-- more -->

I. 项目环境

本文创建的实例工程采用SpringBoot 2.2.1.RELEASE + maven 3.5.3 + idea进行开发

1. pom 依赖

具体的 SpringBoot 项目工程创建就不赘述了,对于 pom 文件中,需要重点关注下面两个依赖类

代码语言:txt
复制
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

请注意我们并没有引入 react 相关的包,所以是没法直接使用 webclient 的

简单的配置一下 logback 的日志输出(非必要条件), application.yml文件内容如下

代码语言:txt
复制
logging:
  pattern:
    console: (%msg%n%n){blue}

2. 测试接口

编写几个简单的用于测试的 REST 接口

代码语言:txt
复制
@GetMapping(path = "atimeout")
public String aTimeOut(HttpServletRequest request) throws InterruptedException {
    Thread.sleep(3_000L);
    return "time out!" + JSON.toJSONString(request.getParameterMap());
}

@GetMapping(path = "4xx")
public String _4xx(HttpServletRequest request, HttpServletResponse response) {
    response.setStatus(401);
    return "return 401 : " + JSON.toJSONString(request.getParameterMap());
}

II. 使用说明

从接口声明上来看,AsyncRestTemplate 与 RestTemplate 的使用姿势没有什么区别,如典型的 GET/POST 接口声明如下

代码语言:txt
复制
// GET
@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException

@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(String url, Class<T> responseType,
		Map<String, ?> uriVariables) throws RestClientException

@Override
public <T> ListenableFuture<ResponseEntity<T>> getForEntity(URI url, Class<T> responseType)
		throws RestClientException

//POST
@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
		Class<T> responseType, Object... uriVariables) throws RestClientException

@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(String url, @Nullable HttpEntity<?> request,
		Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException

@Override
public <T> ListenableFuture<ResponseEntity<T>> postForEntity(URI url,
		@Nullable HttpEntity<?> request, Class<T> responseType) throws RestClientException

1. 使用姿势

GET/POST 的访问姿势就不再赘述,有兴趣的小伙伴可以查看 RestTemplate 的使用博文:【WEB 系列】RestTemplate 基础用法小结

注意到不同的点在于返回的对象,RestTemplate是直接返回实体;而AsyncRestTemplate返回的则是ListenerableFuture包装的结果,这个类属于 Spring 自定义对象,继承自 Future 体系,而 Future 是我们并发编程中用于获取异步结果的一个接口

ListenerableFuture的最大特点在于它可以绑定执行完成的监听器,就不需要通过 get 来阻塞获取结果了,一个简单的使用姿势如下, 分别演示正常返回,异常返回的回调 case(两者都不会阻塞主线程的执行哦)

代码语言:txt
复制
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();

long start = System.currentTimeMillis();
ListenableFuture<ResponseEntity<String>> response =
        asyncRestTemplate.getForEntity("http://127.0.0.1:8080/atimeout?name=一灰灰&age=20", String.class);

response.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
    @Override
    public void onFailure(Throwable throwable) {
        log.info("1. Async get error! cost: {}, e: {}", System.currentTimeMillis() - start, throwable);
    }

    @Override
    public void onSuccess(ResponseEntity<String> stringResponseEntity) {
        String ans = stringResponseEntity.getBody();
        log.info("1. success get: {}, cost: {}", ans, System.currentTimeMillis() - start);
    }
});

response = asyncRestTemplate.getForEntity("http://127.0.0.1:8080/4xx?name=一灰灰&age=20", String.class);
response.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
    @Override
    public void onFailure(Throwable ex) {
        log.info("2. Async get error! cost: {}, e: {}", System.currentTimeMillis() - start, ex);
    }

    @Override
    public void onSuccess(ResponseEntity<String> result) {
        log.info("2. success get: {}, cost: {}", result, System.currentTimeMillis() - start);
    }
});
log.info("do something else!!!");

请注意下面的动图,主线程的do something else!!!文案会优先输出,并不会被阻塞;然后就是返回结果之后的回调,因为第一个 case 访问的 rest 服务有个 sleep,所以输出也会有一个明显的滞后

2. Guava 方式的异步请求

除了上面说到的 AsyncRestTemplate 来实现异步访问,我们也可以借助 Gauva 配合RestTemplate来实现类似的效果,下面作为扩展知识点,给出一个等效的使用说明

代码语言:txt
复制
public void guava() {
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    // 基于jdk线程池,创建支持异步回调的线程池
    ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);

    long start = System.currentTimeMillis();
    // 具体的异步访问任务
    com.google.common.util.concurrent.ListenableFuture<HttpEntity<String>> ans =
            listeningExecutorService.submit(new Callable<HttpEntity<String>>() {
                @Override
                public HttpEntity<String> call() throws Exception {
                    RestTemplate restTemplate = new RestTemplate();
                    return restTemplate
                            .getForEntity("http://127.0.0.1:8080/atimeout?name=一灰灰&age=19", String.class);
                }
            });

    // 完成之后,在指定的线程池(第三个参数)中回调
    Futures.addCallback(ans, new com.google.common.util.concurrent.FutureCallback<HttpEntity<String>>() {
        @Override
        public void onSuccess(@Nullable HttpEntity<String> stringHttpEntity) {
            log.info("guava call back res: {}, cost: {}", stringHttpEntity.getBody(),
                    System.currentTimeMillis() - start);
        }

        @Override
        public void onFailure(Throwable throwable) {
            log.info("guava call back failed cost:{}, e: {}", System.currentTimeMillis() - start, throwable);
        }
    }, Executors.newFixedThreadPool(1));

    log.info("do something other in guava!");
    listeningExecutorService.shutdown();
}

看到这里自然而然会有一个疑问,异步任务完成的回调,是怎么实现呢?

欢迎各位小伙伴评论给出看法

II. 其他

0. 项目&系列博文

博文

源码

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • I. 项目环境
    • 1. pom 依赖
      • 2. 测试接口
      • II. 使用说明
        • 1. 使用姿势
          • 2. Guava 方式的异步请求
          • II. 其他
            • 0. 项目&系列博文
              • 1. 一灰灰 Blog
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档