⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7474019962719567898 「小小年纪不学好」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

今天突然接到了监控平台的电话,顿时有一种不好的预感。果然是我们模块把服务器干崩溃了。

事故

服务器某个接口一小时被访问了六百多万次,导致监控平台数据失真、瘫痪。

距离上次发版其实已经过去一周时间了,按理说这种问题应该立马爆出来才对,立即排查…

排查

这种问题定位起来比较容易,移动端只有一个地方调用了这个接口,大家review下代码能不能发现问题

public class Module {

privatestaticfinal ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

// 模块启动
public void moduleStart() {
// 定时任务,延迟 0 分钟开始,每隔 5 分钟执行一次
executor.scheduleAtFixedRate(() -> {
// 在子线程中执行任务
request(new Callback(){
@Override
public void onResponse(Data data) {
if (data == null) {
Log.w("data is null");
return;
}
...
}
});
}, 0, 5, TimeUnit.MINUTES);
}

public void moduleStop() {
}

}

就这么一段简单的代码有多少问题

  • 模块被多次拉起会不会创建多个定时任务?
  • 为什么5分钟请求一次,而不是下发数据更新通知?
  • 定时任务什么时候结束?
  • 线程池什么时候关闭?

解决措施

从上面的4个问题相信大家已经知道原因了

public class Module {

privatefinal ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

private ScheduledFuture<?> scheduledFuture;

public void moduleStart() {
// 如果已经创建就不创建了
if (scheduledFuture == null) {
scheduledFuture = executor.scheduleAtFixedRate(() -> {
// 在子线程中执行任务
request(new Callback(){
@Override
public void onResponse(Data data) {
if (data == null) {
Log.w("data is null");
return;
}
...
}
});
}, 0, 5, TimeUnit.MINUTES);
}


}

public void moduleStop() {
// 结束任务
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
scheduledFuture = null;
}
// 关闭线程池
executor.shutdown();
}

}

复盘

开发

开发自身素质肯定是第一责任人,前面提到的几个问题都没有考虑到

测试

1、为什么没测出来?了解原因后也是哭笑不得。

开发虽然这些简单问题没考虑到,但是容灾、逃生做的相当不错,接口异常并没有影响到正常业务(这里提出表扬)。

2、压测

虽然业务不受影响,但是压测后肯定能发现服务器异常。

压测变成了一种形式,更没有做到多端同步

review

更不用说了,国内同事深有感触,形式大于一切

日志

不知道大家注意到没有,当这个接口返回空数据后其实是有一行日志打印的,所以细心的同学完全可以在上线前避免这个问题。

日志的作用到底是什么?

  • 定位测试或线上反馈的问题,这应该是99%的用途了吧
  • 处理到位问题,日志就毫无用处了吗?显然不是。如果在开发编译运行阶段有时时观察日志,而不是只看自己想看的,这个问题早就被发现了

希望通过这个案例能让大家今后多注意日志的正确使用,测试反馈的日志定位完问题全部扫一遍,或者搜一下这次开发需求中的关键日志,看看有没有走到异常路径,避免线上事故。

文章目录
  1. 1. 事故
  2. 2. 排查
    1. 2.1. 解决措施
  3. 3. 复盘
    1. 3.1. 开发
    2. 3.2. 测试
      1. 3.2.1. 1、为什么没测出来?了解原因后也是哭笑不得。
      2. 3.2.2. 2、压测
    3. 3.3. review
    4. 3.4. 日志