当前位置: 首页 > 科技观察

SpringBoot性能太差?试试这些食谱!

时间:2023-03-13 09:01:51 科技观察

今天的文章介绍了七个常见的SpringBoot性能优化方向。1.实现异步执行有两种方式:使用异步注解@Aysnc,启动类:添加@EnableAsync注解。JDK8本身有一个非常有用的Future类——CompletableFuture。@AllArgsConstructor公共类AskThread实现Runnable{privateCompletableFuturere=null;publicvoidrun(){intmyRe=0;尝试{myRe=re.get()*re.get();}catch(Exceptione){e.printStackTrace();}System.out.println(myRe);}publicstaticvoidmain(String[]args)throwsInterruptedException{finalCompletableFuturefuture=newCompletableFuture<>();新线程(新AskThread(未来))。开始();//模拟一个长时间的计算过程Thread.sleep(1000);//通知完成结果future.complete(60);}}本例中,启动了一个线程,此时AskThread对象还没有获取到自己需要的数据,执行到myRe=re.get()*re.get()时会阻塞。我们sleep1秒来模拟一个长时间的计算过程,把计算结果的执行结果告诉future,AskThread线程会继续执行。publicclassCalc{publicstaticIntegercalc(Integerpara){try{//模拟长时间执行Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}返回参数*参数;}publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{finalCompletableFuturefuture=CompletableFuture.supplyAsync(()->calc(50)).thenApply((i)->Integer.toString(i)).thenApply((str)->"\""+str+"\"").thenAccept(System.out::println);未来.get();}}CompletableFuture.supplyAsync方法构造一个CompletableFuture实例,在supplyAsync()方法中,它会在一个新的线程中执行传递的参数。这里会执行calc()方法,可能会比较慢,但这并不影响CompletableFuture实例的构建速度,supplyAsync()会立即返回。返回的CompletableFuture实例可以作为本次调用的契约,在未来的任何场合获取最终的计算结果。supplyAsync用于提供返回值。CompletableFuture还有一个不需要返回值的异步调用方法runAsync(Runnablerunnable)。一般我们在优化Controllers的时候会比较多的使用这种方式。如果不指定线程池,这两个方法都是在ForkJoinPool.common线程池中执行的,而这个线程池中的所有线程都是Daemon(守护)线程,所以当主线程结束时,这些线程不管执行完毕,系统将退出。核心代码:CompletableFuture.runAsync(()->this.afterBetProcessor(betRequest,betDetailResult,appUser,id));使用Callable实现异步调用:@RestControllerpublicclassHelloController{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(HelloController.class);@AutowiredprivateHelloService你好;@GetMapping("/helloworld")publicStringhelloWorldController(){returnhello.sayHello();/***异步调用restful*当controller的返回值为Callable时,springmvc会启动一个线程,将Callable交给TaskExecutor处理*然后DispatcherServlet和所有的spring拦截器退出主线程,然后保持responseopen*Callable执行结束,springmvc会重启并分配一个request请求,然后DispatcherServlet重新*调用并处理Callable异步执行的返回结果,然后返回视图**@return*/@GetMapping("/hello")publicCallablehelloController(){logger.info(Thread.currentThread().getName()+"进入helloController方法");可调用<字符串g>callable=newCallable(){@OverridepublicStringcall()throwsException{logger.info(Thread.currentThread().getName()+"进入调用方法");字符串say=hello.sayHello();logger.info(Thread.currentThread().getName()+"从helloService方法返回");返回说;}};logger.info(Thread.currentThread().getName()+"从helloController方法返回");返回可调用;}}异步调用方法WebAsyncTask:@RestControllerpublicclassHelloController{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(HelloController.class);@AutowiredprivateHelloService你好;/***带有超时的异步请求通过WebAsyncTask自定义客户端超时**@return*/@GetMapping("/world")publicWebAsyncTaskworldController(){logger.info(Thread.currentThread().getName()+"进入helloController方法");//如果3s没有返回,则认为超时WebAsyncTaskwebAsyncTask=newWebAsyncTask<>(3000,newCallable(){@OverridepublicStringcall()throwsException{logger.info(Thread.currentThread().getName()+"进入调用方法");Stringsay=hello.sayHello();logger.info(Thread.currentThread().getName()+"从helloService方法返回");returnsay;}});logger.info(Thread.currentThread().getName()+"从helloController方法返回");webAsyncTask.onCompletion(newRunnable(){@Overridepublicvoidrun(){logger.info(Thread.currentThread().getName()+"执行完成");}});webAsyncTask.onTimeout(newCallable(){@OverridepublicStringcall()抛出异常{logger.info(Thread.currentThread().getName()+"onTimeout");//当超时时,直接抛出异常,让外层处理超时异常thrownewTimeoutException("calltimeout");}});返回webAsyncTask;}/***异步调用,异常处理,详细处理流程见MyExceptionHandler类**@return*/@GetMapping("/exception")publicWebAsyncTaskexceptionController(){logger.info(Thread.currentThread().getName()+"进入helloController方法");Callablecallable=newCallable(){@OverridepublicStringcall()throwsException{logger.info(Thread.currentThread().getName()+"进入调用方法");thrownewTimeoutException("调用超时!");}};logger.info(Thread.currentThread().getName()+"从helloController方法返回");返回新的WebAsyncTask<>(20000,可调用e);}}2。增加嵌入式Tomcat最大连接数的代码如下:tomcatFactory.addConnectorCustomersTomcattomcatFactory.setPort(8005);tomcatFactory.setContextPath("/api-g");返回tomcat工厂;}classMyTomcatConnectorCustomizerimplementsTomcatConnectorCustomizer{publicvoidcustomize(Connectorconnector){Http11NioProtocolprotocol=(Http11NioProtocol.设置最大连接数protocol.setMaxConnections(20000);//设置最大线程数protocol.setMaxThreads(2000);protocol.setConnectionTimeout(30000);}}}3.使用@ComponentScan()定位扫描数据包比@SpringBootAp4.默认的Tomcat容器更改为Undertow。默认的Tomcat容器改为Undertow(Jboss下的服务器,Tomcat的吞吐量为5000,Undertow的吞吐量为8000)org.springframework.bootspring-boot-starter-tomcat改为:org.springframework.bootspring-boot-starter-undertow5.使用BufferedWriter进行缓冲。这里就不举例了,大家可以自己试试。6、Deferred方法实现异步调用代码如下:@RestControllerpublicclassAsyncDeferredController{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());私有最终LongTimeTask任务服务;@AutowiredpublicAsyncDeferredController(LongTimeTasktaskService){this.taskService=taskService;}@GetMapping("/deferred")publicDeferredResultexecuteSlowTask(){logger.info(Thread.currentThread().getName()+"进入executeSlowTask方法");DeferredResultdeferredResult=newDeferredResult<>();//调用长时间运行的任务taskService.execute(deferredResult);//当使用deferred.setResult("world");长时间运行的任务中的方法,它将从长时间运行的任务返回并在控制器中继续Processlogger.info(Thread.currentThread().getName()+"returnfromexecuteSlowTaskmethod");//超时回调方法deferredResult.onTimeout(newRunnable(){@Overridepublicvoidrun(){logger.info(Thread.currentThread().getName()+"onTimeout");//返回超时信息deferredResult.setErrorResult("timeout!");}});//处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法deferredResult.onCompletion(newRunnable(){@Overridepublicvoidrun(){logger.info(Thread.currentThread().getName()+"onCompletion");}});返回延迟结果;}}7.异步调用可以使用AsyncHandlerInterceptor进行截取代码如下:@ComponentpublicclassMyAsyncHandlerInterceptorimplementsAsyncHandlerInterceptor{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{//HandlerMethod句柄erMethod=(HandlerMethod)处理程序;logger.info(Thread.currentThread().getName()+"服务调用完成,返回结果给客户端");}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{if(null!=ex){System.out.println("发生异常:"+ex.getMessage());}}@OverridepublicvoidafterConcurrentHandlingStarted(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{//拦截后重写数据,将原来的helloworld替换为如下字符串Stringresp="mynameischhliu!";response.setContentLength(resp.length());response.getOutputStream().write(resp.getBytes());logger.info(Thread.currentThread().getName()+"进入afterConcurrentHandlingStarted方法");}}