通过八种不同的方式增加SpringBoot吞吐量

方式一、使用异步注解@aysnc

用法1、

1.在启动类或者Controller类加上@EnableAsync注解

/**
 * @EnableAsync注解可以开启多线程,
 * 可标注在方法、类上
 */
@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.在需要进行异步处理的方法上加@Async注解

/**
 * 无参返回方法
 * 使用@Async注解的类需要是spring管理的类
 */
@Async
public void testAsync() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    System.out.println(Thread.currentThread().getName() + ":void testAsync1(),耗时:" +     
    (endTime - startTime));
}


/**
 * 有参返回方法
 * 使用@Async注解的类需要是spring管理的类
 */
@Override
@Async
public Future<String> testAsync2() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    return new AsyncResult<>("testAsync4:耗时:" + (endTime - startTime));
}

用法2

通用配置类

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步任务通用配置类
 */
@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(5);
        // 设置最大线程数
        executor.setMaxPoolSize(10);
        // 设置队列容量
        executor.setQueueCapacity(20);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("Test-Async");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

方法上添加注解

@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()+ "服务调用完成,返回结果给客户端");
 }
 
 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
   throws Exception {
  if(null != ex){
   System.out.println("发生异常:"+ex.getMessage());
  }
 }
 
 @Override
 public 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方法");
 }
 
}

方式二、采用缓存方式存储部分固定数据

将部分热点数据或者静态数据放到本地缓存或者Redis中,如果有需要可以定时更新缓存数据

方式三、业务拆分

将比较耗时或者不同的业务拆分出来提供单节点的吞吐量

方式四、增加内嵌 Tomcat 的最大连接数

@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();
            //设置最大连接数
            protocol.setMaxConnections(20000);
            //设置最大线程数
            protocol.setMaxThreads(2000);
            protocol.setConnectionTimeout(30000);
        }
    }
}

方式五、使用 @ComponentScan()

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

方式六、默认 Tomcat 容器改为 Undertow

默认 Tomcat 容器改为 Undertow。

Undertow是Jboss 下的服务器,Tomcat 吞吐量 5000,Undertow 吞吐量 8000。

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

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

方式七、Deferred 方式实现异步调用

@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() + "进入executeSlowTask方法");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        // 调用长时间执行任务
        taskService.execute(deferredResult);
        // 当长时间任务中使用deferred.setResult("world");
        // 这个方法时,会从长时间任务中返回,继续controller里面的流程
        logger.info(Thread.currentThread().getName() + "从executeSlowTask方法返回");
        // 超时的回调方法
        deferredResult.onTimeout(new Runnable(){
             @Override
             public void run() {
                 logger.info(Thread.currentThread().getName() + " onTimeout");
                 // 返回超时信息
                 deferredResult.setErrorResult("time out!");
             }
             });
        
             // 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
             deferredResult.onCompletion(new Runnable(){

             @Override
             public void run() {
                 logger.info(Thread.currentThread().getName() + " onCompletion");
             }
             });
             return deferredResult;
    }
}

方式八、异步调用可以使用 AsyncHandlerInterceptor 进行拦截

新建拦截器类,代码如下

@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()+ "服务调用完成,返回结果给客户端");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) throws Exception {
        if(null != ex){
            System.out.println("发生异常:"+ex.getMessage());
        }
    }

    @Override
    public 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方法");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值