• Dubbo 分布式 日志 追踪


    使用dubbo分布式框架进行微服务的开发,一个大系统往往会被拆分成很多不同的子系统,并且子系统还会部署多台机器,当其中一个系统出问题了,查看日志十分麻烦。

    所以需要一个固定的流程ID和机器ip地址等来把所有的日志进行染色处理,当然可以通过调用其他接口时参数进行传递,但是这样子对代码的耦合性太强,对代码有侵入性。

    我们可以通过dubbo的filter 结合slf4j的MDC或者log4j2的ThreadContext的进行参数的注入,可以直接在日志文件中配置被注入的参数,这样就对系统和日志id打印进行了解耦。

    其中当用logback日志的时候是需要调用MDC的方法,而log4j2则需要调用ThreadContext的方法。

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

    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 extends 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

    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 extends 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}

    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

    9.最后通过Kibana对日志进行收集归总,使用trace_id筛选,快速定位到错误位置。

    最终效果如下:

    注意:

    1. dubbo应用同时担任provider、consumer时,RpcTraceFilter 不能合并成一个类,必须分开。

    2.多线程的情况下,会取不到这个ID,需要做处理,比如将创建线程的时候将id通过参数传入(见:Dubbo分布式日志追踪,多线程不能获取窜ID和IP问题

    PS:

    https://blog.csdn.net/qq_20641565/article/details/78627202

    https://blog.csdn.net/qq_20641565/article/details/78628115

    https://www.jianshu.com/p/3dca4aeb6edd

    https://blog.csdn.net/weixin_39178876/article/details/85088410

    https://blog.csdn.net/xiaolyuh123/article/details/80560662

  • 相关阅读:
    linux c++爬虫(一)
    计算进程消费cpu和内存
    ZooKeeper server &&client
    ZooKeeper 数据模型
    转载 分布式协调技术 分布式锁
    转载 ~shell简介
    增删改查时要注意的几个地方
    搜索时,大小写问题
    ajax的一些实用技巧
    向redis插入数据时,返回值问题
  • 原文地址:https://www.cnblogs.com/phpdragon/p/10520621.html
Copyright © 2020-2023  润新知