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、源码目录

9795603-b6bf62bee398b610.png
服务端源码目录.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、源码目录

9795603-7da1367d5beab85e.png
客户端源码目录.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/

客户端日志:

9795603-2941526b6f61350e.png
客户端日志.png

服务端日志:

9795603-71b2d8440b678a3f.png
服务端日志.png

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值