共计 1580 个字符,预计需要花费 4 分钟才能阅读完成。
痛点分析
在高并发场景下,SpringBoot 应用常会遇到以下典型性能问题:

- 线程竞争:默认的 Tomcat 线程池配置(如 200 线程)在突发流量下容易耗尽,导致请求排队甚至被拒绝
- 缓存穿透:大量查询不存在的 Key 导致直接击穿到数据库
- 同步阻塞:耗时操作(如 IO 调用)占用工作线程,降低系统吞吐量
技术方案
1. 动态线程池配置
SpringBoot 2.3+ 支持通过配置文件动态调整线程池参数:
server:
tomcat:
threads:
max: 500 # 最大线程数
min-spare: 50 # 核心线程数
accept-count: 100 # 等待队列长度
关键参数说明:
- 建议
max值不超过(CPU 核心数 *2)+1的 3 倍 accept-count过大可能导致旧请求超时- 配合
ThreadPoolTaskExecutor可实现业务线程池隔离
2. 多级缓存设计
推荐组合方案:
- 本地缓存:Caffeine(比 Guava Cache 性能高 30%)
- 分布式缓存:Redis 集群(处理缓存击穿)
- 数据库:最终数据源
防雪崩的缓存空值示例:
@Cacheable(value="users", key="#id",
unless="#result == null") // 不缓存 null
public User getUser(Long id) {//...}
3. 异步化改造
两种实现方式对比:
| 方案 | 适用场景 | 注意事项 |
|---|---|---|
| @Async | 简单异步调用 | 需启用 @EnableAsync |
| CompletableFuture | 需要编排多个异步任务 | 注意线程池隔离 |
代码示例
线程池配置类
@Configuration
public class ThreadPoolConfig {@Bean("ioThreadPool")
public Executor ioIntensiveTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 常驻线程数
executor.setMaxPoolSize(50); // 最大扩容线程数
executor.setQueueCapacity(100); // 队列容量
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 降级策略
return executor;
}
}
缓存雪崩防护
通过 SPEL 实现随机过期时间:
@Cacheable(value="products",
key="#id",
expire="T(java.util.concurrent.ThreadLocalRandom).current()
.nextInt(3600, 7200)") // 1- 2 小时随机过期
public Product getProduct(String id) {...}
避坑指南
- 线程上下文丢失:
- 异步线程无法获取 RequestContext
-
解决方案:使用
TaskDecorator传递上下文 -
异步事务边界:
- @Async 方法默认不继承事务
-
需要显式添加
@Transactional注解 -
缓存一致性:
- 先更新 DB 再删除缓存(双删)
- 引入消息队列保证最终一致性
验证指标
JMeter 压测对比
| 优化项 | QPS | 平均响应时间 |
|---|---|---|
| 默认配置 | 1200 | 450ms |
| 优化后 | 3800 | 120ms |
Arthas 监控
通过 thread -b 命令可发现阻塞线程:
[arthas@12345]$ thread -b
"http-nio-8080-exec-1" Id=27 BLOCKED on java.lang.Object@1a2b3c4d
开放性问题
在实际项目中,您如何权衡以下矛盾:
- 缓存时效性要求高(需要频繁更新)vs 数据库压力
- 线程池大小设置偏向突发流量处理 vs 日常资源占用
- 本地缓存的数据一致性 vs 性能收益
正文完
发表至: 技术分享
近三天内
