<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 性能太差?試試這幾招!

          共 22175字,需瀏覽 45分鐘

           ·

          2024-06-25 12:20

          大家好,我是胖虎,給首先大家分享兩個(gè)產(chǎn)品

          無需魔法,國內(nèi)可直接使用官方ChatGPT-4(Plus)、ChatGPT-4o!

          Java八股文面試小程序!

          今天這篇文章介紹七種常見的Spring Boot 性能優(yōu)化方向

          1. 異步執(zhí)行

          實(shí)現(xiàn)方式二種:

          • 使用異步注解 @Aysnc、啟動(dòng)類:添加 @EnableAsync 注解
          • JDK 8 本身有一個(gè)非常好用的 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();  
                  //模擬長時(shí)間的計(jì)算過程  
                  Thread.sleep(1000);  
                  //告知完成結(jié)果  
                  future.complete(60);  
              }  
          }  

          在該示例中,啟動(dòng)一個(gè)線程,此時(shí) AskThread 對(duì)象還沒有拿到它需要的數(shù)據(jù),執(zhí)行到 myRe = re.get() * re.get() 會(huì)阻塞。

          我們用休眠 1 秒來模擬一個(gè)長時(shí)間的計(jì)算過程,并將計(jì)算結(jié)果告訴 future 執(zhí)行結(jié)果,AskThread 線程將會(huì)繼續(xù)執(zhí)行。

          public class Calc {  
              public static Integer calc(Integer para) {  
                  try {  
                      //模擬一個(gè)長時(shí)間的執(zhí)行  
                      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 方法構(gòu)造一個(gè) CompletableFuture 實(shí)例,在 supplyAsync() 方法中,它會(huì)在一個(gè)新線程中,執(zhí)行傳入的參數(shù)。

          在這里它會(huì)執(zhí)行 calc() 方法,這個(gè)方法可能是比較慢的,但這并不影響 CompletableFuture 實(shí)例的構(gòu)造速度,supplyAsync() 會(huì)立即返回。

          而返回的 CompletableFuture 實(shí)例就可以作為這次調(diào)用的契約,在將來任何場合,用于獲得最終的計(jì)算結(jié)果。

          supplyAsync 用于提供返回值的情況,CompletableFuture 還有一個(gè)不需要返回值的異步調(diào)用方法 runAsync(Runnable runnable),一般我們?cè)趦?yōu)化 Controller 時(shí),使用這個(gè)方法比較多。

          這兩個(gè)方法如果在不指定線程池的情況下,都是在 ForkJoinPool.common 線程池中執(zhí)行,而這個(gè)線程池中的所有線程都是 Daemon(守護(hù))線程,所以,當(dāng)主線程結(jié)束時(shí),這些線程無論執(zhí)行完畢都會(huì)退出系統(tǒng)。

          核心代碼:

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

          異步調(diào)用使用 Callable 來實(shí)現(xiàn):

          @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();  
              }  
            
              /**  
               * 異步調(diào)用restful  
               * 當(dāng)controller返回值是Callable的時(shí)候,springmvc就會(huì)啟動(dòng)一個(gè)線程將Callable交給TaskExecutor去處理  
               * 然后DispatcherServlet還有所有的spring攔截器都退出主線程,然后把response保持打開的狀態(tài)  
               * 當(dāng)Callable執(zhí)行結(jié)束之后,springmvc就會(huì)重新啟動(dòng)分配一個(gè)request請(qǐng)求,然后DispatcherServlet就重新  
               * 調(diào)用和處理Callable異步執(zhí)行的返回結(jié)果, 然后返回視圖  
               *  
               * @return  
               */    
              @GetMapping("/hello")  
              public Callable<String> helloController() {  
                  logger.info(Thread.currentThread().getName() + " 進(jìn)入helloController方法");  
                  Callable<String> callable = new Callable<String>() {  
            
                      @Override    
                      public String call() throws Exception {  
                          logger.info(Thread.currentThread().getName() + " 進(jìn)入call方法");  
                          String say = hello.sayHello();  
                          logger.info(Thread.currentThread().getName() + " 從helloService方法返回");  
                          return say;  
                      }  
                  };  
                  logger.info(Thread.currentThread().getName() + " 從helloController方法返回");  
                  return callable;  
              }  
          }  

          異步調(diào)用的方式 WebAsyncTask

          無需魔法,國內(nèi)可直接使用官方ChatGPT-4(Plus)、ChatGPT-4o!

          Java八股文面試小程序!

          @RestController    
          public class HelloController {  
            
              private static final Logger logger = LoggerFactory.getLogger(HelloController.class);  
            
              @Autowired    
              private HelloService hello;  
            
                  /**  
               * 帶超時(shí)時(shí)間的異步請(qǐng)求 通過WebAsyncTask自定義客戶端超時(shí)間  
               *  
               * @return  
               */    
              @GetMapping("/world")  
              public WebAsyncTask<String> worldController() {  
                  logger.info(Thread.currentThread().getName() + " 進(jìn)入helloController方法");  
            
                  // 3s鐘沒返回,則認(rèn)為超時(shí)  
                  WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, new Callable<String>() {  
            
                      @Override    
                      public String call() throws Exception {  
                          logger.info(Thread.currentThread().getName() + " 進(jìn)入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() + " 執(zhí)行完畢");  
                      }  
                  });  
            
                  webAsyncTask.onTimeout(new Callable<String>() {  
            
                      @Override    
                      public String call() throws Exception {  
                          logger.info(Thread.currentThread().getName() + " onTimeout");  
                          // 超時(shí)的時(shí)候,直接拋異常,讓外層統(tǒng)一處理超時(shí)異常  
                          throw new TimeoutException("調(diào)用超時(shí)");  
                      }  
                  });  
                  return webAsyncTask;  
              }  
            
              /**  
               * 異步調(diào)用,異常處理,詳細(xì)的處理流程見MyExceptionHandler類  
               *  
               * @return  
               */    
              @GetMapping("/exception")  
              public WebAsyncTask<String> exceptionController() {  
                  logger.info(Thread.currentThread().getName() + " 進(jìn)入helloController方法");  
                  Callable<String> callable = new Callable<String>() {  
            
                      @Override    
                      public String call() throws Exception {  
                          logger.info(Thread.currentThread().getName() + " 進(jìn)入call方法");  
                          throw new TimeoutException("調(diào)用超時(shí)!");  
                      }  
                  };  
                  logger.info(Thread.currentThread().getName() + " 從helloController方法返回");  
                  return new WebAsyncTask<>(20000, callable);  
              }  
            
          }  

          2. 增加內(nèi)嵌 Tomcat 的最大連接數(shù)

          代碼如下:

          @Configuration  
          public class TomcatConfig {  
              @Bean  
              public 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();  
                      //設(shè)置最大連接數(shù)  
                      protocol.setMaxConnections(20000);  
                      //設(shè)置最大線程數(shù)  
                      protocol.setMaxThreads(2000);  
                      protocol.setConnectionTimeout(30000);  
                  }  
              }  
            
          }  

          3. 使用 @ComponentScan()

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

          4. 默認(rèn) Tomcat 容器改為 Undertow

          默認(rèn) Tomcat 容器改為 Undertow(Jboss 下的服務(wù)器,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 進(jìn)行緩沖

          這里不給大家舉例,可自行嘗試。

          6. Deferred 方式實(shí)現(xiàn)異步調(diào)用

          代碼如下:

          @RestController  
          public class AsyncDeferredController {  
              private final Logger logger = LoggerFactory.getLogger(this.getClass());  
              private final LongTimeTask taskService;  
            
              @Autowired  
              public AsyncDeferredController(LongTimeTask taskService) {  
                  this.taskService = taskService;  
              }  
            
              @GetMapping("/deferred")  
              public DeferredResult<String> executeSlowTask() {  
                  logger.info(Thread.currentThread().getName() + "進(jìn)入executeSlowTask方法");  
                  DeferredResult<String> deferredResult = new DeferredResult<>();  
                  // 調(diào)用長時(shí)間執(zhí)行任務(wù)  
                  taskService.execute(deferredResult);  
                  // 當(dāng)長時(shí)間任務(wù)中使用deferred.setResult("world");這個(gè)方法時(shí),會(huì)從長時(shí)間任務(wù)中返回,繼續(xù)controller里面的流程  
                  logger.info(Thread.currentThread().getName() + "從executeSlowTask方法返回");  
                  // 超時(shí)的回調(diào)方法  
                  deferredResult.onTimeout(new Runnable(){  
            
             @Override  
             public void run() {  
              logger.info(Thread.currentThread().getName() + " onTimeout");  
              // 返回超時(shí)信息  
              deferredResult.setErrorResult("time out!");  
             }  
            });  
            
                  // 處理完成的回調(diào)方法,無論是超時(shí)還是處理成功,都會(huì)進(jìn)入這個(gè)回調(diào)方法  
                  deferredResult.onCompletion(new Runnable(){  
            
             @Override  
             public void run() {  
              logger.info(Thread.currentThread().getName() + " onCompletion");  
             }  
            });  
            
                  return deferredResult;  
              }  
          }  

          7. 異步調(diào)用可以使用 AsyncHandlerInterceptor 進(jìn)行攔截

          代碼如下:

          @Component  
          public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {  
            
           private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);  
            
           @Override  
           public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
             throws Exception {  
            return true;  
           }  
            
           @Override  
           public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
             ModelAndView modelAndView) throws Exception {  
          // HandlerMethod handlerMethod = (HandlerMethod) handler;  
            logger.info(Thread.currentThread().getName()+ "服務(wù)調(diào)用完成,返回結(jié)果給客戶端");  
           }  
            
           @Override  
           public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
             throws Exception {  
            if(null != ex){  
             System.out.println("發(fā)生異常:"+ex.getMessage());  
            }  
           }  
            
           @Override  
           public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)  
             throws Exception {  
            
            // 攔截之后,重新寫回?cái)?shù)據(jù),將原來的hello world換成如下字符串  
            String resp = "my name is chhliu!";  
            response.setContentLength(resp.length());  
            response.getOutputStream().write(resp.getBytes());  
            
            logger.info(Thread.currentThread().getName() + " 進(jìn)入afterConcurrentHandlingStarted方法");  
           }  
            
          }

          瀏覽 141
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产麻豆系列 | 日韩一级电影在线 | www.毛片网站 | 产一级a 超碰爱爱 | 青青草视频在线观看免费 |