文章 587
评论 5
浏览 230074
数据库连接池泄露检测:连接未归还导致耗尽?HikariCP leakDetectionThreshold 精准定位!

数据库连接池泄露检测:连接未归还导致耗尽?HikariCP leakDetectionThreshold 精准定位!

运营说后台管理页面打不开了。查日志,全是 HikariPool-1 - Connection is not available, request timed out after 30000ms。连接池配了 20 个连接,监控显示全部是 active,一个 idle 都没有。等了十分钟也没恢复——不是流量高,是连接被人借走没还。重启能好,但过两天又犯。 连接泄露比内存泄露更难排查。内存泄露至少还有 heap dump 可以分析,连接泄露你只能看到连接池满了,但看不到是谁借了没还、从哪里借的。等你发现的时候,池子已经空了,所有需要数据库的请求都在排队等超时。 今天聊聊怎么用 HikariCP 自带的一个配置项,把连接泄露的元凶精准揪出来。 连接是怎么被"偷"走的 正常情况下的连接使用流程是这样的: 借连接 → 执行 SQL → 还连接 Spring 的 @Transactional 和 JdbcTemplate 会帮你自动管理这个过程。方法结束,事务提交或回滚,连接自动归还池子。 但有些写法会悄悄地把连接"偷"走: 经典泄露场景:Stream 没关 // ❌ 连接泄露 jdbcTemp....

热点 Key 自动发现与本地缓存:Redis 热键打爆?Caffeine 二级缓存+过期抖动防雪崩!

热点 Key 自动发现与本地缓存:Redis 热键打爆?Caffeine 二级缓存+过期抖动防雪崩!

大促的时候,运营在首页挂了一个爆款商品。瞬间几十万用户涌进来,同一个商品详情接口被疯狂调用。Redis 里这个商品的缓存 Key 被打到单节点 QPS 上限,响应时间从 1ms 飙升到 50ms,Redis 线程池打满,带着其他 Key 的请求也一起慢了。一块热铁掉进水里,整锅水都烫了。 这就是典型的热点 Key 问题。一个 Key 太热,把 Redis 单节点打穿了。因为 Redis 是单线程处理命令的,一个慢不会拖累别的,但如果请求量超过这个单节点的处理能力上限,所有请求都得排队。 今天聊聊怎么用 Caffeine 本地缓存做二级缓存,配合过期时间抖动防止缓存雪崩。 热点 Key 为什么难搞 先搞清楚热点 Key 的问题本质。正常缓存访问是这样的: 请求 → Redis → 命中 → 返回(1ms) 热点 Key 的情况下: 1000 个并发请求 → Redis 同一个 Key │ └─ 1000 次网络 IO(即使是 Redis,单节点也有处理上限) 问题不在"数据能不能被缓存",而在"所有请求都打到了同一个 Redis 节点上"。如果你的 Redis 是集群模式,这个热点....

启动慢排查指南:Bean 初始化耗时 2 分钟?Profile 分析+懒加载优化,提速 50%!

启动慢排查指南:Bean 初始化耗时 2 分钟?Profile 分析+懒加载优化,提速 50%!

公司一个微服务,启动一次要两分多钟。每次发布都是煎熬——CI/CD 等两分钟,滚动更新每起一个新 Pod 又是两分钟,发个版十分钟起步。他们以为是 Spring Boot 就这样,直到有一天我在控制台加了一行 -Dspring-startup-analyzer,发现有个 Bean 的 @PostConstruct 里竟然在同步加载全量字典数据,光它一个就花了 40 秒。 Spring Boot 启动慢这件事,大多数时候不是你 Bean 太多,而是有几个 Bean 初始化的时候干了不该干的活。今天聊聊怎么把这几颗老鼠屎找出来,以及怎么做懒加载优化。 先定位:到底是哪些 Bean 在拖后腿 Spring Boot 启动慢,第一件事不是优化,是找到瓶颈。没有数据支撑的优化就是瞎改。 最简单的方式:Spring Boot Actuator 的 Startup 端点 Spring Boot 2.4+ 内置了一个 ApplicationStartup 机制。在配置文件里开一下就能用: # application.yml spring: application: startup: step-rec....

Prometheus 指标采集性能损耗:每秒万次采集拖慢应用?采样率调整+Pushgateway 缓冲!

Prometheus 指标采集性能损耗:每秒万次采集拖慢应用?采样率调整+Pushgateway 缓冲!

公司在做压测的时候发现一个奇怪的现象:QPS 跑到 8000 的时候,接口响应时间有个规律性的毛刺——每隔 15 秒,P99 延迟就会跳一下,从 50ms 飙到 200ms,持续一两秒又恢复正常。查了半天,根因是 Prometheus 来拉指标了。JVM 要遍历所有指标、序列化成文本、再通过网络发出去,这一整套操作每次都要几十毫秒。平时感觉不到,高并发下就成了定时炸弹。 Prometheus 好用,但它的 Pull 模式有个固有的问题:每次抓取,应用程序都要做一遍"遍历指标 → 格式化 → 输出"的全流程。 指标越多、频率越高,这个开销越不可忽略。 为什么 Pull 模式在高频采集下会出问题 Prometheus 默认每 15 秒拉一次指标。对于大多数服务来说,这个频率没啥问题——15 秒一次,每次几十毫秒,感知不到。 但如果你把采集频率调成了 5 秒、甚至 1 秒,或者你的应用里有大量自定义指标(几百个 Counter/Histogram),情况就不一样了。 每次采集,JVM 里发生的事情是: Prometheus 发 GET /metrics 请求 │ ├─ 遍历所有注册的指标(....

日志异步落盘阻塞优化:Logback AsyncAppender 队列满丢弃日志?CallerRuns 策略保关键日志!

日志异步落盘阻塞优化:Logback AsyncAppender 队列满丢弃日志?CallerRuns 策略保关键日志!

去年双十一,隔壁组出了个事故。交易系统在高并发的时候偶发超时,运维翻日志找原因,结果发现那个时间段的 ERROR 日志全是空的。排查了一圈才搞明白——Logback 设了 AsyncAppender,队列满了之后,新来的日志直接被丢弃了。问题日志没留下来,复盘都无从下手。 异步日志本来是提升性能的好东西,但如果没搞懂它"队列满了怎么办",关键时刻它会把你最需要的那条日志扔掉。 今天聊聊怎么用好 Logback 的异步日志——既不让日志拖慢业务线程,也不让关键日志在队列溢出时被丢弃。 同步日志慢在哪 先说清楚为什么需要异步。一次常规的日志写入,背后是好几步操作: 业务线程调用 log.info() │ ├─ 格式化:把参数拼进日志模板 → "订单 12345 支付成功" ├─ 编码:转成字节数组(UTF-8) ├─ 写磁盘:fsync 刷到文件 └─ 返回,业务线程继续 其中"写磁盘"这一步最慢。固态硬盘一次随机写入大约 0.1ms,机械硬盘可能到几毫秒。如果业务代码里日志打得很密,每次 log.info() 业务线程都要等磁盘写完才能继续——高并发下这个等待会非常可观。 异步日志....

文件预览安全沙箱:Office 文档含宏病毒?LibreOffice 隔离进程转换,防服务器感染!

文件预览安全沙箱:Office 文档含宏病毒?LibreOffice 隔离进程转换,防服务器感染!

去年一个做在线文档平台的哥们半夜被运维叫起来。服务器 CPU 打到 100%,磁盘疯狂读写,查了半天发现有人在后台跑挖矿脚本。溯源之后找到入口——一个用户上传的 .docm 文件,里面有段恶意 VBA 宏。系统调用 Office 转 PDF 的时候,宏被执行了,服务端直接中招。 这事儿比 SQL 注入还吓人。注入你得找到注入点,宏病毒你只要把文件上传上去,别人帮你打开,你就进去了。 文件预览几乎是所有企业应用的标配——合同管理要预览 PDF、OA 系统要预览 Word、网盘要预览 Excel。但很少有人意识到,每次把用户上传的 Office 文件扔进转换引擎,都等于在服务器上双击了一个陌生人发给你的附件。 今天聊聊怎么用进程隔离的思路,把这个风险降到最低。 宏病毒是怎么在服务端生效的 很多人觉得"我服务端又没有 Office 软件,哪里来的宏?" 但实际上,现在主流的文件预览方案底层都绕不开文档转换引擎。LibreOffice、OnlyOffice、Apache POI——它们做的事情就是把 docx / xlsx / pptx 转成 PDF 或者 HTML,然后前端渲染。而这些引....

图片压缩质量平衡:WebP 格式兼容性差?自动降级 JPEG + 智能画质调整,体积减半!

图片压缩质量平衡:WebP 格式兼容性差?自动降级 JPEG + 智能画质调整,体积减半!

上个月产品经理跑过来说,用户反馈有些手机上商品图不显示,全是裂图。我第一反应是 CDN 挂了,查了一圈没问题。后来发现是运营上传了一批 WebP 格式的图片,而部分老款 Android 机和 iOS 14 以下的 Safari 根本不支持 WebP。图片存了,用户看不见,等于没存。 WebP 确实是好东西——同样画质下体积只有 JPEG 的 60% 左右,做图片多的业务能省下一大笔带宽。但兼容性这个坑,处理不好就是事故。 今天聊聊怎么既能吃到 WebP 的红利,又不让老用户看到一屏裂图。 WebP 为什么好,又为什么烦 先看一组实测数据。同一张 1920×1080 的照片: 原始 PNG: 2.1MB JPEG 85%: 420KB WebP 85%: 260KB ← 比 JPEG 小 38% AVIF 85%: 180KB ← 更小,但兼容性更差 同样的视觉效果,WebP 能省将近一半的带宽。如果你的业务每天有 100 万次图片请求,平均每张 300KB,换成 WebP 后每张 180KB,一天能省 120GB 的流量。 但问题在哪?Can I Use 上 WebP 的支持率大约....

断点续传进度持久化:上传中断后从头开始?Redis 记录分片状态,秒级续传!

断点续传进度持久化:上传中断后从头开始?Redis 记录分片状态,秒级续传!

公司做视频平台,用户上传一个 2GB 的素材文件,进度条跑到 95%,浏览器崩了。刷新页面重新上传,进度条又从 0% 开始。用户直接关了页面,再也没回来。 后来加了断点续传。用户重新打开页面,后端一问 Redis——"这个文件你上次传了 38 个分片,还差 2 个",直接从第 39 片开始传。3 秒搞定,用户甚至没注意到断过。 大文件上传这种事,不怕慢,怕的是断了之后要从头再来。今天聊聊怎么用 Redis 记录分片上传状态,实现真正的秒级续传。 先说清楚:为什么大文件上传非得分片 一个 2GB 的文件你不可能一口气传上去。网络稍微波动一下,整个 HTTP 请求就废了,2GB 白传。 所以业内的标准做法是分片上传:客户端把文件切成小块(比如每片 5MB),一片一片发。服务端收齐所有分片后,按顺序合并成完整文件。 客户端: 文件 2GB → 切成 400 片,每片 5MB 上传过程: 上传第 1 片 ✓ 上传第 2 片 ✓ ... 上传第 399 片(网络断了!)✗ 上传第 400 片 ✗ 服务端: 收到 398 片 等待第 399、400 片(永远等不到) 分片之后,问题就变成了....

令牌桶限流突发流量处理:瞬间峰值超过阈值?预热机制+弹性令牌补充,不误杀正常请求!

令牌桶限流突发流量处理:瞬间峰值超过阈值?预热机制+弹性令牌补充,不误杀正常请求!

朋友公司做秒杀,限流用的是固定窗口计数器——每分钟最多 1000 个请求。每次秒杀开始的瞬间,前 0.5 秒冲进来 800 个请求,计数器直接打满,后面 59.5 秒的正常用户一个都进不来。更要命的是,这个计数器在第 60 秒重置,第 61 秒又是一波 800 个请求冲进来。限流器没有挡住流量洪峰,反而把正常用户拦在了门外。 固定窗口的问题是它不管流量分布。1000 个请求挤在前 5 秒和均匀分布在 60 秒里,对它来说是一样的——反正超了就拒。但业务上,前 5 秒的 800 个秒杀请求是正常的,后面的零散查询也是正常的。你需要的不是"别超过 1000",而是"别把服务器打爆"。 这就是令牌桶擅长的事。 令牌桶和固定窗口的差别在哪 固定窗口的逻辑很粗暴:一个计数器,到时间清零。令牌桶的思路完全反过来——以固定的速度往桶里放令牌,请求来了要先拿到令牌才能通过。 令牌桶模型: 令牌生成器 —→ 每秒放 100 个令牌 —→ [桶容量: 200] │ 请求到达 ────────────────────────────────┤ │ 有令牌?—→ 拿走一个,放行 │ 没令牌?—→ 拒绝 │....

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程