我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring Boot异步请求和异步调用的示例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring Boot异步请求和异步调用的示例分析

这篇文章主要为大家展示了“Spring Boot异步请求和异步调用的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring Boot异步请求和异步调用的示例分析”这篇文章吧。

一、Spring Boot中异步请求的使用

1、异步请求与同步请求

Spring Boot异步请求和异步调用的示例分析

Spring Boot异步请求和异步调用的示例分析

特点:

可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应。

一句话:增加了服务器对客户端请求的吞吐量(实际生产上我们用的比较少,如果并发请求量很大的情况下,我们会通过nginx把请求负载到集群服务的各个节点上来分摊请求压力,当然还可以通过消息队列来做请求的缓冲)。

2、异步请求的实现

方式一:Servlet方式实现异步请求

@RequestMapping(value = "/email/servletReq", method = GET)    public void servletReq (HttpServletRequest request, HttpServletResponse response) {        AsyncContext asyncContext = request.startAsync();        //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理        asyncContext.addListener(new AsyncListener() {            @Override            public void onTimeout(AsyncEvent event) throws IOException {                System.out.println("超时了...");                //做一些超时后的相关操作...            }            @Override            public void onStartAsync(AsyncEvent event) throws IOException {                System.out.println("线程开始");            }            @Override            public void onError(AsyncEvent event) throws IOException {                System.out.println("发生错误:"+event.getThrowable());            }            @Override            public void onComplete(AsyncEvent event) throws IOException {                System.out.println("执行完成");                //这里可以做一些清理资源的操作...            }        });        //设置超时时间        asyncContext.setTimeout(20000);        asyncContext.start(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(10000);                    System.out.println("内部线程:" + Thread.currentThread().getName());                    asyncContext.getResponse().setCharacterEncoding("utf-8");                    asyncContext.getResponse().setContentType("text/html;charset=UTF-8");                    asyncContext.getResponse().getWriter().println("这是异步的请求返回");                } catch (Exception e) {                    System.out.println("异常:"+e);                }                //异步请求完成通知                //此时整个请求才完成                asyncContext.complete();            }        });        //此时之类 request的线程连接已经释放了        System.out.println("主线程:" + Thread.currentThread().getName());    }

方式二:使用很简单,直接返回的参数包裹一层callable即可,可以继承WebMvcConfigurerAdapter类来设置默认线程池和超时处理

@RequestMapping(value = "/email/callableReq", method = GET)    @ResponseBody    public Callable<String> callableReq () {        System.out.println("外部线程:" + Thread.currentThread().getName());        return new Callable<String>() {            @Override            public String call() throws Exception {                Thread.sleep(10000);                System.out.println("内部线程:" + Thread.currentThread().getName());                return "callable!";            }        };    }    @Configuration    public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {    @Resource    private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;    @Override    public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {        //处理 callable超时        configurer.setDefaultTimeout(60*1000);        configurer.setTaskExecutor(myThreadPoolTaskExecutor);        configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());   }    @Bean    public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {        return new TimeoutCallableProcessingInterceptor();    }  }

方式三:和方式二差不多,在Callable外包一层,给WebAsyncTask设置一个超时回调,即可实现超时处理

@RequestMapping(value = "/email/webAsyncReq", method = GET)      @ResponseBody      public WebAsyncTask<String> webAsyncReq () {          System.out.println("外部线程:" + Thread.currentThread().getName());          Callable<String> result = () -> {              System.out.println("内部线程开始:" + Thread.currentThread().getName());              try {                  TimeUnit.SECONDS.sleep(4);              } catch (Exception e) {                  // TODO: handle exception              }              logger.info("副线程返回");              System.out.println("内部线程返回:" + Thread.currentThread().getName());              return "success";          };          WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);          wat.onTimeout(new Callable<String>() {              @Override              public String call() throws Exception {                  // TODO Auto-generated method stub                  return "超时";              }          });          return wat;      }

方式四:DeferredResult可以处理一些相对复杂一些的业务逻辑,最主要还是可以在另一个线程里面进行业务处理及返回,即可在两个完全不相干的线程间的通信。

@RequestMapping(value = "/email/deferredResultReq", method = GET)      @ResponseBody      public DeferredResult<String> deferredResultReq () {          System.out.println("外部线程:" + Thread.currentThread().getName());          //设置超时时间          DeferredResult<String> result = new DeferredResult<String>(60*1000L);          //处理超时事件 采用委托机制          result.onTimeout(new Runnable() {              @Override              public void run() {                  System.out.println("DeferredResult超时");                  result.setResult("超时了!");              }          });          result.onCompletion(new Runnable() {              @Override              public void run() {                  //完成后                  System.out.println("调用完成");              }          });          myThreadPoolTaskExecutor.execute(new Runnable() {              @Override              public void run() {                  //处理业务逻辑                  System.out.println("内部线程:" + Thread.currentThread().getName());                  //返回结果                  result.setResult("DeferredResult!!");              }          });         return result;      }

二、Spring Boot中异步调用的使用

1、介绍

异步请求的处理。除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。这个时候正常就是启一个新线程去做一些业务处理,让主线程异步的执行其他业务。

2、使用方式(基于spring下)

需要在启动类加入@EnableAsync使异步调用@Async注解生效

在需要异步执行的方法上加入此注解即可@Async("threadPool"),threadPool为自定义线程池。

代码略。。。就俩标签,自己试一把就可以了

3、注意事项

在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。所以最好我们来自定义一个线程池。

调用的异步方法,不能为同一个类的方法(包括同一个类的内部类),简单来说,因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。

其他的注解如@Cache等也是一样的道理,说白了,就是Spring的代理机制造成的。所以在开发中,最好把异步服务单独抽出一个类来管理。下面会重点讲述。。

4、什么情况下会导致@Async异步方法会失效?

调用同一个类下注有@Async异步方法:

在spring中像@Async和@Transactional、cache等注解本质使用的是动态代理,其实Spring容器在初始化的时候Spring容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器,那么解决方法也会沿着这个思路来解决。

调用的是静态(static )方法

调用(private)私有化方法

5、解决4中问题1的方式(其它2,3两个问题自己注意下就可以了)

将要异步执行的方法单独抽取成一个类,原理就是当你把执行异步的方法单独抽取成一个类的时候,这个类肯定是被Spring管理的,其他Spring组件需要调用的时候肯定会注入进去,这时候实际上注入进去的就是代理类了。

其实我们的注入对象都是从Spring容器中给当前Spring组件进行成员变量的赋值,由于某些类使用了AOP注解,那么实际上在Spring容器中实际存在的是它的代理对象。那么我们就可以通过上下文获取自己的代理对象调用异步方法。

@Controller  @RequestMapping("/app")  public class EmailController {      //获取ApplicationContext对象方式有多种,这种最简单,其它的大家自行了解一下      @Autowired      private ApplicationContext applicationContext;      @RequestMapping(value = "/email/asyncCall", method = GET)      @ResponseBody      public Map<String, Object> asyncCall () {          Map<String, Object> resMap = new HashMap<String, Object>();          try{              //这样调用同类下的异步方法是不起作用的              //this.testAsyncTask();              //通过上下文获取自己的代理对象调用异步方法              EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);              emailController.testAsyncTask();              resMap.put("code",200);          }catch (Exception e) {              resMap.put("code",400);              logger.error("error!",e);          }          return resMap;      }       //注意一定是public,且是非static方法      @Async      public void testAsyncTask() throws InterruptedException {          Thread.sleep(10000);          System.out.println("异步任务执行完成!");      }  }

开启cglib代理,手动获取Spring代理类,从而调用同类下的异步方法。首先,在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)注解。代码实现,如下:

@Service  @Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)  public class EmailService {      @Autowired      private ApplicationContext applicationContext;      @Async      public void testSyncTask() throws InterruptedException {          Thread.sleep(10000);          System.out.println("异步任务执行完成!");      }      public void asyncCallTwo() throws InterruptedException {          //this.testSyncTask();  //        EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);  //        emailService.testSyncTask();          boolean isAop = AopUtils.isAopProxy(EmailController.class);//是否是代理对象;          boolean isCglib = AopUtils.isCglibProxy(EmailController.class);  //是否是CGLIB方式的代理对象;          boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class);  //是否是JDK动态代理方式的代理对象;          //以下才是重点!!!          EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);          EmailService proxy = (EmailService) AopContext.currentProxy();          System.out.println(emailService == proxy ? true : false);          proxy.testSyncTask();          System.out.println("end!!!");      }  }

三、异步请求与异步调用的区别

两者的使用场景不同,异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量;而异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析等。

异步请求是会一直等待response相应的,需要返回结果给客户端的;而异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。

以上是“Spring Boot异步请求和异步调用的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring Boot异步请求和异步调用的示例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spring Boot异步请求和异步调用的示例分析

这篇文章主要为大家展示了“Spring Boot异步请求和异步调用的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring Boot异步请求和异步调用的示例分析”这篇文章吧。一、Sp
2023-06-16

Java Servlet异步请求开启的示例分析

这篇文章主要为大家展示了“Java Servlet异步请求开启的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java Servlet异步请求开启的示例分析”这篇文章吧。1. 背景在研究
2023-06-29

异步请求如何利用Spring Boot来实现

这篇文章给大家介绍异步请求如何利用Spring Boot来实现,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。首先说一下几个要点: 1、@WebFilter 和 @WebServlet 注解中的 asyncSupport
2023-05-31

React中setState同步和异步的示例分析

这篇文章主要介绍React中setState同步和异步的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个颠覆式的前
2023-06-15

C#异步编程的示例分析

小编给大家分享一下C#异步编程的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!异步编程在处理并发方面被使用的越来越多,之所以说上面一句话,是为了区分多线程
2023-06-17

SpringMVC异步处理的示例分析

这篇文章主要介绍SpringMVC异步处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!异步处理 demo如果要启用异步返回,需要开启 @EnableAsync。如下的代码中,使用 DeferredResu
2023-06-17

C#异步通信的示例分析

这篇文章主要为大家展示了“C#异步通信的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C#异步通信的示例分析”这篇文章吧。C#异步通信概念及应用的认识首先让我们来看看:在网络编程中运用S
2023-06-17

JS中异步和单线程的示例分析

这篇文章主要介绍了JS中异步和单线程的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。单线程但是我们在开发中,遇到请求网络,或者定时任务的时候,如果等待网络请求结束或者
2023-06-15

Java中使用同步回调和异步回调的示例详解

这篇文章主要介绍了Java中使用同步回调和异步回调的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-05-16

c#中异步编程的示例分析

这篇文章给大家分享的是有关c#中异步编程的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、什么算异步?  广义来讲,两个工作流能同时进行就算异步,例如,CPU与外设之间的工作流就是异步的。在面向服务的系
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录