dubbo应用笔记(2)基于Filter、RpcContext隐藏参数及SLF的MDC的服务调用链日志跟踪

使用dubbo做分布式服务,当查看日志时,需要在多个应用中对日志进行查询;若一个接口被多个客户端同时调用,则会出现日志查找辨别非常困难,无法及时定位错误。

本示例基于MVC拦截器、Dubbo的Filter及SLF的MDC实现,原理为在客户端调用http接口时,利用MVC拦截器,在MDC中放置一个reqId,并在logback日志中对此reqId进行输出;当此次请求进行RPC请求时,Filter会获取MDC中的reqId,并将此reqId以隐藏参数的形式传递给服务提供者;服务提供者的Filter会再将隐藏参数中的reqId放到服务端调用的MDC中,从而实现整个调用流程使用同一个reqId;

1、服务端实现

1.1、源码目录

服务端源码目录.png

目录说明:
dubbo.trace.server.api包:此包为接口及接口实现;
dubbo.trace.server.config包:此包配置加载dubbo的配置;
dubbo.trace.server.filter包:服务端Filter实现;
resource/config目录:logback日志配置及dubbo配置;
resource/config/META-INF/dubbo目录:dubbo的SPI扩展目录,本处扩展了com.alibaba.dubbo.rpc.Filter接口;

1.2、接口及实现

接口:

public interface DubboTraceApi {
    public String echoTest(String msg);
}

实现:

public class DubboTraceApiImpl implements DubboTraceApi {

    private static Logger logger = LoggerFactory.getLogger(DubboTraceApiImpl.class);

    @Override
    public String echoTest(String msg){
        logger.info("echoTest:{}", msg);
        return msg;
    }
}

1.3、Filter实现

public class ServerTraceFilter implements Filter {

    private static Logger logger = LoggerFactory.getLogger(ServerTraceFilter.class);

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        logger.info("####className:{}", invocation.getClass().getName());
        logger.info("####methodName:{}", invocation.getMethodName());

        //获取appCode及secretKey
        String appCode = RpcContext.getContext().getAttachment("appCode");
        String secretKey = RpcContext.getContext().getAttachment("secretKey");

        if(Strings.isNullOrEmpty(appCode) || Strings.isNullOrEmpty(secretKey)){
            throw new RpcException("Sorry, your access is denied!");
        }

        logger.info("appCode:{}", appCode);

        //获取reqId,若没有,则通过UUID生成一个;然后将reqId放到MDC中,便于日志中打印
        String reqId = RpcContext.getContext().getAttachment("reqId");
        reqId = !Strings.isNullOrEmpty(reqId) ? reqId : UUID.randomUUID().toString();
        MDC.put("appCode", appCode);
        MDC.put("reqId", reqId);

        logger.info("reqId:{}", reqId);
        if(!appCode.equals("zhaozhou11") || !secretKey.equals("666666")){
            throw new RpcException("your appCode or secretKey is error!");
        }

        return invoker.invoke(invocation);
    }
}

本处主要获取appCode、secretKey及reqId,对appCode及secretKey进行验证,将appCode及reqId放到MDC中;

1.4、dubbo服务端配置

dubbo-rpc-provider.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="privider-api-test"  />

    <!-- 使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry id="zookeeperRegistry" protocol="zookeeper" address="zookeeper://127.0.0.1:2181" check="false" />

    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />


    <dubbo:provider filter="traceFilter" ></dubbo:provider>

    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service id="dubboTestApi" interface="dubbo.trace.server.api.DubboTraceApi" ref="dubboTraceApiImpl" cache="false" />

    <!-- 和本地bean一样实现服务 -->
    <bean id="dubboTraceApiImpl" class="dubbo.trace.server.api.DubboTraceApiImpl" />
</beans>

主要配置注册中心、暴露协议、服务提供者等,重点是设置provider的Filter为traceFilter,此处的traceFilter是在META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件中进行设置的;

设置如下:

traceFilter=dubbo.trace.server.filter.ServerTraceFilter

1.5、logback日志配置

logback日志配置就不全贴出来了,只贴出日志输出格式的设置,如下:

<property name="patternlayout" value="==%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L [%X{appCode}]-[%X{reqId}] - %msg%n"/>

格式中变量appCode及reqId即为在Filter中设置的MDC值。

2、客户端实现

2.1、源码目录

客户端源码目录.png

目录说明:
dubbo.trace.client.config包:客户端的bean配置加载,包括dubbo及接口拦截器;
dubbo.trace.client.controller包:controller类;
dubbo.trace.client.filter包:客户端的Filter实现;
resource/config目录:dubbo客户端配置及logback配置;
resource/META-INF/dubbo目录:dubbo的SPI扩展配置;
resource/templates目录:页面资源目录

2.2、拦截器实现

public class TraceClientInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        MDC.put("userName", "zhaozhou11");
        MDC.put("reqId", UUID.randomUUID().toString());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        MDC.remove("userName");
        MDC.remove("reqId");
    }
}

拦截器主要是在接口调用之前设置MDC值,此处设置userName及reqId,并在调用完成后清除;

2.2、Filter实现

public class ConsumerRpcFilter implements Filter {

    private static Logger logger = LoggerFactory.getLogger(ConsumerRpcFilter.class);

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        logger.info("###className:{}", invocation.getClass().getName());
        logger.info("###methodName:{}", invocation.getMethodName());
        String appCode = invoker.getUrl().getParameter("appCode");
        String secretKey = invoker.getUrl().getParameter("secretKey");

        String reqId = MDC.get("reqId");
        reqId = Strings.isNotEmpty(reqId) ? reqId : UUID.randomUUID().toString();

        RpcContext.getContext().setAttachment("appCode",appCode);
        RpcContext.getContext().setAttachment("secretKey", secretKey);
        RpcContext.getContext().setAttachment("reqId", reqId);
        return invoker.invoke(invocation);
    }
}

本处主要是获取appCode、secretKey及reqId,并将这三个参数放到RpcContext的Attachment中,而服务端就可以从其对应的Attachment中获取这些参数。

2.3、Controller实现

@Controller
@RequestMapping(value = "")
public class MainController {

    @Autowired
    private DubboTraceApi traceApi;

    @RequestMapping(value = "/")
    public String gotoIndexPage(Model model){
        String ret = traceApi.echoTest("this is test!");
        model.addAttribute("ret", ret);
        return "index";
    }
}

3、测试输出

确保zookeeper是启动的,并启动服务端和客户端,浏览器访问:http://localhost:8080/

客户端日志:

客户端日志.png

服务端日志:

服务端日志.png

服务端和客户端日志中都有相同的reqId。

示例源码:https://github.com/zhaozhou11/dubbo-demo.git

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容