zoukankan      html  css  js  c++  java
  • Dubbo自定义日志拦截器

    前言

    上一篇文章 Spring aop+自定义注解统一记录用户行为日志 记录了 web层中通过自定义注解配合Spring aop自动记录用户行为日志的过程。那么按照分布式架构中Dubbo服务层的调用过程是否也可以实现统一记录日志?自定义日志拦截器可以实现这个需求。

    需求场景

    在使用Dubbo搭建的分布式项目中,服务层代码调用是这样的:

    1     @GetMapping(value = "/info")
    22    public BaseResult userInfo() {
    33        //rpc远程调用用户服务
    44        BaseResult result = mUserService.userInfo();
    56        return result;
    67    }

    这里的用户服务位于另外一个服务进程,由服务提供者暴露出来,让web层远程调用,需要记录服务结果的调用过程,便于跟踪定位bug.

    自定义日志拦截器

    翻看下Dubbo官方文档,可以看到如下内容:

    简要说明:

    • Dubbo 中所有的拦截器全部继承自org.apache.dubbo.rpc.Filter接口,我们自己也可以自行扩展,只要继承该接口即可.
    • 用户自定义 filter 默认在内置 filter 之后执行

    新增 DubboServiceFilter 拦截器如下:

     1public class DubboServiceFilter implements Filter {
    2
    3    private static final Logger LOGGER = LoggerFactory.getLogger(DubboServiceFilter.class);
    4
    5    @Override
    6    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    7        //外部日志开关默认关闭
    8        String logSwitch = StringUtils.equals(RedisUtil.get(BaseConstants.CACHE_SERVICE_LOG_SWITCH), BaseConstants.YES) ? BaseConstants.YES : BaseConstants.NO;
    9        if (StringUtils.equals(BaseConstants.YES, logSwitch)) {
    10            //打印入参日志
    11            DubboServiceRequest serviceRequest = new DubboServiceRequest();
    12            serviceRequest.setInterfaceName(invocation.getInvoker().getInterface().getName());
    13            serviceRequest.setMethodName(invocation.getMethodName());
    14            serviceRequest.setArgs(invocation.getArguments());
    15            LOGGER.info("dubbo服务接口入参: " + JSON.toJSONString(serviceRequest));
    16        }
    17        //开始时间
    18        long startTime = System.currentTimeMillis();
    19        //执行接口调用逻辑
    20        Result result = invoker.invoke(invocation);
    21        //调用耗时
    22        long elapsed = System.currentTimeMillis() - startTime;
    23        //如果发生异常 则打印异常日志
    24        if (result.hasException() && invoker.getInterface() != GenericService.class) {
    25            LOGGER.error("dubbo执行异常: ", result.getException());
    26        } else {
    27            if (StringUtils.equals(BaseConstants.YES, logSwitch)) {
    28                //打印响应日志
    29                DubboServiceResponse serviceResponse = new DubboServiceResponse();
    30                serviceResponse.setMethodName(invocation.getMethodName());
    31                serviceResponse.setInterfaceName(invocation.getInvoker().getInterface().getName());
    32                serviceResponse.setArgs(invocation.getArguments());
    33                serviceResponse.setResult(new Object[]{result.getValue()});
    34                serviceResponse.setSpendTime(elapsed);
    35                LOGGER.info("dubbo服务响应成功,返回数据: " + JSON.toJSONString(serviceResponse));
    36            }
    37        }
    38        //返回结果响应结果
    39        return result;
    40    }
    41}

    代码中对应的实体bean如下:

    入参实体:

     1/**
    2 * @program: easywits
    3 * @description:Dubbo服务请求入参实体
    4 * @author: zhangshaolin
    5 * @create: 2019-01-08 20:35
    6 **/

    7@Data
    8public class DubboServiceRequest implements Serializable{
    9    private static final long serialVersionUID = 7127824956842786618L;
    10
    11    /**
    12     * 接口名
    13     */

    14    private String interfaceName;
    15
    16    /**
    17     * 方法名
    18     */

    19    private String methodName;
    20
    21    /**
    22     * 参数
    23     */

    24    private Object[] args;
    25}

    响应实体:

     1/**
    2 * @program: easywits
    3 * @description: Dubbo服务响应结果实体
    4 * @author: zhangshaolin
    5 * @create: 2019-01-08 20:36
    6 **/

    7@Data
    8public class DubboServiceResponse implements Serializable{
    9    private static final long serialVersionUID = -2531169660859647737L;
    10
    11    /**
    12     * 接口名
    13     */

    14    private String interfaceName;
    15
    16    /**
    17     * 方法名
    18     */

    19    private String methodName;
    20
    21    /**
    22     * 参数
    23     */

    24    private Object[] args;
    25
    26    /**
    27     * 返回结果
    28     */

    29    private Object result;
    30
    31    /**
    32     * 调用耗时(毫秒)
    33     */

    34    private long spendTime;
    35}

    /src/main/resources/META-INF/dubbo目录下新增纯文本文件org.apache.dubbo.rpc.Filter 内容为:

    1dubboServiceFilter=com.easywits.common.filter.DubboServiceFilter
    • 键值对形式,键随便起个名字
    • 值为DubboServiceFilter拦截器的完整包名.

    最后在服务提供者配置文件中添加配置使拦截器生效:

     1<?xml version="1.0" encoding="UTF-8"?>
    2<beans xmlns="http://www.springframework.org/schema/beans"
    3      ...省略部分代码">
    4
    5    <!--服务提供方应用信息,用于计算依赖关系-->
    6    <dubbo:application name="
    easywits-upms-rpc-service"/>
    7
    8    <!--用dubbo协议在20881端口暴露服务-->
    9    <dubbo:protocol name="
    dubbo" port="20881" payload="52428800"/>
    10
    11    <!--自定义服务层过滤器,值为上述步骤文本文件中的键-->
    12    <dubbo:provider filter="
    dubboServiceFilter"/>
    13
    14    ....省略部分服务配置
    15</beans>
    16

    验证结果

    抓一下我们业务中的部分日志信息看下效果,如下图:

    可以清楚地看到Dubbo服务接口调用的请求参数信息,以及最终的响应结果信息,便于定位线上问题。

    参考文档:http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html

    最后

    记录一个比较简单的具体实用场景,后续会不定期更新更多的实用场景,欢迎关注公众号【张少林同学】!

  • 相关阅读:
    MOSS007 服务器的配置
    服务器日志查询代码
    MOSS2007页面UI定制(无代码上手)
    远程服务器监控程序浅谈
    CoreChart安装使用分享(附 安装文件CoreChart.wsp)
    Tree WebPart的介绍
    思科金牌、银牌代理商名录
    如何让你的移动硬盘或者U盘变得百毒不侵呢?
    CCNA网络工程师面试题
    ICMP报文的各种格式和种类
  • 原文地址:https://www.cnblogs.com/zhangshaolin/p/10242848.html
Copyright © 2011-2022 走看看