我是如何把SpringBoot项目的并发提升十倍量级的

背景

生产环境偶尔会有一些慢请求导致系统性能下降,吞吐量下降,下面介绍几种优化建议。

方案

1、undertow替换tomcat

电子商务类型网站大多都是短请求,一般响应时间都在100ms,这时可以将web容器从tomcat替换为undertow,下面介绍下步骤:1、增加pom配置

  1.  
    <dependency>
  2.  
        <groupid>org.springframework.boot</groupid>
  3.  
        <artifactid>spring-boot-starter-web</artifactid>
  4.  
        <exclusions>
  5.  
           <exclusion>
  6.  
              <groupid>org.springframework.boot</groupid>
  7.  
              <artifactid>spring-boot-starter-tomcat</artifactid>
  8.  
           </exclusion>
  9.  
        </exclusions>
  10.  
    </dependency>
  11.  
    <dependency>
  12.  
        <groupid>org.springframework.boot</groupid>
  13.  
        <artifactid>spring-boot-starter-undertow</artifactid>
  14.  
    </dependency>

2、增加相关配置

  1.  
    server:
  2.  
      undertow:
  3.  
        direct-buffers: true
  4.  
        io-threads: 4
  5.  
        worker-threads: 160

重新启动可以在控制台看到容器已经切换为undertow了

2、缓存

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

3、异步

在代码过程中我们很多代码都不需要等返回结果,也就是部分代码是可以并行执行,这个时候可以使用异步,最简单的方案是使用springboot提供的@Async注解,当然也可以通过线程池来实现,下面简单介绍下异步步骤。1、pom依赖 一般springboot引入web相关依赖就行

  1.  
    <dependency>
  2.  
        <groupid>org.springframework.boot</groupid>
  3.  
        <artifactid>spring-boot-starter-web</artifactid>
  4.  
    </dependency>

2、在启动类中增加@EnableAsync注解

  1.  
    @EnableAsync
  2.  
    @SpringBootApplication
  3.  
    public class AppApplication
  4.  
    {
  5.  
        public static void main(String[] args)
  6.  
        {
  7.  
            SpringApplication.run(AppApplication.class, args);
  8.  
        }
  9.  
    }

3、需要时在指定方法中增加@Async注解,如果是需要等待返回值,则demo如下

  1.  
    @Async
  2.  
    public Future<string> doReturn(int i){
  3.  
        try {
  4.  
            // 这个方法需要调用500毫秒
  5.  
             Thread.sleep(500);
  6.  
         } catch (InterruptedException e) {
  7.  
           e.printStackTrace();
  8.  
        }
  9.  
        / 消息汇总
  10.  
        return new AsyncResult&lt;&gt;("异步调用");
  11.  
    }

4、如果有线程变量或者logback中的mdc,可以增加传递

  1.  
    import org.slf4j.MDC;
  2.  
    import org.springframework.context.annotation.Configuration;
  3.  
    import org.springframework.core.task.TaskDecorator;
  4.  
    import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
  5.  
    import org.springframework.scheduling.annotation.EnableAsync;
  6.  
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  7.  
     
  8.  
    import java.util.Map;
  9.  
    import java.util.concurrent.Executor;
  10.  
     
  11.  
    /**
  12.  
     * @Description:
  13.  
     */
  14.  
    @EnableAsync
  15.  
    @Configuration
  16.  
    public class AsyncConfig extends AsyncConfigurerSupport {
  17.  
        @Override
  18.  
        public Executor getAsyncExecutor() {
  19.  
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  20.  
            executor.setTaskDecorator(new MdcTaskDecorator());
  21.  
            executor.initialize();
  22.  
            return executor;
  23.  
        }
  24.  
    }
  25.  
     
  26.  
    class MdcTaskDecorator implements TaskDecorator {
  27.  
     
  28.  
        @Override
  29.  
        public Runnable decorate(Runnable runnable) {
  30.  
            Map<string, string> contextMap = MDC.getCopyOfContextMap();
  31.  
            return () -&gt; {
  32.  
                try {
  33.  
                    MDC.setContextMap(contextMap);
  34.  
                    runnable.run();
  35.  
                } finally {
  36.  
                    MDC.clear();
  37.  
                }
  38.  
            };
  39.  
        }
  40.  
    }

5、有时候异步需要增加阻塞

  1.  
    import lombok.extern.slf4j.Slf4j;
  2.  
    import org.springframework.context.annotation.Bean;
  3.  
    import org.springframework.context.annotation.Configuration;
  4.  
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  5.  
     
  6.  
    import java.util.concurrent.Executor;
  7.  
    import java.util.concurrent.ThreadPoolExecutor;
  8.  
     
  9.  
    @Configuration
  10.  
    @Slf4j
  11.  
    public class TaskExecutorConfig {
  12.  
     
  13.  
        @Bean("localDbThreadPoolTaskExecutor")
  14.  
        public Executor threadPoolTaskExecutor() {
  15.  
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  16.  
            taskExecutor.setCorePoolSize(5);
  17.  
            taskExecutor.setMaxPoolSize(200);
  18.  
            taskExecutor.setQueueCapacity(200);
  19.  
            taskExecutor.setKeepAliveSeconds(100);
  20.  
            taskExecutor.setThreadNamePrefix("LocalDbTaskThreadPool");
  21.  
            taskExecutor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor executor) -&gt; {
  22.  
                        if (!executor.isShutdown()) {
  23.  
                            try {
  24.  
                                Thread.sleep(300);
  25.  
                                executor.getQueue().put(r);
  26.  
                            } catch (InterruptedException e) {
  27.  
                                log.error(e.toString(), e);
  28.  
                                Thread.currentThread().interrupt();
  29.  
                            }
  30.  
                        }
  31.  
                    }
  32.  
            );
  33.  
            taskExecutor.initialize();
  34.  
            return taskExecutor;
  35.  
        }
  36.  
     
  37.  
     
  38.  
    }

4、业务拆分

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

5、集成消息队列

有很多场景对数据实时性要求不那么强的,或者对业务进行业务容错处理时可以将消息发送到kafka,然后延时消费。举个例子,根据条件查询指定用户发送推送消息,这里可以时按时、按天、按月等等,这时就 

 

posted @ 2021-06-11 10:30  疯子110  阅读(1443)  评论(0编辑  收藏  举报