项目中出现ActiveMQ consumer log中出现断续情况,没有traceId输出
这样根本连不成一个链路来追踪问题了
原因:链路从主线程到子线程没有传递
解决方案:
@TraceCrossThread
@TraceCrossThread为skywalking提供的工具包中的注解,你可以认为其有一定的侵入性。使用方式则是在那个线程类上加上该注解,在类加载时,其构造方法会被代理agent做一次增强
@TraceCrossThread
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(TraceContext.traceId());
doNothing();
}
}
但是这种方式要去改ActiveMQ源码,不太合适
apm-jdk-threading-plugin
apm-jdk-threading-plugin这是官方提供的一个插件,是真正的无侵入了,使用方式也很简单,这个插件位于${skywalking_dir}/agent/bootstrap-plugins目录下,我们需要做的就是将其复制到${skywalking_dir}/agent/plugins目录下即可。
另外,还需要对代理的配置进行修改${skywalking_dir}/agent/config/agent.config,告诉代理对那些包下的线程池进行增强,配置如下:
plugin.jdkthreading.threading_class_prefixes=${SW_PLUGIN_JDKTHREADING_THREADING_CLASS_PREFIXES:}
可以采用配置覆盖的方式,系统环境变量配置:
SW_PLUGIN_JDKTHREADING_THREADING_CLASS_PREFIXES=org.springframework.jms.listener.DefaultMessageListenerContainer
参考:配置覆盖
ActiveMQ消费消息日志保持tid输出
还是为了借据ActiveMQ consumer 的log 输出中没有tid,在了解了skywalking plugin的源码后,对activemq-5.x-plugin官方插件进行改造
-
添加ActiveMessageListenerInstrumentation类,用于启用探针
#skywalking-plugin.def
activemq-5.x=org.apache.skywalking.apm.plugin.activemq.define.ActiveMessageListenerInstrumentation
public class ActiveMessageListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.activemq.ActiveMessageListenerInterceptor";
public static final String ENHANCE_CLASS_CONSUMER = "org.springframework.jms.listener.DefaultMessageListenerContainer";
public static final String ENHANCE_METHOD_DISPATCH = "doExecuteListener";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[0];
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named(ENHANCE_METHOD_DISPATCH);
}
@Override
public String getMethodsInterceptor() {
return INTERCEPTOR_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
@Override
protected ClassMatch enhanceClass() {
return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS_CONSUMER);
}
}
-
关键代码:
ENHANCE_CLASS_CONSUMER = "org.springframework.jms.listener.DefaultMessageListenerContainer";
ENHANCE_METHOD_DISPATCH = "doExecuteListener";
主要了解了Spring jms的源码后得知DefaultMessageListenerContainer是重用线程,用于异步消息监听,真正消费消息是最后调用了他的父类AbstractMessageListenerContainer#doExecuteListener方法,所以此做探针对该方法进行代理
-
具体方法拦截逻辑
public class ActiveMessageListenerInterceptor implements InstanceMethodsAroundInterceptor {
public static final String OPERATE_NAME_PREFIX = "ActiveMQ/";
public static final String CONSUMER_OPERATE_NAME_SUFFIX = "/OnMessage";
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
ContextCarrier contextCarrier = new ContextCarrier();
Message message = (Message) allArguments[1];
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
Object propertyValue = message.getStringProperty(next.getHeadKey());
if (propertyValue != null) {
next.setHeadValue(propertyValue.toString());
}
}
AbstractSpan localSpan = ContextManager.createEntrySpan(OPERATE_NAME_PREFIX + message.getJMSDestination() + CONSUMER_OPERATE_NAME_SUFFIX, contextCarrier);
localSpan.setComponent(ComponentsDefine.ACTIVEMQ_CONSUMER);
SpanLayer.asMQ(localSpan);
ContextManager.extract(contextCarrier);
}
···以下省略
拦截逻辑主要是对Message中的属性进行解析,因为skywalking会在mq头中加上相关属性。
通过ContestManager#createEntrySpan方法创建EntrySpan,最后使用ContextManager#extract,建立分布式调用关联;
-
参考1:org.apache.skywalking.apm.plugin.activemq.ActiveMQConsumerInterceptor源码
-
参考2:插件开发指南
-
最后package后替换插件到:/app/skywalking/agent/plugins/apm-activemq-5.x-plugin-xxx.jar