美团CAT源码分析 - 客户端(上-消息结构)

深度解析Cat源码系列专栏点击访问
持续更新中

CAT源码分析 - 客户端(上-消息结构)

由于cat-client目录下的代码官方已经不再维护更新,所以分析lib目录下的cat客户端源码

1. 官方Demo

CAT客户端的源码分析以官方的使用QuickStart入手
官方Demo
如上图代码所示:

  • 通过Cat客户端开启了一个Transaction,记录整个方法的运行时间。
  • 在过程中“记录”(插入)了成功的Event事件,和两个Metric业务指标。
  • 最后设置Transaction成功 SUCCESS ,标记结束 complete()

2. 客户端初始化

CAT客户端使用时,不需要显式的预初始化,而是在客户端调用时再“懒”初始化。
Cat 类是一个单例,封装了客户端对Message消息的一些列API操作。
以newTransation方法为例分析代码:

  • 首先获取MessageProcuder,通过MessageProducer来创建Transaction。
  • MessageProducer是一个消息提供类,包括了消息的创建、上下文管理、初始化等逻辑。

注:这段代码可以看到NullMessage.TRANSACTION,包括getProducer()的实现,都使用了“空对象”模式,来减少空判断。这种模式在cat源码中大量使用。具体介绍-空对象模式

public static Transaction newTransaction(String type, String name) {
   
     if (isEnabled()) {
    // 判断客户端是否生效,初始化失败会为false
         try {
   
             // 获取Producer工程,
             return Cat.getProducer().newTransaction(type, name);
         } catch (Exception e) {
   
             errorHandler(e);
             return NullMessage.TRANSACTION;
         }
     } else {
   
         return NullMessage.TRANSACTION;
     }
}

public static MessageProducer getProducer() {
   
    try {
   
        // 检查是否执行初始化
        checkAndInitialize();
        if (producer != null) {
   
            // 返回producer
            return producer;
        } else {
   
            return NullMessageProducer.NULL_MESSAGE_PRODUCER;
        }
    } catch (Exception e) {
   
        errorHandler(e);
        return NullMessageProducer.NULL_MESSAGE_PRODUCER;
    }
}
  • 初始化方法校验是否已经初始化,之后通过Java SPI读取用户是否设置了自定义的ClientConfigProvider实现。这为用户提供了自定义配置和获取配置的方式。

默认情况下,使用CAT默认的配置进行初始化。

private static void checkAndInitialize() {
   
    try {
   
        // 由于是懒加载,避免重复初始化
        if (!init) {
   
            // 通过SPI读取自定义的配置
        	ClientConfig clientConfig = getSpiClientConfig();
			if (clientConfig == null) {
   
                // 执行初始化
				initializeInternal();
			} else {
   
				initializeInternal(clientConfig);
			}
        }
    } catch (Exception e) {
   
        errorHandler(e);
    }
}
    
private static ClientConfig getSpiClientConfig() {
   
    // 通过SPI接口,获取/META-INF/service下是否有自定义配置
	ServiceLoader<ClientConfigProvider> clientConfigProviders = ServiceLoader.load(ClientConfigProvider.class);
	if (clientConfigProviders == null) {
   
		return null;
	}
	Iterator<ClientConfigProvider> iterator = clientConfigProviders.iterator();
	if (iterator.hasNext()){
   
		//只支持一个ClientConfigProvider的实现,默认取查询结果第一个
		ClientConfigProvider clientConfigProvider = (ClientConfigProvider)iterator.next();
		return clientConfigProvider.getClientConfig();
	} else {
   
		return null;
	}
}

真正的初始化逻辑com.dianping.cat.Cat#initializeInternal():

  • 校验是否配置了app.name(/META-INFO/app.properties),作为Domain上报Server使用
  • 单例双重检查是否初始化
  • 引用MessageProducer实例和MessageManager实例(下文详解)
  • 开启客户端通信TcpSocketSender
  • 启动HeartBeat消息的检查线程
private static void initializeInternal() {
   
    // 检查是否配置appname
    validate();
    if (isEnabled()) {
   
        try {
   
            // 单例的双重检查
            if (!init) {
   
                synchronized (instance) {
   
                    if (!init) {
   
                        // 一系列的初始化操作
                        producer = DefaultMessageProducer.getInstance();
                        manager = DefaultMessageManager.getInstance();
                        StatusUpdateTask heartbeatTask = new StatusUpdateTask();
                        TcpSocketSender messageSender = TcpSocketSender.getInstance();
                        Threads.forGroup("cat").start(heartbeatTask);
                        Threads.forGroup("cat").start(messageSender);
                        Threads.forGroup("cat").start(new LocalAggregator.DataUploader());
                        CatLogger.getInstance().info("Cat is lazy initialized!");
                        init = true;
                    }
                }
            }
        } catch (Exception e) {
   
            errorHandler(e);
            disable();
        }
    }
}
    
private static void validate() {
   
    // 系统变量检查是否开启CAT客户端,默认开启
    String enable = Properties.forString().fromEnv().fromSystem().getProperty("CAT_ENABLED", "true");
    if ("false".equals(enable)) {
   
        CatLogger.getInstance().info("CAT is disable due to system environment CAT_ENABLED is false.");
        enabled = false;
    } else {
   
        // 检查是否从系统变量读取自定义的Domain
        String customDomain = getCustomDomain();
        // 没有特殊定义,默认从/META-INF/app.properties中读取app.name
        if (customDomain == null && UNKNOWN.equals(ApplicationEnvironment.loadAppName(UNKNOWN))) 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值