分布式服务链路追踪的解决方案

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQ994406030/article/details/85449998

随着业务越来越复杂,系统也随之进行各种拆分,特别是随着微服务架构和容器技术的兴起,看似简单的一个应用,后台可能有几十个甚至几百个服务在支撑;一个前端的请求可能需要多次的服务调用最后才能完成;当请求变慢或者不可用时,我们无法得知是哪个后台服务引起的,这时就需要解决如何快速定位服务故障点,今天介绍的分布式服务链路追踪就能很好的解决这样的问题。

它可以做什么呢?

  • 用户行为链路:如浏览页面,观看视频,购买商品,收藏,评论等等行为。
  • 服务链路追踪:快速定位异常
  • 大数据AI画像:一个链路非业务的动态行为数据,才是最贴近用户的素材

服务链路追踪的基本出发点——记录足迹

  

  •     (1)入口处生成链路标识traceId
  •     (2)传递traceId参数给下层业务方法
  •     (3)各方法内部记录访问的信息

但是这种方式,违背了我们面向对象原则:高内聚低耦合。严重的和业务代码耦合在一起。链路追踪不能成为我们的主流业务方法,增值性的服务不能影响我们的主流业务(异常、耗时)

单系统链路追踪-优化方案

我们可以采用ThreadLocal变量来存储traceId(关于threadLocal使用自行百度),traceId就可以在当前线程的范围内访问。

/**
 * @desc
 * @author lizehao
 * @company 连连支付
 * @date 2018年12月30日上午10:40:49
 */
public class TraceDataHolder {

	private static ThreadLocal<String> instanceHolder = new ThreadLocal<String>();

	public static void setTraceId() {
		String traceId = instanceHolder.get();
		if (StringUtils.isEmpty(traceId)) {
			traceId = UUID.randomUUID().toString();
			instanceHolder.set(traceId);
		}
	}

	public static String getTraceId() {
		return instanceHolder.get();
	}
}

在程序的访问入口处,调用TrackDataHolder.setTraceId()即可,获取traceId调用TrackDataHolder.getTraceId().

还可以进一步进行优化,入口处我们可以采用基于AOP的实现,也可以采用基于注解的方式。

这样方式在单系统中是可以的,但是在分布式系统,比如说dubbo之间的服务调用,它可不认这一套,所以我们还需要进一步进行优化。

如何让traceId的传递不侵入业务?

MDC是log日志系统支持的一个功能,底层支持threadLocal变量。并且可以在打印日志的时候,获取该变量。

在方法的入口处,通过MDC.put("traceId","****")进行设置,日志格式采用 %X{traceId},代码里通过MDC.get("traceId")获取。

通过MDC设置值也可以通过AOP、注解实现。

但是在dubbo服务之间,traceId如何传递的呢?我们就需要用到dubbo的Filter。

link-trace项目

/**
 * @desc
 * @author lizehao
 * @company 连连支付
 * @date 2018年8月23日下午6:32:26
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackAnnotation {

}
/**
 * @desc 
 * @author lizehao
 * @company 
 * @date 2018年12月30日下午8:58:00
 */
@Aspect
@Component
public class TrackAspect {

	public final static String TRACK_ID = "trackId";

	@Around("@annotation( com.lizh.link.track.demo.annotation.TrackAnnotation )")
	public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
		MDC.put(TRACK_ID, UUID.randomUUID().toString());
		return pjp.proceed();
	}
}

 通过dubbo的SPI机制,扩展Filter

/**
 * @desc
 * @author lizehao
 * @company
 * @date 2018年12月30日下午9:43:59
 */
@Slf4j
@Activate(group = "provider")
public class MDCFilter implements Filter {

	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		String trackId = RpcContext.getContext().getAttachment(TrackAspect.TRACK_ID);
		MDC.put(TrackAspect.TRACK_ID, trackId);
		log.info("---------MDC:[{}]--------", trackId);
		return invoker.invoke(invocation);
	}
}
/**
 * @desc
 * @author lizehao
 * @company
 * @date 2018年12月30日下午9:44:04
 */
@Slf4j
@Activate(group = "consumer")
public class RPCFilter implements Filter {

	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		// RpcContext.getContext().setAttachments(MDC.getContext());
		log.info("---------RPC:[{}]--------", MDC.get(TrackAspect.TRACK_ID));
		RpcContext.getContext().setAttachment(TrackAspect.TRACK_ID, (String) MDC.get(TrackAspect.TRACK_ID));
		return invoker.invoke(invocation);
	}
}

/src/main/resources/META-INF/dubbo下新建一个文件,文件名为接口的全类名(com.alibaba.dubbo.rpc.Filter)

mdc=com.lizh.link.trace.demo.log.MDCFilter
rpc=com.lizh.link.trace.demo.log.RPCFilter

服务的消费方:

#################Dubbo 服务提供者配置################# 
spring.dubbo.application.name=order-consumer
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.protocol.name=dubbo
spring.dubbo.consumer.filter=rpc
spring.dubbo.scan=com.lizh

核心配置:spring.dubbo.consumer.filter=rpc

/**
 * @desc 
 * @author lizehao
 * @company 
 * @date 2018年12月30日下午9:11:34
 */
@Slf4j
@Controller
public class OrderController {

	@Reference
	private OrderService orderService;

	@ResponseBody
	@TrackAnnotation
	@RequestMapping("/order")
	public Order findOne(long id) {
		log.info("前置开始查询订单信息[{}]", id);
		Order order = orderService.findOrderById(id);
		log.info("订单信息[{}]", JSON.toJSONString(order));
		return order;
	}
}
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger- [%X{trackId}]- %msg%n 
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger-[%X{trackId}]- %msg%n

核心配置:@TrackAnnotation

服务的提供方

#################Dubbo \u670D\u52A1\u63D0\u4F9B\u8005\u914D\u7F6E################# 
spring.dubbo.application.name=order-provider
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880
spring.dubbo.scan=com.lizh
spring.dubbo.provider.filter=mdc
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger- [%X{trackId}]- %msg%n 
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger-[%X{trackId}]- %msg%n

运行结果:

消费方:

2018-12-31 10:10:44 INFO  com.lizh.order.demo.controller.OrderController- [9b1851dc-f9e6-444a-bce5-613974220450]- 前置开始查询订单信息[111]
2018-12-31 10:10:44 INFO  com.lizh.link.track.demo.log.RPCFilter- [9b1851dc-f9e6-444a-bce5-613974220450]- ---------RPC:[9b1851dc-f9e6-444a-bce5-613974220450]--------
2018-12-31 10:10:44 INFO  com.lizh.order.demo.controller.OrderController- [9b1851dc-f9e6-444a-bce5-613974220450]- 订单信息[{"id":111,"name":"订单_293","price":"23"}]

提供方:

2018-12-31 10:10:44 INFO  com.lizh.link.track.demo.log.MDCFilter- [9b1851dc-f9e6-444a-bce5-613974220450]- ---------MDC:[9b1851dc-f9e6-444a-bce5-613974220450]--------
2018-12-31 10:10:44 INFO  com.lizh.order.demo.service.OrderServiceImpl- [9b1851dc-f9e6-444a-bce5-613974220450]- 查询订单[111]对象......

猜你喜欢

转载自blog.csdn.net/QQ994406030/article/details/85449998