一 线上问题
昨晚上突然接到公司紧急来电电桩设备大面积离线,意味着某市的车无法充电……”,细想这个平台很久都没有更新,最近出现问题是刚好在一个月前也是出现这种情况,再上一次就是几年前更新的。平台平时是稳定,开始找BUG ……
二 线上分析
1 查项目运行日记
整个充电桩平台两台线上云服务器十多个java项目服务开始排查,由于整个项目都是由本人设计并负责核心的开发所以很快就定位服务器和出问题的服务,查该服务日记,发现大量的报错如下图:
看到是MQ报的错,一堆事物未确认回滚的错误,马上想到队列存在积压问题,访问web管理平台查看队列情况,发现阻塞有几千设备发的消息。立马清除通道不保不延迟(设备要求不可以延迟),发现队列恢复正常。观察一几分种后又开始有堆积的现象,消费端出了问题,除了队列异常项目没有明显出错信息。
2 查服务器流量:
查流量,发现网络流量异常如下图:
波动如此大,基本确定与网络传送有关联。在查询端口报文数据分析也排除外网被攻击,很可能是报文实时分发环节出了问题,设备报文发送到另一家公司系统(业务要求桩设备上传的数据,实时上到另外一家公司,用于核查数据)。
3 查项目运行状态:
问题项目cpu每间隔一段时间占用很高如下图:
在升高的时候查看栈信息
jstack 22561 |grep 'pole'
说明: jstack 是查看java项目运行中的一些栈信息, grep 是过滤内容,不然输出信息太多看不了这么多,pole是本项目的相关代码内容,如果不了解项目代码也可以用Excption或Error代替。
找到相关代码(太急了没截到图),代码功能正是报文实时分发部分,也与上面流量分析相应证,为了再次确定,马上把分发代码注释,编译打包,重新发布项目后恢复正常。
4 优化代码,顺便解决历史安全问题
在协议处理实时分发关键代码(未修改前):
try {
String time=TimeFormatUtils.getTimeStrBy_yyyyMMddHHmmss(now);
String txtTime=org_hex.concat("$$").concat(time)
RedisTemplate sec= SecondRedisConnection.getSecondRedisTemplate();
sec.opsForList().leftPush("PUSH_TO_DB",txtTime);
return true;
} catch (Exception e) {
logger.error("分发报文错误", e);
return false;
}
在协议处理实时分发代码分离,不再实时处理(2秒以上延迟),交由专门的任务线程处理,代码修改后:
try {
String txtTime = org_hex.concat("$$").concat(time);
MemDataTable.HEX_PUSH_HEX.offer(txtTime);
return true;
} catch (Exception e) {
logger.error("分发报文错误", e);
return false;
}
public final static ArrayBlockingQueue<String> HEX_PUSH_HEX =new ArrayBlockingQueue<String>(300000);
public static boolean sendDbRedis = true;
public static long sendErr = 0;
/**
* 每2秒执行一次
*/
@Scheduled(cron = "*/2 * * * * *")
public void scheduled() {
if (MemDataTable.HEX_PUSH_HEX.size() > 0) {
RedisTemplate sec = SecondRedisConnection.getSecondRedisTemplate();
while (MemDataTable.HEX_PUSH_HEX.peek()!=null) {
String hex=MemDataTable.HEX_PUSH_HEX.poll();
if (sendDbRedis) {
try {
sec.opsForList().leftPush("PUSH_HEX", hex);
sendErr=0;
} catch (Exception e1) {
logger.error("分发报文错误", e1);
logger.error("分发报文异常 hex="+hex, e1);
if(sendErr>10){
sendDbRedis = false;
logger.error("分发报文终止 hex="+hex, e1);
}
sendErr++;
}
}else{
logger.error("====================分发报文,放弃:"+MemDataTable.HEX_PUSH_HEX.size());
MemDataTable.HEX_PUSH_HEX.clear();
}
}
}
}
代码分隔后,可以保证第三方网络断开与网络速度慢时影响到平台充电。
另外在修改项目代码准备发布前,想起这个项目用的apacheMQ是2019年安装(当时没有漏洞)在2023年10月份时报出严重漏洞,那时想过修复,跟业务提过,但由于线上不能停机,当时临时处理只配置了防火墙安全策略关了外网访问这些问题端口。开始换MQ后,各服务配置修改重新发布上线,
小结
有些情况不能按业务说得算。不然迟早要爆雷。