7 种提升 SpringBoot 吞吐量神技!

7 种提升 SpringBoot 吞吐量神技!

      • 1、异步执行
      • 2、增加内嵌 Tomcat 的最大连接数
      • 3、使用 @ComponentScan()
      • 4、默认 Tomcat 容器改为 Undertow
      • 5、使用 BufferedWriter 进行缓冲
      • 6、Deferred 方式实现异步调用
      • 7、异步调用可以使用 AsyncHandlerInterceptor 进行拦截

1、异步执行

实现方式二种:

  • 使用异步注解 @aysnc、启动类:添加 @EnableAsync 注解

  • JDK 8 本身有一个非常好用的 Future 类——CompletableFuture

@AllArgsConstructor
public class AskThread implements Runnable{private CompletableFuture<Integer> re = null;public void run() {int myRe = 0;try {myRe = re.get() * re.get();} catch (Exception e) {e.printStackTrace();}System.out.println(myRe);}public static void main(String[] args) throws InterruptedException {final CompletableFuture<Integer> future = new CompletableFuture<>();new Thread(new AskThread(future)).start();//模拟长时间的计算过程Thread.sleep(1000);//告知完成结果future.complete(60);}
}

在该示例中,启动一个线程,此时 AskThread 对象还没有拿到它需要的数据,执行到 myRe = re.get() * re.get() 会阻塞。

我们用休眠 1 秒来模拟一个长时间的计算过程,并将计算结果告诉 future 执行结果,AskThread 线程将会继续执行。

public class Calc {public static Integer calc(Integer para) {try {//模拟一个长时间的执行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return para * para;}public static void main(String[] args) throws ExecutionException, InterruptedException {final CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(50)).thenApply((i) -> Integer.toString(i)).thenApply((str) -> "\"" + str + "\"").thenAccept(System.out::println);future.get();}
}

CompletableFuture.supplyAsync 方法构造一个 CompletableFuture 实例,在 supplyAsync() 方法中,它会在一个新线程中,执行传入的参数。

在这里它会执行 calc() 方法,这个方法可能是比较慢的,但这并不影响 CompletableFuture 实例的构造速度,supplyAsync() 会立即返回。

而返回的 CompletableFuture 实例就可以作为这次调用的契约,在将来任何场合,用于获得最终的计算结果。

supplyAsync 用于提供返回值的情况,CompletableFuture 还有一个不需要返回值的异步调用方法 runAsync(Runnable runnable),一般我们在优化 Controller 时,使用这个方法比较多。

这两个方法如果在不指定线程池的情况下,都是在 ForkJoinPool.common 线程池中执行,而这个线程池中的所有线程都是 Daemon(守护)线程,所以,当主线程结束时,这些线程无论执行完毕都会退出系统。

核心代码:

CompletableFuture.runAsync(() ->this.afterBetProcessor(betRequest,betDetailResult,appUser,id)
);

异步调用使用 Callable 来实现:

@RestController  
public class HelloController {private static final Logger logger = LoggerFactory.getLogger(HelloController.class);@Autowired  private HelloService hello;@GetMapping("/helloworld")public String helloWorldController() {return hello.sayHello();}/*** 异步调用restful* 当controller返回值是Callable的时候,springmvc就会启动一个线程将Callable交给TaskExecutor去处理* 然后DispatcherServlet还有所有的spring拦截器都退出主线程,然后把response保持打开的状态* 当Callable执行结束之后,springmvc就会重新启动分配一个request请求,然后DispatcherServlet就重新* 调用和处理Callable异步执行的返回结果, 然后返回视图** @return*/  @GetMapping("/hello")public Callable<String> helloController() {logger.info(Thread.currentThread().getName() + " 进入helloController方法");Callable<String> callable = new Callable<String>() {@Override  public String call() throws Exception {logger.info(Thread.currentThread().getName() + " 进入call方法");String say = hello.sayHello();logger.info(Thread.currentThread().getName() + " 从helloService方法返回");return say;}};logger.info(Thread.currentThread().getName() + " 从helloController方法返回");return callable;}
}

异步调用的方式 WebAsyncTask:

@RestController  
public class HelloController {private static final Logger logger = LoggerFactory.getLogger(HelloController.class);@Autowired  private HelloService hello;/*** 带超时时间的异步请求 通过WebAsyncTask自定义客户端超时间* @return*/  @GetMapping("/world")public WebAsyncTask<String> worldController() {logger.info(Thread.currentThread().getName() + " 进入helloController方法");// 3s钟没返回,则认为超时WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, new Callable<String>() {@Override  public String call() throws Exception {logger.info(Thread.currentThread().getName() + " 进入call方法");String say = hello.sayHello();logger.info(Thread.currentThread().getName() + " 从helloService方法返回");return say;}});logger.info(Thread.currentThread().getName() + " 从helloController方法返回");webAsyncTask.onCompletion(new Runnable() {@Override  public void run() {logger.info(Thread.currentThread().getName() + " 执行完毕");}});webAsyncTask.onTimeout(new Callable<String>() {@Override  public String call() throws Exception {logger.info(Thread.currentThread().getName() + " onTimeout");// 超时的时候,直接抛异常,让外层统一处理超时异常throw new TimeoutException("调用超时");}});return webAsyncTask;}/*** 异步调用,异常处理,详细的处理流程见MyExceptionHandler类* @return*/  @GetMapping("/exception")public WebAsyncTask<String> exceptionController() {logger.info(Thread.currentThread().getName() + " 进入helloController方法");Callable<String> callable = new Callable<String>() {@Override  public String call() throws Exception {logger.info(Thread.currentThread().getName() + " 进入call方法");throw new TimeoutException("调用超时!");}};logger.info(Thread.currentThread().getName() + " 从helloController方法返回");return new WebAsyncTask<>(20000, callable);}
}

2、增加内嵌 Tomcat 的最大连接数

代码如下:

@Configuration
public class TomcatConfig {@Beanpublic ConfigurableServletWebServerFactory webServerFactory() {TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());tomcatFactory.setPort(8005);tomcatFactory.setContextPath("/api-g");return tomcatFactory;}class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {public void customize(Connector connector) {Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();//设置最大连接数protocol.setMaxConnections(20000);//设置最大线程数protocol.setMaxThreads(2000);protocol.setConnectionTimeout(30000);}}
}

3、使用 @ComponentScan()

使用 @ComponentScan() 定位扫包比 @SpringBootApplication 扫包更快。

4、默认 Tomcat 容器改为 Undertow

默认 Tomcat 容器改为 Undertow(Jboss 下的服务器,Tomcat 吞吐量 5000,Undertow 吞吐量 8000)

<exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion>
</exclusions>

改为:

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

5、使用 BufferedWriter 进行缓冲

这里不给大家举例,可自行尝试。

6、Deferred 方式实现异步调用

代码如下:

@RestController
public class AsyncDeferredController {private final Logger logger = LoggerFactory.getLogger(this.getClass());private final LongTimeTask taskService;@Autowiredpublic AsyncDeferredController(LongTimeTask taskService) {this.taskService = taskService;}@GetMapping("/deferred")public DeferredResult<String> executeSlowTask() {logger.info(Thread.currentThread().getName() + "进入executeSlowTask方法");DeferredResult<String> deferredResult = new DeferredResult<>();// 调用长时间执行任务taskService.execute(deferredResult);// 当长时间任务中使用deferred.setResult("world");这个方法时,会从长时间任务中返回,继续controller里面的流程logger.info(Thread.currentThread().getName() + "从executeSlowTask方法返回");// 超时的回调方法deferredResult.onTimeout(new Runnable(){@Overridepublic void run() {logger.info(Thread.currentThread().getName() + " onTimeout");// 返回超时信息deferredResult.setErrorResult("time out!");}});// 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法deferredResult.onCompletion(new Runnable(){@Overridepublic void run() {logger.info(Thread.currentThread().getName() + " onCompletion");}});return deferredResult;}
}

7、异步调用可以使用 AsyncHandlerInterceptor 进行拦截

代码如下:

@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
// HandlerMethod handlerMethod = (HandlerMethod) handler;logger.info(Thread.currentThread().getName()+ "服务调用完成,返回结果给客户端");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {if(null != ex){System.out.println("发生异常:"+ex.getMessage());}}@Overridepublic void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// 拦截之后,重新写回数据,将原来的hello world换成如下字符串String resp = "my name is chhliu!";response.setContentLength(resp.length());response.getOutputStream().write(resp.getBytes());logger.info(Thread.currentThread().getName() + " 进入afterConcurrentHandlingStarted方法");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/627675.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[shell]实现多个shell脚本之间变量传递

一、需求 需要有一些变量要从一个shell文件向另一个shell文件传递。 二、方案 2.1通过入参形式传递 当A脚本中的变量需要传递到B脚本中时&#xff0c;可以在运行B脚本的基础上&#xff0c;将A脚本中的数据加入到B脚本的入参中&#xff0c;从而达到传递参数的目的。 2.2通过…

C++ 图形界面学习效果及代码

#include <stdio.h> #include<conio.h> #include <stdlib.h> #include<graphics.h> #define WIDTH 800 #define HEIGHT 480 #define SIZE 20 int main() {const char* str "人生就是由欲望不满足而痛苦和满足之后无趣这两者所构成";const …

1.你好, Python!

快速介绍 Python 语法、变量赋值和数字! 这门课程涵盖了你在使用Python进行数据科学时所需的关键Python技能。该课程适合有一些先前编码经验的人,希望将Python添加到他们的技能库中。(如果您是第一次编码,建议您查看我们的编程入门课程,该课程专为完全初学者设计,希望开始…

07 整合SSM的快速理解

1.1 第一问&#xff1a;SSM整合需要几个IoC容器&#xff1f; 两个容器 本质上说&#xff0c;整合就是将三层架构和框架核心API组件交给SpringIoC容器管理&#xff01; 一个容器可能就够了&#xff0c;但是我们常见的操作是创建两个IoC容器&#xff08;web容器和root容器&…

Node 相关记录

Node 版本管理 nvm 卸载之前的 nodejs下载安装配置 settings.txt root: D:\nvm # nvm 安装路径 path: D:\nvm\nodejs # node 安装路径 proxy: # 淘宝镜像 node_mirror: https://npm.taobao.org/mirrors/node/ npm_mirror: https://npm.taobao.org/mirrors/n…

2024年美赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

uni-app的组件(一)

scroll-view 可滚动视图区域。用于区域滚动 <scroll-view scroll-y"true" :scroll-top"scrollTop" class"scroll-y" scroll"scroll"><view id"demo1" class"scroll-view-item bg-red">A</view>…

【Linux笔记】进程等待与程序替换

一、进程的终止 1、进程退出码 在讲解进程的终止之前&#xff0c;先要普及一下进程的退出码概念。 我们父进程之所以要创建子进程&#xff0c;就是为了让子进程运行不一样的任务&#xff0c;那么对于子进程执行的这个任务执行完毕后的结果是否正确或者是否出差错&#xff0c…

View 自定义 - View 类中的方法

一、概念 View这个类代表用户界面组件的基本构建块。View在屏幕上占据一个矩形区域&#xff0c;并负责绘制和事件处理。View是用于创建交互式用户界面组件&#xff08;按钮、文本等&#xff09;的基础类。它的子类ViewGroup是所有布局的父类&#xff0c;它是一个可以包含其他vi…

将 pyparamvalidate 项目,发布至 pypi

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、编写 pyproject.toml 文件3、编写 LICENSE 文件4、编写 README.md 文件5、升级 pip、build、twine 工具6、打包发布的版本7、测试发布至 TestPyPI8、创建测试项目&#xff0c;测试发布结果9、正…

SQLite,ROOM 清空表数据并将自增量归零

1.先清空表数据&#xff1a; delete from [tablename]; 2.当数据库中包含自增列时&#xff0c;会自动建立一个名为 sqlite_sequence 的表。这个表包含两个列&#xff1a;name和seq。name记录自增列所在的表&#xff08;即tablename&#xff09;&#xff0c;seq记录当前序号&…

SSM框架学习笔记04 | SpringMVC

文章目录 一、SpringMVC简介二、 请求与响应1. 请求映射路径2. get请求与post请求3. 响应 二、REST风格1.简介 三、 SSM整合四、拦截器1. 定义拦截器2.配置拦截器3.拦截器执行顺序4.拦截器参数5.多个连接器工作流程分析6.拦截器链的运行顺序 一、SpringMVC简介 SpringMVC技术与…

AI嵌入式K210项目(3)-GPIO控制

文章目录 前言一、背景知识二、背景知识二、开始你的表演代码实现 总结 前言 前面介绍了开发板和环境搭建的基本情况&#xff0c;接下来我们开始学习使用C进行裸板开发&#xff0c;本节课先来学习下K210最基础的功能&#xff0c;引脚映射和点灯。 在开始具体学习之前&#xff…

java数据结构与算法刷题-----LeetCode96. 不同的二叉搜索树

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 很多人觉得动态规划很难&#xff0c;但它就是固定套路而已。其实动态规划只…

TRB 2024论文分享:融合Transformer和自监督学习的长时交通流预测模型

TRB&#xff08;Transportation Research Board&#xff0c;美国交通研究委员会&#xff0c;简称TRB&#xff09;会议是交通研究领域知名度最高学术会议之一&#xff0c;近年来的参会人数已经超过了2万名&#xff0c;是参与人数和国家最多的学术盛会。TRB会议几乎涵盖了交通领域…

Ceph的介绍与部署

目录 存储基础 单机存储设备 DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; NAS&#xff08;网络附加存储&#xff0c;是通过网络附加到当前主机文件系统之上的存储&#xff09; SAN&#xff08;存储区域网络&#xff09; 单…

基于ssm的数学课程评价系统的设计与开发+jsp论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本数学课程评价系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

List转数组,使用toArray时,new String[0] 的数组空间大小怎么传?

我们在日常开发中&#xff0c;经常遇到List转数组&#xff0c;数组转List的场景。当我们List转数组时&#xff0c;一般使用这种方式&#xff1a; String[] queryTaskIdArr queryTaskIds.toArray(new String[0]); 但是toArray方法中的入参有什么需要注意的呢&#xff1f; 如下…

Hive日期函数详细讲解

Hive 提供了一系列的内建日期函数&#xff0c;用于处理日期和时间数据。以下是您提到的日期函数的详细讲解&#xff0c;包括案例和使用注意事项&#xff1a; FROM_UNIXTIME() 功能&#xff1a;将 Unix 时间戳&#xff08;秒为单位&#xff09;转换为日期时间格式。语法&#xf…

故障处理流程规范(新)

一、背景 为什么要重新制定故障处理流程&#xff1f; 2020年写过一篇文章&#xff1a; 故障处理流程和规范&#xff0c;在过去的这三年内&#xff0c;故障处理流程输出了好几个版本&#xff0c;但都没有很好的落地&#xff0c;所以本次的目标是&#xff0c;制定一个简单、易操…