需求
因为服务器内存不足,没有引入链路追踪的框架,项目使用到了Dubbo,所有通过self4j的MDC和Dubbo的RpcContext实现简单的traceId 的多服务模块的传递
解决思路
- 在第一个模块生成traceId(比如网关模块),生成traceId,并存入MDC,RpcContext,请求头等地方,使用完在响应时进行删除
- 如果是Dubbo的Rpc调用则能将traceId传递到服务提供者模块,服务端通过过滤器进行拦截,加入到自己的MDC,RpcContext,使用完后清除
- 如果是网关的转发请求,则通过请求头进行转发traceId,下游模块存入自己的MDC,RpcContext,使用完后清除
- 因为Dubbo的RpcContext每次调用服务端方法后,消费端的RpcContext就会被清除,所有通过MDC在每次rpc调用前通过调用前过滤器再加上traceId
版本
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.1.6</version></dependency>
解决方案
Dubbo消费端过滤器
就是调用服务器提供者的接口前,在消费端先过滤,加一点参数啥的,再转发
以下默认已经配置好,并写好了Dubbo远程调用服务,并能够进行调用。在此基础上进行过滤器添加。
1 . 代码
@Activate(group = {CommonConstants.CONSUMER}, order = -30000)
@Slf4j
public class RemoteTraceIdFilter implements Filter {private static final String TRACE_ID = "traceId";@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {// 消费者RpcServiceContext currentServiceContext = RpcContext.getCurrentServiceContext();boolean consumerSide = currentServiceContext.isConsumerSide();if (consumerSide) {String traceId = MDC.get(TRACE_ID);if(traceId==null){traceId = UUID.randomUUID().toString();}//消费者 将trace_id(业务流水号) set至上下文中RpcContext.getClientAttachment().setAttachment(TRACE_ID, traceId);} else {// 服务提供者
// String traceId = RpcContext.getClientAttachment().getAttachment(TRACE_ID);String traceId = RpcContext.getServerAttachment().getAttachment(TRACE_ID);if (traceId == null) {traceId = UUID.randomUUID().toString();}//slf4j 中设置了日志打印格式用作日志链路追踪MDC.put(TRACE_ID, traceId);}try {return invoker.invoke(invocation);} finally {if (RpcContext.getCurrentServiceContext().isProviderSide()) {MDC.remove(TRACE_ID);}else{RpcContext.getClientAttachment().removeAttachment(TRACE_ID);}}}
}
- 配置
1. 创建Resource相关配置(可以参考官方文档 官网)
--META-INF
----dubbo
------org.apache.dubbo.rpc.Filter文件内容(traceId是自定义的,具体看官网)
traceId = 类路径
Dubbo服务提供端过滤器
一个样不赘述
问题
- 每次RpcContext中存入值,在过滤器finally都要删除防止内存泄漏
- 每次RpcContext中存入值,并调用一次接口后,在消费端再次获取时会获取不到,因为已经被自动删除了,所有此处用了MDC配合RpcContext实现了traceId的保留。
具体看官网: 链路传递 - 侵入性比较高,实现也比较粗糙,如果条件允许建议直接使用sentinel等专业高级好用的框架直接实现链路的追踪
总结
此方式只是一个简单的实现,真实场景允许条件下直接使用sentinel等专门框架完成的限流链路追踪的功能。