Dubbo鏈路追蹤——生成全局ID(traceId)

全局 traceId

關於鏈路追蹤,在微服務的趨勢下,一次調用的日誌信息分佈在不一樣的機器上或目錄下,當須要看一條鏈路調用全部的日誌信息時,這是個比較困難的地方,咱們雖然有ELK , Sentry等日誌異常收集分析工具, 可是如何把信息串起來也是一個關鍵的問題。 咱們通常的作法是在系統調用開始時生成一個traceId , 而且它伴隨着一次調用的整個生命週期 。 當一個服務調用另一個服務的時候,traceId 則向下透傳,全局使用惟一一個。html

1、經過修改Dubbo源碼實現全局traceId(侵入性高不建議使用)

咱們經過分析源碼能夠知道客戶端在調用服務段進行服務消費時,實際上發送的是封裝過的Request實體 ,Data爲Invocation實體對象(接口簽名,參數類型,參數值,及attachment附件)java

final class HeaderExchangeChannel implements ExchangeChannel {
...
    public ResponseFuture request(Object request, int timeout) throws RemotingException {
        if (closed) {
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
        }
        // create request.
        Request req = new Request();
        req.setVersion("2.0.0");
        req.setTwoWay(true);
        req.setData(request);
        DefaultFuture future = new DefaultFuture(channel, req, timeout);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }
...
}

經過源碼可知 request是入參,其餘參數均爲固定,因此只能在request中作文章。apache

代碼涉及到的類有

  • TraceIdUtil : 鏈路追蹤全局ID生成器
  • InvokerInvocationHandler : 消費端Invocation處理器
  • DubboProtocol : Dubbo協議處理入口,內有服務提供者處理請求數據信息的Handler處理器

TraceIdUtil源碼以下dom

public class TraceIdUtil {

	private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<String>();

	public static String getTraceId() {
		if(TRACE_ID.get() == null) {
			String s = UUID.randomUUID().toString();
			setTraceId(s);
		}
		return TRACE_ID.get();
	}

	public static void setTraceId(String traceId) {
		TRACE_ID.set(traceId);
	}
}

Dubbo線程模型圖示可知,Dubbo客戶端調用實際上經過JavassistProxyFactory獲取的是Proxy代理對象。 Dubbo線程模型圖示 代碼以下ide

public class JavassistProxyFactory extends AbstractProxyFactory {
	...
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
	...
}

其中 InvokerInvocationHandler 調用數據的處理及rpc調用實體的封裝過程。因此咱們須要在調用的起始位置添加traceId信息 。 traceId微服務

其次是服務提供者進行請求處理過程: DubboProtocol的requestHandler 請求處理器(不明白請詳讀服務Dubbo服務暴露過程源碼) traceId工具

將traceId 從RpcInvocation 的attachment屬性中取出 ,傳給TraceIdUtil 方面後續調用過程使用。this

擴展Provider端Filter (無侵入性,建議使用)

建立Filter 服務提供者端擴展線程

/**
 * Created with IntelliJ IDEA.
 *
 * @author: bakerZhu
 * @description:
 * @time: 2018年09月09日
 * @modifytime:
 */
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER} , order = -9999)
public class GlobalTraceFilter implements Filter {

	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		String traceId = invocation.getAttachment("traceId");
		if(!StringUtils.isBlank(traceId)) {
			RpcContext.getContext().setAttachment("traceId",traceId);
		}else { // 第一次發起調用
			RpcContext.getContext().setAttachment("traceId", UUID.randomUUID().toString());
		}
		return invoker.invoke(invocation);
	}
}

資源文件夾下建立 META-INF/dubbo 文件夾 建立com.alibaba.dubbo.rpc.Filter 文件,並編輯文件內容 gtrace=com.alibaba.dubbo.rpc.filter.GlobalTraceFilter3d

疑惑點 爲何設置Dubbo上下文的attachment調用時會出如今 RpcInvocation中

詳見 AbstractInvoker.invoke(Invocation inv) 方法

public abstract class AbstractInvoker<T> implements Invoker<T> {
    public Result invoke(Invocation inv) throws RpcException {
		......
        Map<String, String> context = RpcContext.getContext().getAttachments();
        if (context != null) {
            invocation.addAttachmentsIfAbsent(context);
        }
        if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
            invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
        }
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
		......
    }
}

將上下文的「附件信息」拷貝到RpcInvocation中

讚揚支持

讚揚支持

相關文章
相關標籤/搜索