线上Bug梳理
链接:https://juejin.cn/post/7294844864020430902
一、场景罗列
第一类:并发访问、异步编程、资源竞争
第二类:缓存相关,缓存一致性
第三类:脏数据、数据倾斜
第四类:边界值、超时、限流
第五类:服务器、硬件
第六类:程序代码
第七类:网络等其他
二、案例描述
非线程安全集合类
List<XXXDO> dataList = 从 DB 中获取结果集合
// 非线程安全集合了
List<XXXDO> successList = new ArrayList();
List<XXXDO> failList = new ArrayList();
// parallelStream 并行流中使用了不安全的集合
dataList.parallelStream().forEach(
vo -> {
…….
if(执行成功) {
successList.add(vo);
} else {
failList.add(vo);
}
}
);
ThreadLocal
// 正常情况能够执行 remove
try {
…
} finally {
threadLocalUser.remove();
}
// 错误使用
try {
…
// 业务异常, 未能执行 remove
threadLocalUser.remove();
} catch(Exception exception) {
…
}
修改成员变量
{
“authorized”:{
“themeHeader”:”交付授权协议”,
“contentDesc”:”交付工程师: ($userName$) 向您申请交付权限”,
“keyNote”:”特别提醒:如果不签署,交付工程师将无法进行交付”,
“redirectLinkText”:”立即前往授权”,
“tenantId”:”您所在的组织“$tenantId$”有以下权限申请需要授权”
}
}
// 从配置中心读取配置,用成员变量保存
public class CardSceneParamConfig implements XXXDataCallback {
// 从 nacos 配置读取初始化模板数据
private Map<String, AuthorizedCardParamVO> cardParamVO = new HashMap<>();
……
// 获取配置模板
public AuthorizedCardParamVO getAuthorizedCardParamVO(String sceneCode) {
return cardParamVO.get(sceneCode);
}
}
// 获取模板对象,修改了模板里面的占位符。
private AuthorizedCardParamVO xxx(AuthorizedCardParamVO stable, Map<String, String> params) {
……
final String contentDescStr = Optional.ofNullable(stable.getContentDesc())
.map(contentDesc -> contentDesc.replace(“$userName$”, params.get(“userName”)))
.orElse(stable.getContentDesc());
// 更改了成员变量
stable.setContentDesc(contentDescStr);
…….
return dynamic;
}
"contentDesc":"交付工程师: ($userName$) 向您申请交付权限"
"contentDesc":"交付工程师: (XXXX) 向您申请交付权限"
。异步依赖
List<XXXDO> dataList = 从 DB 中获取结果集合
// 非线程安全集合
List<XXXDO> successList = new ArrayList();
List<XXXDO> failList = new ArrayList();
for (XXXDO vo : dataList) {
ThreadUtil.execute(() -> {
// API 操作 vo
…….
if(执行成功) {
successList.add(vo);
} else {
failList.add(vo);
}
});
}
// 可能未获取运行结果就返回了
-
上传 excel 数据,到服务端解析,将解析结果上再传到 redis,redis 设置 1min 过期;解析这个过程也是一个异步行为。 -
客户端上传完成再点击提交数据,从刚才的 redis 取数据再保存到 DB 中。
-
未解析完成,提交时 redis 还没有数据 -
提交按钮迟了,redis 解析的数据过期
并发性修改
public class UnsafeConcurrencyExample {
private static int counter = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter++;
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter++;
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“Counter: ” + counter);
}
}
数据不一致
public class CacheExample {
// 创建缓存
private static Cache<String, Object> cache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存过期时间为10分钟
.build();
public static void main(String[] args) {
String key = “data”; // 缓存的键
// 从缓存中获取数据,如果缓存中不存在,则从数据库获取
Object data = cache.get(key, () -> fetchDataFromDB());
System.out.println(“Data: ” + data);
}
// 模拟从数据库获取数据
private static Object fetchDataFromDB() {
// 从数据库获取数据的逻辑
System.out.println(“Fetching data from DB…”);
return “Data from DB”;
}
}
未考虑优雅关闭
public class SimpleThreadPool {
private ExecutorService executor;
public SimpleThreadPool(int threads) {
executor = Executors.newFixedThreadPool(threads);
}
public void execute(Runnable task) {
executor.execute(task);
}
public void shutdown() {
executor.shutdown();
}
}
脏数据导致查询结果多条
com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: One record is expected, but the query result is multiple records] with root cause
边界值触发限流
机器中存在机器异常
-
分批发布时,没有做好机器的优雅下线 -
节点异常,没有剔除该 IP
-
下游 RPC 请求异常;该服务的依赖方异常 -
本机器请求异常 -
mq 消费异常 -
…….
因为磁盘打满而出现机器挂了
数据不在同一个事务内
// 假设这是一个转账操作,从账户A向账户B转账
updateBalance(connection, “B”, 100); // 向账户B添加100元
// A账户钱不够了
updateBalance(connection, “A”, -100); // 从账户A扣除100元
网络入口带宽不足
DDos攻击等导致正常用户异常
rpc 超时
内存泄漏
-
本来是单例的对象,但是却在每次执行方法时被创建 -
因为错误发生,这个方法被发送到 mq 进行重试 -
但是 mq 未设置最大重试次数 -
因为集群机器都监听这个 mq,导致错误被不断地发送到 mq,形成了死循环。对象被无限创建,导致集群机器内存全部飙高。
三、总结
-
合理的代码编写,很多问题都是编码导致,甚至还有很多低级错误 -
多考虑边界值,边界值常常因为不会发生而被忽略 -
合理的日志,方便排查,没有日志的异常增加排查难度 -
别随便转换异常,做好异常处理 -
压测,数据大会提前暴露并发相关问题 -
别吞掉异常,否则出现错误时不容易排查,偶发性问题就变成灵异事件了 -
机器一定要有完善的监控。包括上下游的监控,否则其中 1 个节点出现问题,整个链路都会因为这个节点出现偶发性的问题。 -
做好优雅关闭等
微信赞赏支付宝扫码领红包