CAT集成Dubbo

背景

在上面介绍了CAT如何做跨进程追踪的。公司项目采用业务垂直切分的方式开发、前后端也是分离方式。那么实时监控埋点的地方有如下几处:

  • 前端和后端交互
  • 后端业务逻辑
  • 数据库存储
  • 远程PRC调用
  • 消息队列

 

RPC框架

公司选用的RPC框架是apache dubbo ,版本是2.7.5 。dubbo是一款非常优秀的RPC框架。对于框架我就不做过多的介绍了。cat 集成 dubbo 首要考虑的就是让业务系统无感。那么优秀的框架一定有扩展点。在dubbo中可以使用Filter 来进行扩展。

 

CAT集成dubbo

dubbo集成的时候需要区分是生产者还是消费者,生产者和消费者监控的数据大致上都是一样的,有少量数据不一样。并且需要处理异常的情况。由于Filter需要对调用前和调用后进行监控。并且流程上固定的。所以这里我采用了模板的方式。代码的类图如下:

 

使用AbstractCatTransaction 中进行流程的定义,并且将公共代码都放到抽象类中。定义抽象想法让子类去实现。代码详情如下:

 

AbstractCatTransaction 代码:

/**
 * CAT-DUBBO 监控抽象类。采用模板设计把固定的流程固化。
 * @author tengx
 */
public abstract class AbstractCatTransaction implements Filter {

    /**
     * 拦截rpc 请求,设计3个扩展口子。
     * 1.创建 CAT 事务
     * 2.在rpc调用前执行扩展方法
     * 3.在rpc调用后执行扩展方法
     * 4.记录rpc花费的时间
     * 5.对于异常的处理
     *
     * @param invoker 调用者
     * @param invocation 调用
     * @return 返回结果
     * @throws RpcException RPC异常
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        Result invokeResult=null;
        Transaction t=createTransaction(invoker,invocation);

        try{

            beforeInvoke(invoker,invocation,t);
            long startTime=System.currentTimeMillis();
            //rpc 返回的结果
            invokeResult=invoker.invoke(invocation);

            long endTime=System.currentTimeMillis();

            afterInvoke(invoker,invocation,t);
            //记录rpc花费的时间
            long spendTime=endTime-startTime;
            t.setDurationInMillis(spendTime);
            //记录rpc花费的时间
            Cat.logEvent(CatConstants.DUBBO_SPEND_TIME,spendTime+"ms");

            boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
            //异步的不能判断是否有异常,这样会阻塞住接口(<AsyncRpcResult>hasException->getRpcResult->resultFuture.get()
            if (isAsync) {
                t.setStatus(Transaction.SUCCESS);
                return invokeResult;
            }
            //没有异常直接返回结果
            if (!invokeResult.hasException()){
                t.setStatus(Transaction.SUCCESS);
                return invokeResult;
            }
            handlerRpcException(invokeResult,t);
        }catch (Exception e){
            t.setStatus(e);
            Cat.logError(e);
        }finally {
            t.complete();
        }

        return invokeResult;
    }

    /**
     * 创建CAT监控事务模型
     * @param invoker 调用人
     * @param invocation 调用
     * @return 事务模型
     */
    protected abstract Transaction createTransaction(Invoker<?> invoker, Invocation invocation);

    /**
     * 在rpc方法执行前进行监控操作
     * @param invoker 调用人
     * @param invocation 调用
     * @param t  CAT事务模型
     */
    protected abstract void beforeInvoke(Invoker<?> invoker, Invocation invocation,Transaction t);

    /**
     * 在rpc方法执行后进行监控操作
     * @param invoker 调用人
     * @param invocation 调用
     * @param t CAT事务模型
     */
    protected abstract void afterInvoke(Invoker<?> invoker, Invocation invocation,Transaction t);
    /**
     * 对于RPC异常情况的处理
     * @param result rpc返回的结果
     * @param t cat的事务
     */
    protected void handlerRpcException(Result result, Transaction t){
        if (!result.hasException()){
            return;
        }
        Throwable throwable=result.getException();
        if(RpcException.class==throwable.getClass()){
            Throwable caseBy = throwable.getCause();
            if(caseBy != null&&caseBy.getClass()== TimeoutException.class){
                Cat.logError(CatConstants.DUBBO_TIMEOUT_ERROR,throwable);
            }else{
                Cat.logError(CatConstants.DUBBO_REMOTING_ERROR,throwable);
            }
        }else if(RemotingException.class.isAssignableFrom(throwable.getClass())){
            Cat.logError(CatConstants.DUBBO_REMOTING_ERROR,throwable);
        }else{
            Cat.logError(CatConstants.SERVER_BIZ_ERROR,throwable);
        }
        t.setStatus(throwable);
    }
}

 

CatConsumerTransaction 代码

/**
 * CAT-DUBBO 消费端监控
 * @author tengx
 */
@Activate(group = {CommonConstants.CONSUMER})
@Slf4j
public class CatConsumerTransaction extends AbstractCatTransaction  {

    /**
     *  通过url获取应用名称,然后创建事务模型
     * @param invoker 调用人
     * @param invocation 调用
     * @return 事务模型
     */
    @Override
    protected Transaction createTransaction(Invoker<?> invoker, Invocation invocation) {
        URL url=invoker.getUrl();
        String applicationName=url.getParameter(CommonConstants.APPLICATION_KEY);
        //创建Transaction
        return  Cat.newTransaction(CatConstants.DUBBO_CONSUMER_NAME,applicationName);

    }

    /**
     *  拦截rpc消费请求,使用CAT 记录以下信息:
     *  1.消费端的名称
     *  2.记录消费端的IP
     *  3.记录消费端调用的方法和参数
     *  4.记录消费端调用的持续时间
     * @param invoker 调用者
     * @param invocation 调用
     * @param t 事务模型
     */
    @Override
    protected void beforeInvoke(Invoker<?> invoker, Invocation invocation,Transaction t) {
        URL url=invoker.getUrl();
        String applicationName=url.getParameter(CommonConstants.APPLICATION_KEY);
        Context ctx = new CatContext();
        Cat.logRemoteCallClient(ctx,applicationName);
        //消费端传递RPC参数
        consumerSetAttachment(url, applicationName, ctx);
        Cat.logEvent(CatConstants.DUBBO_CONSUMER_IP,url.getIp());
        Cat.logEvent(CatConstants.DUBBO_CONSUMER_METHOD,invocation.getMethodName());
        Cat.logEvent(CatConstants.DUBBO_CONSUMER_METHOD_Arguments, JsonUtils.toJSONString(invocation.getArguments()));
        Cat.logEvent(CatConstants.DUBBO_CONSUMER_DATETIME, DateUtils.format(new Date(),DateUtils.DATE_HH_MM_SS));
    }

    /**
     * 空方法,rpc调用后没有逻辑
     * @param invoker 调用人
     * @param invocation 调用
     * @param t CAT事务模型
     */
    @Override
    protected void afterInvoke(Invoker<?> invoker, Invocation invocation,Transaction t) {
    }

    /**
     * 向RPC Context中设置CAT监控跨进程需要的参数
     * @param url dubbo的url
     * @param applicationName 应用名称
     * @param ctx CAT上下文
     */
    private  void consumerSetAttachment(URL url, String applicationName, Context ctx) {
        //向rpc context 中放入追踪的参数
        RpcContext.getContext().setAttachment(Context.ROOT, ctx.getProperty(Context.ROOT));
        RpcContext.getContext().setAttachment(Context.CHILD, ctx.getProperty(Context.CHILD));
        RpcContext.getContext().setAttachment(Context.PARENT, ctx.getProperty(Context.PARENT));
        RpcContext.getContext().setAttachment(CatConstants.DUBBO_CONSUMER_NAME, applicationName);
        RpcContext.getContext().setAttachment(CatConstants.DUBBO_CONSUMER_IP, url.getIp());
    }

}

 

这里需要注意的是,消费者会把CAT做追踪的参数、应用名称、消费端IP放到 RpcContext中。这样生产者就能获取到这样参数。在监控中做对应。在生产环境中,系统会有多个节点。所以这里把消费者的IP传入的RpcContext 然后生产者那边接受这个参数并记录。目的是对应上服务器。

 

CatProviderTransaction的代码

/**
 * CAT-DUBBO 生产者记录
 * @author tengx
 */
@Activate(group = {CommonConstants.PROVIDER})
@Slf4j
public class CatProviderTransaction extends AbstractCatTransaction {


    @Override
    protected Transaction createTransaction(Invoker<?> invoker, Invocation invocation) {
        URL url=invoker.getUrl();
        String applicationName=url.getParameter(CommonConstants.APPLICATION_KEY);
        //创建Transaction
        return Cat.newTransaction(CatConstants.DUBBO_PROVIDER_NAME,applicationName);
    }

    @Override
    protected void beforeInvoke(Invoker<?> invoker, Invocation invocation, Transaction t) {
        URL url=invoker.getUrl();
        Context ctx = new CatContext();
        ctx.addProperty(Context.ROOT,RpcContext.getContext().getAttachment(Context.ROOT));
        ctx.addProperty(Context.CHILD,RpcContext.getContext().getAttachment(Context.CHILD));
        ctx.addProperty(Context.PARENT,RpcContext.getContext().getAttachment(Context.PARENT));

        Cat.logRemoteCallServer(ctx);

        String str=RpcContext.getContext().getAttachment(CatConstants.DUBBO_CONSUMER_NAME)+
                ":"+RpcContext.getContext().getAttachment(CatConstants.DUBBO_CONSUMER_IP);

        Cat.logEvent(CatConstants.DUBBO_CONSUMER_NAME,str);
        Cat.logEvent(CatConstants.DUBBO_PROVIDER_IP,url.getIp());
        Cat.logEvent(CatConstants.DUBBO_PROVIDER_METHOD,invocation.getMethodName());
        Cat.logEvent(CatConstants.DUBBO_PROVIDER_METHOD_Arguments, JsonUtils.toJSONString(invocation.getArguments()));
        Cat.logEvent(CatConstants.DUBBO_PROVIDER_DATETIME, DateUtils.format(new Date(),DateUtils.DATE_HH_MM_SS));
    }

    @Override
    protected void afterInvoke(Invoker<?> invoker, Invocation invocation, Transaction t) {
    }
}

 

最后在使用dubbo Filter的时候需要在resources 下MEAT-INF/dubbo/创建Filter文件。Filter文件名称是:org.apache.dubbo.rpc.Filter

 

总结:

  1. 优秀的框架都会留有扩展的接口。所以在做集成的时候先看看框架扩展接口,然后再思考是否自己写扩展方式。
  2. 对于公用的代码可以使用模板的方式进行抽取。
  3. dubbo框架可以使用filter接口对调用进行扩展。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值