SpringMVC创建异步回调请求的4种方式
首先要明确一点,同步请求和异步请求对于客户端用户来讲是一样的,都是需客户端等待返回结果。不同之处在于请求到达服务器之后的处理方式,下面用两张图解释一下同步请求和异步请求在服务端处理方式的不同:

同步请求

异步请求
两个流程中客户端对Web容器的请求,都是同步的。因为它们在请求客户端时都处于阻塞等待状态,并没有进行异步处理。在Web容器部分,第一个流程采用同步请求,第二个流程采用异步回调的形式。通过异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,从而增加了服务器对客户端请求的吞吐量。但并发请求量较大时,通常会通过负载均衡的方案来解决,而不是异步。
使用AsyncContext执行异步请求
package com.example.async;import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AsyncContextController {@GetMapping("/asyncContext")@ResponseBodypublic String asyncTask(HttpServletRequest request) {AsyncContext asyncContext = request.startAsync();asyncContext.addListener(new AsyncListener() {@Overridepublic void onTimeout(AsyncEvent event) throws IOException {System.out.println("处理超时了...");}@Overridepublic void onStartAsync(AsyncEvent event) throws IOException {System.out.println("线程开始执行");}@Overridepublic void onError(AsyncEvent event) throws IOException {System.out.println("执行过程中发生错误:" + event.getThrowable().getMessage());}@Overridepublic void onComplete(AsyncEvent event) throws IOException {System.out.println("执行完成,释放资源");}});asyncContext.setTimeout(6000);asyncContext.start(new Runnable() {@Overridepublic void run() {try {Thread.sleep(5000);System.out.println("内部线程:" + Thread.currentThread().getName());asyncContext.getResponse().getWriter().println("async processing");} catch (Exception e) {System.out.println("异步处理发生异常:" + e.getMessage());}asyncContext.complete(); // 异步请求完成通知,整个请求完成}});System.out.println("主线程:" + Thread.currentThread().getName()); return "OK";}
}使用Callable执行异步请求
package com.example.async;import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class CallableController {@GetMapping(path = "/callable")@ResponseBodypublic Callable<String> asyncRequest() {return () -> {TimeUnit.SECONDS.sleep(10);return "OK";};}
}使用WebAsyncTask执行异步请求
package com.example.async;import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;@RestController
public class WebAsyncTaskController {@GetMapping("/webAsyncTask")@ResponseBodypublic WebAsyncTask<String> asyncTask() {WebAsyncTask<String> webAsyncTask = new WebAsyncTask<String>(1000l * 10, new Callable<String>() {@Overridepublic String call() throws Exception {TimeUnit.SECONDS.sleep(5);return "OK";}});webAsyncTask.onCompletion(new Runnable() {@Overridepublic void run() {System.out.println("调用完成");}});webAsyncTask.onTimeout(new Callable<String>() {@Overridepublic String call() throws Exception {return "Time Out";}});return webAsyncTask;}
}
使用DeferredResult执行异步请求
package com.example.async;import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;@RestController
public class DeferredResultController {@GetMapping(path = "/deferredResult")@ResponseBodypublic DeferredResult<String> asyncRequest() {DeferredResult<String> deferredResult = new DeferredResult<>(1000L * 5, "失败");deferredResult.onTimeout(() -> {System.out.println("调用超时");deferredResult.setResult("调用超时");});deferredResult.onCompletion(() -> {System.out.println("调用完成");});new Thread(() -> {try {TimeUnit.SECONDS.sleep(10);deferredResult.setResult("OK");} catch (Exception e) {e.printStackTrace();}}).start();return deferredResult;}
}另外:Spring Boot中使用注解@Async处理异步任务
@Async注解的异步操作和上文所诉的四种异步请求不同之处在于,使用@Async处理异步任务时没有异步回调响应客户端的流程:
使用@EnableAsync开启@Async
package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
public class ExampleApplication {public static void main(String[] args) {SpringApplication.run(ExampleApplication.class, args);}}如果将@Async加在Controller上或是 Controller 的方法上
package com.example.async;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AsyncController {@Autowiredprivate TestService testService;@GetMapping("/async")@ResponseBody@Asyncpublic String asyncTask() {testService.doSomeThing();System.out.println("处理完成");return "OK";}
}控制器立即会给客户端空响应,但是控制器方法依旧执行


如果将@Async加在Service上或是 Service 的方法上
package com.example.async;import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class TestService {@Asyncpublic void doSomeThing() {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}
}控制器不再等待Service方法执行完毕就响应客户端

相关文章:
SpringMVC创建异步回调请求的4种方式
首先要明确一点,同步请求和异步请求对于客户端用户来讲是一样的,都是需客户端等待返回结果。不同之处在于请求到达服务器之后的处理方式,下面用两张图解释一下同步请求和异步请求在服务端处理方式的不同:同步请求异步请求两个流程…...
MySQL(二)表的操作
一、创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 说明: field 表示列名 datatype 表示列的类型 character set 字符集,如…...
SpringCloud - 入门
目录 服务架构演变 单体架构 分布式架构 分布式架构要考虑的问题 微服务 初步认识 案例Demo 服务拆分注意事项 服务拆分示例 服务调用 服务架构演变 单体架构 将业务的所有功能集中在一个项目中开发,打成一个包部署优点: 架构简单部署成本低缺…...
进一步了解C++函数的各种参数以及重载,了解C++部分的内存模型,C++独特的引用方式,巧妙替换指针,初步了解类与对象。满满的知识,希望大家能多多支持
C的编程精华,走过路过千万不要错过啊!废话少说,我们直接进入正题!!!! 函数高级 C的函数提高 函数默认参数 在C中,函数的形参列表中的形参是可以有默认值的。 语法:返…...
Chapter6:机器人SLAM与自主导航
ROS1{\rm ROS1}ROS1的基础及应用,基于古月的课,各位可以去看,基于hawkbot{\rm hawkbot}hawkbot机器人进行实际操作。 ROS{\rm ROS}ROS版本:ROS1{\rm ROS1}ROS1的Melodic{\rm Melodic}Melodic;实际机器人:Ha…...
Sass的使用要点
Sass 是一个 CSS 预处理器,完全兼容所有版本的 CSS。实际上,Sass 并没有真正为 CSS 语言添加任何新功能。只是在许多情况下可以可以帮助我们减少 CSS 重复的代码,节省开发时间。 一、注释 方式一:双斜线 // 方式二:…...
计算机启动过程,从按下电源按钮到登录界面的详细步骤
1、背景 自接触计算机以来,一直困扰着我一个问题。当我们按下电脑的开机键后,具体发生了哪些过程呢?计算机启动的具体步骤是什么? 计算机启动过程通常分为五个步骤:电源自检、BIOS自检、引导设备选择、引导程序加载和…...
LeetCode 刷题之 BFS 广度优先搜索【Python实现】
1. BFS 算法框架 BFS:用来搜索 最短路径 比较合适,如:求二叉树最小深度、最少步数、最少交换次数,一般与 队列 搭配使用,空间复杂度比 DFS 大很多DFS:适合搜索全部的解,如:寻找最短…...
Hadoop01【尚硅谷】
大数据学习笔记 大数据概念 大数据:指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 主要解决,海量数据的存储…...
Echarts 配置横轴竖轴指示线,更换颜色、线型和大小
第018个点击查看专栏目录本示例是描述如何在Echarts上配置横轴竖轴指示线,更换颜色、线型和大小。方法很简单,参考示例源代码。 文章目录示例效果示例源代码(共85行)相关资料参考专栏介绍示例效果 示例源代码(共85行&a…...
OpenAI 官方API Java版SDK,两行代码即可调用。包含GhatGPT问答接口。
声明:这是一个非官方的社区维护的库。 已经支持OpenAI官方的全部api,有bug欢迎朋友们指出,互相学习。 注意:由于这个接口: https://platform.openai.com/docs/api-reference/files/retrieve-content 免费用户无法使…...
SpringBoot 日志文件
(一)日志文件有什么用?除了发现和定位问题之外,我们还可以通过日志实现以下功能:记录用户登录日志,以便分析用户是正常登录还是恶意破解用户。记录系统的操作日志,以便数据恢复和定位操作 。记录程序的执行时间&#x…...
SQL71 检索供应商名称
描述Vendors表有字段供应商名称(vend_name)、供应商国家(vend_country)、供应商州(vend_state)vend_namevend_countryvend_stateappleUSACAvivoCNAshenzhenhuaweiCNAxian【问题】编写 SQL 语句,…...
02:入门篇 - 漫谈 CTK
作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 十万个为什么 五千个在哪里?七千个怎么办?十万个为什么?。。。生活中,有很多奥秘在等着我们去思考、揭示! 同样地,在使用 CTK 时,很多小伙伴一定也存在诸多疑问: 为什么 CTK Plugin Framework 要借…...
SpringBoot常用注解
SpringBootApplication注解包含如下三个SpringBootConfigurationEnableAutoConfigurationComponentScanSpringBootConfiguration等同于Configuration,是属于spring的一个配置类这里的 Configuration 对我们来说并不陌生,它就是 JavaConfig 形式的 Spring…...
RBAC权限模型
什么是RBAC权限模型? RBAC是基于角色的访问控制,在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。 1.0级 用户、角色、权限 2.0 权限分级 公司>部门>小组 2.1 权限继承 ps: 这个人是一个小组长…...
【郭东白架构课 模块一:生存法则】07|法则三:架构师如何找到自己的商业模式?
你好,我是郭东白,今天我们来聊聊架构活动中对商业价值的考量。 今天我们要讲的是架构师的第三个生存法则:作为一个架构师,必须要在有限的资源下最大化架构活动所带来的商业价值。对于任何一个架构活动而言,架构师的可…...
STM32 - 看门狗
独立看门狗 IWDG专业时钟LSI 低功耗仍可以运行对定时的控制比较松喂狗这些时间是按照40kHz时钟给出。实际上,MCU内部的RC频率会在30kHz到60kHz之间变化。此外,即使RC振荡器的频率是精确的,确切的时序仍然依赖于APB接口时钟与RC振荡器时钟之间…...
Redis集群搭建
一、哨兵模式 在 redis3.0之前,redis使用的哨兵架构,它借助 sentinel 工具来监控 master 节点的状态;如果 master 节点异常,则会做主从切换,将一台 slave 作为 master。 哨兵模式的缺点: (1&…...
车载基础软件——AUTOSAR AP典型应用案例
我是穿拖鞋的汉子,魔都中一位坚持长期主义的工程师! 最近不知道为何特别喜欢苏轼的一首词: 缺月挂疏桐,漏断人初静。谁见幽人独往来,缥缈孤鸿影。 惊起却回头,有恨无人省。拣尽寒枝不肯栖,寂寞…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
