Dubbo分佈式日誌追蹤

使用dubbo分佈式框架進行微服務的開發,一個大系統每每會被拆分紅不少不一樣的子系統,而且子系統還會部署多臺機器,當其中一個系統出問題了,查看日誌十分麻煩。php

因此須要一個固定的流程ID和機器ip地址等來把全部的日誌進行染色處理,固然能夠經過調用其餘接口時參數進行傳遞,可是這樣子對代碼的耦合性太強,對代碼有侵入性。java

咱們能夠經過dubbo的filter 結合slf4j的MDC或者log4j2的ThreadContext的進行參數的注入,能夠直接在日誌文件中配置被注入的參數,這樣就對系統和日誌id打印進行了解耦。apache

其中當用logback日誌的時候是須要調用MDC的方法,而log4j2則須要調用ThreadContext的方法。服務器

 

下面的例子是使用slf4j的日誌模式:session

 

1.上游系統調用下游系統和下游系統接收上游系統定義兩個filter多線程

ProviderRpcTraceFilter(生產者)框架

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

/**
 * 日誌染色
 * @author phpdragon
 */
@Activate(group = {Constants.PROVIDER},order = 1)
public class ProviderRpcTraceFilter implements Filter {

    /**
     * 
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String traceId = RpcContext.getContext().getAttachment("trace_id");
        if (StringUtils.isBlank(traceId)) {
            traceId = this.getUUID() ;
        }

        //設置日誌traceId變量
        MDC.put("traceId", traceId);

        RpcContext.getContext().setAttachment("trace_id", traceId);

        try{
            return invoker.invoke(invocation);
        }finally {
            MDC.remove("traceId");
        }
    }

    /**
     * 獲取UUID
     * @return String UUID
     */
    public String getUUID(){
        String uuid = UUID.randomUUID().toString();
        //替換-字符
        return uuid.replaceAll("-", "");
    }

}

ConsumerRpcTraceFilter(消費者)dom

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

/**
 * 日誌染色ProviderRpcTraceFilter
 * @author phpdragon
 */
@Activate(group = {Constants.CONSUMER})
public class ConsumerRpcTraceFilter implements Filter {

    /**
     * 
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String traceId = MDC.get("traceId");
        if (StringUtils.isBlank(traceId)) {
            traceId = this.getUUID() ;
        }

        RpcContext.getContext().setAttachment("trace_id", traceId);
     return invoker.invoke(invocation);
    }

    /**
     * 獲取UUID
     * @return String UUID
     */
    public String getUUID(){
        String uuid = UUID.randomUUID().toString();
        //替換-字符
        return uuid.replaceAll("-", "");
    }

}

2.下游系統被調用的時候能夠經過dubbo中RpcContext.getAttachment()方法來獲取上游系統傳遞下來的值分佈式

String traceId = RpcContext.getContext().getAttachment("trace_id");

3.使用MDC來設置日誌變量 %X{traceId}ide

MDC.put("traceId", traceId);

4.在方法調用完成後移除該ID

try{
    return invoker.invoke(invocation);
}finally {
    MDC.remove("traceId");
}

5.當上遊系統調用下游系統的時候,能夠經過dubbo中RpcContext.setAttachment()方法進行參數傳遞

RpcContext.getContext().setAttachment("trace_id", traceId);

6.而後在/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter (或者 com.apache.dubbo.rpc.Filter ) 文件中配置filter

providerRpcTraceFilter=com.xxx.xxx.filter.ProviderRpcTraceFilter
consumerRpcTraceFilter=com.xxx.xxx.filter.ConsumerRpcTraceFilter

7.若是要打印服務器ip,使用com.alibaba.dubbo.common.utils.NetUtils工具獲取ip,而後put到MDC裏面

String serverIp = NetUtils.getLocalHost()
MDC.put("serverId", serverIp);

8.設置logback.xml 的日誌輸出格式

%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{serverId}] [%X{sessionId}] -%5p ${PID:-} [%15.15t] %-40.40logger{39} : %m%n
 

注意:

1. dubbo應用同時擔任provider、consumer時,RpcTraceFilter 不能合併成一個類,必須分開。

2.多線程的狀況下,會取不到這個ID,須要作處理,好比將建立線程的時候將id經過參數傳入(見:https://blog.csdn.net/qq_20641565/article/details/78628115

相關文章
相關標籤/搜索