dubbo基于调用拦截扩展实现分布式调用追踪

      在基于dubbo的分布式应用集群中,调试会变得比较麻烦,不知道一个请求会被发送到哪一台机器上。我们可以通过dubbo的SPI扩展中的调用拦截扩展,来解决这个问题。

      dubbo的调用拦截扩展可以对服务提供方和消费方的调用进行拦截,然后加入自己的处理逻辑。通过简单的三个步骤即可实现一个自定义的调用拦截扩展。

1、扩展配置

    在dubbo的xml配置文件中,加入filter配置,如下所示:

<!-- 消费方调用过程拦截 -->
<dubbo:reference filter="xxx,yyy" />
<!-- 消费方调用过程缺省拦截器,将拦截所有reference -->
<dubbo:consumer filter="xxx,yyy"/>
<!-- 提供方调用过程拦截 -->
<dubbo:service filter="xxx,yyy" />
<!-- 提供方调用过程缺省拦截器,将拦截所有service -->
<dubbo:provider filter="xxx,yyy"/>

2、配置实现

    在META-INF中创建dubbo文件夹,然后在dubbo文件夹下创建com.alibaba.dubbo.rpc.Filter文本文件,如下所示:

    

  然后编辑该文本文件,指定filter的实现类,例如:

xxx=com.gameloft9.XxxFilter

3、编写实现类

     通过实现dubbo的Filter接口,实现具体的拦截逻辑,例如:

package com.gameloft9;
 
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
 
public class XxxFilter implements Filter {
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // before filter ...
        Result result = invoker.invoke(invocation);
        // after filter ...
        return result;
    }
}

 


       Slf4j接口里面有MDC,熟悉logback的同学应该不会陌生,我们可以通过MDC,将有用的信息打印到日志里面去。接下来我们就通过MDC和调用拦截扩展来实现分布式的调用追踪。

    1、扩展配置

<!--配置filter,在filter里面处理traceId-->
<dubbo:provider filter="traceid"/>
<dubbo:consumer filter="traceid"/>

2、配置实现类

traceid=com.gameloft9.demo.filter.TraceDubboFilter

3、编写实现逻辑

     逻辑很简单,当consumer调用时,将生成的traceId放入rpc context,provider接收到请求时,从traceId获取到traceId,并放入MDC。

package com.gameloft9.demo.filter;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.gameloft9.demo.util.TraceUtil;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@Activate(order = 1000, group = {Constants.PROVIDER, Constants.CONSUMER})
public class TraceDubboFilter implements Filter {
	public TraceDubboFilter(){
        super();
	}

	public Result invoke(Invoker<?> invoker, Invocation invocation)
			throws RpcException {
		RpcContext context = RpcContext.getContext();
		if (context.isConsumerSide()) {
			TraceUtil.putTraceInto(context);
		} else if (context.isProviderSide()) {
			TraceUtil.getTraceFrom(context);
		}
		return invoker.invoke(invocation);
	}

}

  对traceId的处理,封装到了一个工具类中:

package com.gameloft9.demo.util;

import com.alibaba.dubbo.rpc.RpcContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

import java.util.UUID;

/**
 * traceId工具类
 * Created by gameloft9 on 2018/9/7.
 */
public class TraceUtil {

    public final static String TRACE_ID = "trace_id";

    /**
     * 初始化traceId,由consumer调用
     */
    public static void initTrace() {
        String traceId = generateTraceId();
        setTraceId(traceId);
    }

    /**
     * 从Dubbo中获取traceId,provider调用
     * @param context
     */
    public static void getTraceFrom(RpcContext context) {
        String traceId = (String) context.getAttachment(TRACE_ID);
        if (traceId == null) {
            traceId = generateTraceId();
        }

        setTraceId(traceId);
    }

    /**
     * 把traceId放入dubbo远程调用中,consumer调用
     * @param context
     */
    public static void putTraceInto(RpcContext context) {
        String traceId = getTraceId();
        if (traceId != null) {
            context.setAttachment(TRACE_ID, traceId);
        }
    }

    /**
     * 从MDC中清除traceId
     */
    public static void clearTrace() {
        MDC.remove(TRACE_ID);
    }

    /****************************私有方法区*********************************/
    
    /**
     * 从MDC中获取traceId
     * */
    private static String getTraceId() {
        return MDC.get(TRACE_ID);
    }

    /**
     * 将traceId放入MDC
     * @param traceId
     */
    private static void setTraceId(String traceId) {
        if (StringUtils.isNotBlank(traceId)) {
            traceId = StringUtils.left(traceId, 36);
        }
        MDC.put(TRACE_ID, traceId);
    }

    /**
     * 生成traceId
     * @return
     */
    static private String generateTraceId() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

}

 如何使用:

1、consumer端

ICalculator demo = (ICalculator)context.getBean("calculator");
TraceUtil.initTrace();
int res = demo.add(1,3);
log.info("测试结果:{}",res);

2、provider端

    provider端不需要做任何操作,读取traceId和设置MDC都在filter里面做了。

3、logback配置

     logback中,通过%X{trace_id}读取MDC 中的traceId,并打印出来。

 <!--控制台打印-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%.15thread] [%X{trace_id}] %logger{36} - %.-4096msg%n
            </pattern>
        </encoder>
    </appender>

   测试:

       我们一共有四个工程,dubbo-test-api,dubbo-test-consumer,dubbo-test-provider,dubbo-test-provider-another。api工程提供了一个简单的加法接口:

/**
 * 接口
 * */
public interface ICalculator {

	int add(int a,int b);

}

dubbo-test-provider和dubbo-test-provider-another均实现了这个接口,并注册到了zookeeper(zookeeper需要自己搭建)。

package com.gameloft9.demo.serviceImp;

import com.gameloft9.demo.api.ICalculator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class Calculator implements ICalculator {

	public int add(int a,int b){
		log.info("收到加法请求:{}+{}",a,b);
		return a+b;
	}
	
}

dubbo-test-consumer调用加法:

ICalculator demo = (ICalculator)context.getBean("calculator");
TraceUtil.initTrace();

log.info("开始测试!");
int res = demo.add(1,3);
log.info("测试结果:{}",res);

运行截图:

1、consumer端日志

2、service端日志

这样我们就成功的在日志中通过traceId来跟踪请求了。

工程地址:https://download.csdn.net/download/gameloft9/10658932

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值