基于Springboot吞吐量优化解决方案
一、异步执行
实现方式二种:
1.使用异步注解@aysnc、启动类:添加@EnableAsync注解
2.JDK8本身有一个非常好用的Future类——CompletableFuture
@AllArgsConstructor
publicclassAskThreadimplementsRunnable{
privateCompletableFuturere=null;
publicvoidrun(){
intmyRe=0;
try{
myRe=re.get()*re.get();
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(myRe);
}
publicstaticvoidmain(String[]args)throwsInterruptedException{
finalCompletableFuturefuture=newCompletableFuture<>();
newThread(newAskThread(future)).start();
//模拟长时间的计算过程
Thread.sleep(1000);
//告知完成结果
future.complete(60);
}
}
在该示例中,启动一个线程,此时AskThread对象还没有拿到它需要的数据,执行到myRe=re.get()*re.get()会阻塞。我们用休眠1秒来模拟一个长时间的计算过程,并将计算结果告诉future执行结果,AskThread线程将会继续执行。
publicclassCalc{
publicstaticIntegercalc(Integerpara){
try{
//模拟一个长时间的执行
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
returnpara*para;
}
publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{
finalCompletableFuturefuture=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(Runnablerunnable),一般我们在优化Controller时,使用这个方法比较多。
这两个方法如果在不指定线程池的情况下,都是在ForkJoinPool.common线程池中执行,而这个线程池中的所有线程都是Daemon(守护)线程,所以,当主线程结束时,这些线程无论执行完毕都会退出系统。
核心代码:
CompletableFuture.runAsync(()-> this.afterBetProcessor(betRequest,betDetailResult,appUser,id) );
异步调用使用Callable来实现
@RestController
publicclassHelloController{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(HelloController.class);
@Autowired
privateHelloServicehello;
@GetMapping("/helloworld")
publicStringhelloWorldController(){
returnhello.sayHello();
}
/**
*异步调用restful
*当controller返回值是Callable的时候,springmvc就会启动一个线程将Callable交给TaskExecutor去处理
*然后DispatcherServlet还有所有的spring拦截器都退出主线程,然后把response保持打开的状态
*当Callable执行结束之后,springmvc就会重新启动分配一个request请求,然后DispatcherServlet就重新
*调用和处理Callable异步执行的返回结果,然后返回视图
*
*@return
*/
@GetMapping("/hello")
publicCallablehelloController(){
logger.info(Thread.currentThread().getName()+"进入helloController方法");
Callablecallable=newCallable(){
@Override
publicStringcall()throwsException{
logger.info(Thread.currentThread().getName()+"进入call方法");
Stringsay=hello.sayHello();
logger.info(Thread.currentThread().getName()+"从helloService方法返回");
returnsay;
}
};
logger.info(Thread.currentThread().getName()+"从helloController方法返回");
returncallable;
}
}
异步调用的方式WebAsyncTask
@RestController
publicclassHelloController{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(HelloController.class);
@Autowired
privateHelloServicehello;
/**
*带超时时间的异步请求通过WebAsyncTask自定义客户端超时间
*
*@return
*/
@GetMapping("/world")
publicWebAsyncTaskworldController(){
logger.info(Thread.currentThread().getName()+"进入helloController方法");
//3s钟没返回,则认为超时
WebAsyncTaskwebAsyncTask=newWebAsyncTask<>(3000,newCallable(){
@Override
publicStringcall()throwsException{
logger.info(Thread.currentThread().getName()+"进入call方法");
Stringsay=hello.sayHello();
logger.info(Thread.currentThread().getName()+"从helloService方法返回");
returnsay;
}
});
logger.info(Thread.currentThread().getName()+"从helloController方法返回");
webAsyncTask.onCompletion(newRunnable(){
@Override
publicvoidrun(){
logger.info(Thread.currentThread().getName()+"执行完毕");
}
});
webAsyncTask.onTimeout(newCallable(){
@Override
publicStringcall()throwsException{
logger.info(Thread.currentThread().getName()+"onTimeout");
//超时的时候,直接抛异常,让外层统一处理超时异常
thrownewTimeoutException("调用超时");
}
});
returnwebAsyncTask;
}
/**
*异步调用,异常处理,详细的处理流程见MyExceptionHandler类
*
*@return
*/
@GetMapping("/exception")
publicWebAsyncTaskexceptionController(){
logger.info(Thread.currentThread().getName()+"进入helloController方法");
Callablecallable=newCallable(){
@Override
publicStringcall()throwsException{
logger.info(Thread.currentThread().getName()+"进入call方法");
thrownewTimeoutException("调用超时!");
}
};
logger.info(Thread.currentThread().getName()+"从helloController方法返回");
returnnewWebAsyncTask<>(20000,callable);
}
}
二、增加内嵌Tomcat的最大连接数
@Configuration
publicclassTomcatConfig{
@Bean
publicConfigurableServletWebServerFactorywebServerFactory(){
TomcatServletWebServerFactorytomcatFactory=newTomcatServletWebServerFactory();
tomcatFactory.addConnectorCustomizers(newMyTomcatConnectorCustomizer());
tomcatFactory.setPort(8005);
tomcatFactory.setContextPath("/api-g");
returntomcatFactory;
}
classMyTomcatConnectorCustomizerimplementsTomcatConnectorCustomizer{
publicvoidcustomize(Connectorconnector){
Http11NioProtocolprotocol=(Http11NioProtocol)connector.getProtocolHandler();
//设置最大连接数
protocol.setMaxConnections(20000);
//设置最大线程数
protocol.setMaxThreads(2000);
protocol.setConnectionTimeout(30000);
}
}
}
三、使用@ComponentScan()定位扫包比@SpringBootApplication扫包更快
四、默认tomcat容器改为Undertow(Jboss下的服务器,Tomcat吞吐量5000,Undertow吞吐量8000)
org.springframework.boot spring-boot-starter-tomcat
改为:
org.springframework.boot spring-boot-starter-undertow
五、使用BufferedWriter进行缓冲
六、Deferred方式实现异步调用
@RestController
publicclassAsyncDeferredController{
privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());
privatefinalLongTimeTasktaskService;
@Autowired
publicAsyncDeferredController(LongTimeTasktaskService){
this.taskService=taskService;
}
@GetMapping("/deferred")
publicDeferredResultexecuteSlowTask(){
logger.info(Thread.currentThread().getName()+"进入executeSlowTask方法");
DeferredResultdeferredResult=newDeferredResult<>();
//调用长时间执行任务
taskService.execute(deferredResult);
//当长时间任务中使用deferred.setResult("world");这个方法时,会从长时间任务中返回,继续controller里面的流程
logger.info(Thread.currentThread().getName()+"从executeSlowTask方法返回");
//超时的回调方法
deferredResult.onTimeout(newRunnable(){
@Override
publicvoidrun(){
logger.info(Thread.currentThread().getName()+"onTimeout");
//返回超时信息
deferredResult.setErrorResult("timeout!");
}
});
//处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
deferredResult.onCompletion(newRunnable(){
@Override
publicvoidrun(){
logger.info(Thread.currentThread().getName()+"onCompletion");
}
});
returndeferredResult;
}
}
七、异步调用可以使用AsyncHandlerInterceptor进行拦截
@Component
publicclassMyAsyncHandlerInterceptorimplementsAsyncHandlerInterceptor{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)
throwsException{
returntrue;
}
@Override
publicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,
ModelAndViewmodelAndView)throwsException{
//HandlerMethodhandlerMethod=(HandlerMethod)handler;
logger.info(Thread.currentThread().getName()+"服务调用完成,返回结果给客户端");
}
@Override
publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)
throwsException{
if(null!=ex){
System.out.println("发生异常:"+ex.getMessage());
}
}
@Override
publicvoidafterConcurrentHandlingStarted(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)
throwsException{
//拦截之后,重新写回数据,将原来的helloworld换成如下字符串
Stringresp="mynameischhliu!";
response.setContentLength(resp.length());
response.getOutputStream().write(resp.getBytes());
logger.info(Thread.currentThread().getName()+"进入afterConcurrentHandlingStarted方法");
}
}
以上这篇基于Springboot吞吐量优化解决方案就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。