故障流程描述和现象
5.04分运维开始构建线上A环境工程。
5.04分运维开始构建线上B环境工程。
5.09分运维开始构建线上A环境完成。
5.10分运维开始构建线上B环境完成。
5.15分运维查看服务运行状况,无异常。
5.17分运维查看grafana kafka topic各项指标,发现异常。
5.20分运维回滚AB环境服务
5.30分开发开始再次审查代码,并发现问题所在。
由于服务数据传递重度依赖kafka,观察topic流入流出指标必不可少
背景描述(简述版)
本次新增功能为Trace转Opentelemetry协议。
那么trace是怎么来的?apm-dc-frontend将收到的pb格式的trace数据通过kafka发送给apm-dc-wrap。
dc-wrap需要将pb格式的数据转化为程序可处理的Trace对象,并做一些属性填充完善「消耗CPU比较高」,
转成Trace后再转为Opentelemetry协议,并发送第三方客户的kafka集群。
但是线上这个功能开关是不开启的,只是保证以后基于这个版本建立私有化分支,携带这个功能,不用再merge代码。
@Override
public boolean accept(Command command) {
logger.debug("The status[{}] of the otlp parse", otlpEnable);
//otlpEnable开关为关闭,那么即使消费到消息,也不会做转换。
return super.accept(command) && otlpEnable && command instanceof JsonActionTraceCommand;
}
问题根因
在判断是否accept这条消息的时候,代码已经将pb数据转为trace了「耗时」。由于又新建了一个消费者组,导致这个耗时的过程*2「其他业务也需要将pb转为trace」。
虽然otlpEnable开关没打开,但是有额外的资源浪费。
问题解决
新增一个Condition 读取nacos配置,用来决定Bean是否需要被初始化并start。
public class OtelConsumerCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
String property = conditionContext.getEnvironment().getProperty("apm.dc.pipeline.otlp-parse.enable");
if (StringUtils.isNotEmpty(property)) {
return Boolean.parseBoolean(property);
}
return false;
}
}
@Bean(initMethod = "start", destroyMethod = "shutdown")
@Conditional(OtelConsumerCondition.class)
public MessageConsumer otlpMultiThreadMessageConsumer(KafkaMQSubscriber perfMessageQueueSubscriber,
@Qualifier("otlpTraceDataHandler") RawDataMessageHandler perfDataMultiThreadHandler){
MessageConsumer consumer = new MessageConsumer();
//·········
}
总结
造成断崖式上升应该是代码哪里拥塞住了,结合async-profiler,调出火焰图去查一下,大概就可以定位问题所在。
测试环境没有查出是因为数据量不够,导致问题没有暴露出来,因此做压力测试还是很有必要的。