拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CAT 服务端 C 客户端(ccat)API 实战指南:初始化、Transaction、Event、Metric 与 Heartbeat 全解析

CAT 服务端 C 客户端ccatAPI 实战指南初始化、Transaction、Event、Metric 与 Heartbeat 全解析【免费下载链接】catCAT 作为服务端项目基础组件提供了 Java, C/C, Node.js, Python, Go 等多语言客户端已经在美团点评的基础架构中间件框架MVC框架RPC框架数据库框架缓存框架等消息队列配置系统等深度集成为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。项目地址: https://gitcode.com/gh_mirrors/ca/catCATCentral Application Tracking是美团点评开源的分布式监控系统ccat 是它的 C 语言客户端实现基于 C99 编写同时支持 Linuxglibc 与 musl-libc以及 OSX 平台。对于使用 C/C 构建的高性能服务、网关、中间件或嵌入式采集程序ccat 提供了轻量、异步、线程安全的埋点能力可将业务调用链、耗时、计数与系统心跳数据实时上报到 CAT 服务端。本文以 lib/c/docs/api.zh-CN.md 为骨架完整覆盖 ccat 的快速起步、通用 API、Transaction、Event、Metric、Heartbeat 六大模块并结合仓库源码如 client.c、transaction.c、message_manager.c 等深入讲解每个 API 的底层行为、默认参数、聚合规则与使用注意事项。读完本文你将能够在 C 程序中正确初始化 ccat、构建嵌套的消息树、记录业务耗时与错误、上报聚合指标并理解消息树何时被序列化发送、心跳与指标聚合是如何在后台线程中完成的。快速起步第一个完整的 ccat 埋点程序下面的示例来自官方 API 文档它在一个函数中展示了 Transaction 的创建与嵌套、Metric 的记录、带耗时 Transaction 的快捷创建以及 Event 的记录是理解 ccat 消息模型的最佳起点#include client.h void test() { /** * 如果当前的栈为空一个消息树会被创建 */ CatTransaction *t1 newTransaction(foo, bar1); /** * Metric 可以在任何地方记录并不会被加到消息树中 */ logMetricForCount(metric-count, 1); logMetricForDuration(metric-duration, 200); /** * 记录一个给定耗时的 transaction 并立刻完成它。 */ newCompletedTransactionWithDuration(foo, bar2-completed, 1000); /** * Transaction 可以嵌套最新的 transaction 会被推到栈顶 */ CatTransaction *t3 newTransaction(foo, bar3); t3-setStatus(t3, CAT_SUCCESS); /** * 当你完成一个 transaction 的时候它会被从栈里面弹出并且 duration 会被计算。 */ t3-complete(t3); char buf[10]; for (int k 0; k 3; k) { /** * 创建一个给定耗时的 transaction */ CatTransaction *t4 newTransactionWithDuration(foo, bar4-with-duration, 1000); snprintf(buf, 9, bar%d, k); /** * 记录一个 event会被添加到当前 transaction 的儿子列表中 */ logEvent(foo, buf, CAT_SUCCESS, NULL); t4-setStatus(t4, CAT_SUCCESS); t4-complete(t4); } t1-setStatus(t1, CAT_SUCCESS); /** * 完成 transaction 并将它从栈里弹出 * 当最后一个元素被弹出时消息树会被序列化并发送给服务端 */ t1-complete(t1); } int main(int argc, char **argv) { CatClientConfig config DEFAULT_CCAT_CONFIG; config.enableHeartbeat 0; config.enableDebugLog 1; catClientInitWithConfig(ccat, config); test(); Sleep(3000); catClientDestroy(); return 0; }从这个示例可以提炼出 ccat 的核心消息模型Transaction 是消息树的骨架第一个newTransaction在栈为空时创建消息树后续的嵌套 Transaction 被压入栈成为父节点的子节点Event 是 Transaction 的轻量补充logEvent记录的事件会被挂到当前栈顶 Transaction 的儿子列表中Metric 独立于消息树指标记录走独立的聚合通道不参与树形结构即使当前没有 Transaction 也可以调用树的弹出时机当栈中最后一个 Transaction 被complete弹出后整棵消息树会被序列化交由 sender 线程异步发送给 CAT 服务端。同样的测试代码也可以在仓库的 scripts/cat_client_test.c 中看到其中testTransaction等测试函数完整演示了上述 API 的组合用法。通用 API初始化、销毁与状态查询catClientInit使用默认配置初始化int catClientInit(const char *appkey);catClientInit使用默认配置初始化 ccat在 client.c 中它的实现等价于int catClientInit(const char* appkey) { return catClientInitWithConfig(appkey, DEFAULT_CCAT_CONFIG); }默认配置DEFAULT_CCAT_CONFIG定义于同一文件其取值如下也可参见 client.h 中CatClientConfig结构体配置项默认值说明encoderTypeCAT_ENCODER_BINARY消息序列化使用二进制编码值为 1文本编码为CAT_ENCODER_TEXT值为 0enableHeartbeat1true开启内置心跳上报enableSampling1true开启采样队列满时对可丢弃消息做聚合/丢弃处理enableMultiprocessing0false关闭多进程模式enableDebugLog0false关闭调试日志值得注意的是DEFAULT_CCAT_CONFIG中还有一个未出现在 API 文档表格里的enableAutoInitialize字段默认 0当进程通过fork产生子进程时若该选项开启ccat 会在子进程中自动重建发送、监控、聚合线程见 client.c 中catClientInitInnerForked的逻辑并自动禁用子进程心跳。catClientInitWithConfig自定义配置初始化有时你需要按业务情况自定义 ccat 的行为CatClientConfig config DEFAULT_CCAT_CONFIG; config.enableHeartbeat 0; config.enableDebugLog 1; catClientInitWithConfig(ccat, config);catClientInitWithConfig的初始化流程见 client.c依次为设置g_cat_init标志并忽略SIGPIPE信号通过initCatClientConfig将传入的配置拷贝到全局g_config并读取CAT_HOME环境变量确定数据目录未设置时回退到默认路径加载CAT_HOME下的client.xml配置文件解析servers/server节点得到 CAT 服务端地址见 client_config.c 的parseCatClientConfig第一个 server 的ip与http-port会被使用初始化消息管理器域名、主机名、IP 信息、服务端连接管理器启动sender、monitor、aggregator三个后台线程注册pthread_atfork回调处理 fork 场景。client.xml的典型形态可参考仓库根目录 docker/client.xml其中通过serversserver ip127.0.0.1 port2280 http-port8080//servers指定 CAT 服务端地址。注意ccat 会优先使用代码里传入的appkey覆盖配置文件中的 domain且 appkey 只能包含英文字母a-z、A-Z、数字0-9、下划线_和中划线-。catClientDestroy关闭并释放资源int catClientDestroy();调用后 ccat 被禁用sender、monitor、aggregator三个后台线程退出通过pthread_join等待线程结束并释放消息管理器、聚合器、sender 队列、服务端连接管理器和消息 ID 帮助器申请的所有资源见 client.c 的catClientDestroy实现。程序退出前务必调用它以避免资源泄漏和消息丢失。isCatEnabled检查初始化状态int isCatEnabled();返回 ccat 是否已成功初始化。其实现是读取全局原子标志g_cat_enabled见 client_config.c该标志只有在服务端路由配置获取成功、各线程启动完成后才会置为 1。ccat 内部几乎所有 API如logEvent、logMetricForCount、newTransaction在isCatEnabled()为假时都会直接返回空实现或空对象见 client.c 中的g_cat_nullMsg/g_cat_nullTrans设计因此即使在未初始化或初始化失败的情况下调用埋点 API 也是安全的。Transaction构建调用链的核心Transaction 是 CAT 消息模型中最重要的类型代表一段带耗时的业务操作如一次 RPC 调用、一个数据库查询。关于 Transaction 的完整属性说明可参考仓库根目录下的消息属性文档文档原文指向_/zh-CN.md其内容位于仓库根目录的 README 相关章节。newTransaction创建 TransactionCatTransaction *newTransaction(const char *type, const char *name);由于安全原因ccat 隐藏了 Transaction 的内部属性_CatTransactionInner结构体对调用者不可见但提供了一组函数指针形式的 API 供你修改它们API作用addData向 Transaction 追加一段调试数据会在 CAT 的 LogView 页面展示addKV以 key-value 形式追加调试数据setStatus设置状态任何不等于0CAT_SUCCESS的状态都会被当作 problem 处理setTimestamp设置 Transaction 的创建时间戳complete完成 Transaction将其从栈中弹出并计算耗时addChild直接向 Transaction 添加子消息一般不建议使用setDurationInMillis手工指定耗时毫秒完成后不再重新计算setDurationStart设置耗时起点完成后按当前时间 - durationStart计算耗时典型用法CatTransaction *t newTransaction(Test1, A); t-setStatus(t, CAT_SUCCESS); t-setTimestamp(t, GetTime64() - 5000); t-setDurationStart(t, GetTime64() - 5000); t-setDurationInMillis(t, 4200); t-addData(t, data); t-addKV(t, k1, v1); t-addKV(t, k2, v2); t-complete(t);使用时有三个关键注意事项文档原文明确强调addData和addKV可以被多次调用多次调用的数据会被连接起来同时指定durationsetDurationInMillis和durationStart是没有意义的二者是互斥的耗时来源不要忘记调用complete否则会得到一棵毁坏的消息树并造成内存泄漏。从源码看setTransactionComplete的行为是若未手工指定耗时durationUs 0则按GetTime64() * 1000 - durationStart / 1000计算真实耗时随后将消息标记为完成并调用catMessageManagerEndTrans弹出栈见 transaction.c。而catMessageManagerEndTrans会检查事务栈是否已空——当最后一个 Transaction 被弹出时整棵消息树被复制、flush 到 sender 队列然后上下文被 reset见 message_manager.c。newTransactionWithDuration创建给定耗时的 TransactionCatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long duration);由于duration已经被指定Transaction 在完成时不会重新计算耗时。注意这个 API 不会自动完成 Transaction你仍然需要手动调用complete。它在 client.c 中等价于CatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long duration) { CatTransaction* trans newTransaction(type, name); trans-setDurationInMillis(trans, duration); if (duration 60 * 1000) { trans-setTimestamp(trans, GetTime64() - duration); } return trans; }可以看到源码在指定耗时的同时若耗时小于 60 秒还会把timestamp拨回让该 Transaction 在时间轴上看起来发生在过去。文档中的等价代码写作t-setDurationInMillis(t4, duration)t4为笔误实际应为t。再次强调不要忘记完成 TransactionnewCompletedTransactionWithDuration记录并立即完成int duration 1000; newCompletedTransactionWithDuration(type, name, duration);用于记录一个给定耗时毫秒的 Transaction 并自动完成它。由于 Transaction 已被自动完成其timestamp会被拨回但只有当给定耗时小于 60,000 毫秒时才会拨回timestamp超过一分钟的耗时不会做时间回溯。其实现client.c等价于// return current timestamp in milliseconds. unsigned long GetTime64(); CatTransaction *t newTransaction(type, name); t-setDurationInMillis(t, duration); if (duration 60000) { t-setTimestamp(t, GetTime64() - duration); } t-complete(t); return;这个 API 非常适合上报已经发生、但当时没有埋点的耗时操作例如在清理资源、释放连接、兜底日志等收尾场景中补记一次远程调用的总耗时。Event无耗时的轻量消息Event 是简化版的 Transaction没有耗时概念用于记录一次发生了什么如一次命中、一次失败、一次异常。它会被挂到当前栈顶 Transaction 的儿子列表中。logEvent记录一个 Eventvoid logEvent(const char *type, const char *name, const char *status, const char *data);type与name用于分类与命名status传入CAT_SUCCESS0或CAT_FAIL-1等状态data为可选的调试数据可为NULL。其实现client.c会创建一个 Event、把非空data写入消息、设置状态并立刻complete。logError记录一个 Errorvoid logError(const char *msg, const char *errStr);用于记录异常等价于logEvent(Exception, msg, CAT_ERROR, errStr);注意源码中logError还多做了一件事将当前消息树的canDiscard标志置为 0见 client.c即包含 Error 的消息树不允许被采样丢弃必须完整上报——这是异常数据不丢失的关键保证。newEvent手动创建 EventCatEvent *newEvent(const char *type, const char *name);通常情况下你不需要使用这个 API使用logEvent/logError是更好的选择因为后者会自动完成 Event 的创建、状态设置与完成动作只有当确实需要精细控制 Event 的生命周期时才手动创建并手动complete。Metric独立于消息树的聚合指标Metric 用于记录计数器与耗时型指标不会加入消息树而是进入独立的聚合通道。在 client.c 中当enableSampling开启时logMetricForCount/logMetricForDuration会把值交给addCountMetricToAggregator/addDurationMetricToAggregator做聚合当采样关闭时则直接构造 Metric 消息即时上报。logMetricForCount计数指标void logMetricForCount(const char *name, int quantity);指标每秒上报一次。例如你在同一秒内调用这个 API 三次可以在不同的线程ccat 内部使用线程安全的 map 缓存指标值只有聚合后的值求和会被上报到服务端。聚合逻辑在 aggregator_metric.c 中CatMetricData使用ATOMICLONG原子变量累加m_count每个指标名对应一条记录上报线程周期性遍历聚合 map将计数以C状态Count 类型的 Metric 消息发出随后清零计数。logMetricForDuration耗时指标void logMetricForDuration(const char *name, unsigned long long duration);与logMetricForCount类似同一秒内上报的耗时指标会被聚合唯一区别是这里使用平均值取代求和。从 aggregator_metric.c 的addTimerMetricToData可以看到每次调用会累加计数m_count与耗时总和m_durationMsSum上报时若m_durationMsSum 0则以S,C状态发送count,sum两个值即 Sum 与 Count 的二元组服务端据此计算平均值同时若设置了慢查询阈值超过阈值的调用还会额外上报name.slowCount计数。两个指标 API 都以isCatEnabled()为前置校验未初始化时直接返回不会产生副作用。Heartbeat系统心跳与自动上报Heartbeat 用于周期性上报客户端所在主机的系统状态CPU、内存、负载等由 ccat自动上报无需业务主动调用。newHeartBeat手动创建 HeartbeatCatHeartBeat *newHeartBeat(const char *type, const char *name);心跳会被 ccat 自动上报因此大多数情况下你不需要使用这个 API除非你想覆盖内置的心跳信息。当你这么做时不要忘记禁用内置心跳config.enableHeartbeat 0。从 monitor.c 的catMonitorFun可以看到内置心跳的完整流程monitor 线程每 1 秒循环一次启动时先上报一条System/Reboot事务每 60 个周期约 1 分钟且enableHeartbeat开启时上报 ccat 版本号Cat_C_Cient_Version事件随后创建一个System/Status事务内部通过get_status_report()见 monitor_collector.c采集系统状态 XML挂载到一个Heartbeat心跳消息上并完成上报。同一个 monitor 线程还负责连接维护每 1 秒检查一次 CAT 服务端连接checkCatActiveConn每 180 个周期约 3 分钟重新拉取路由配置updateCatServerConn。消息树的生命周期从创建到异步发送理解 ccat 的完整链路有助于你写出更可靠的埋点代码。整个流程可概括为创建newTransaction在 context.c 中调用catContextStartTrans若当前事务栈为空Transaction 成为消息树根节点否则被挂为栈顶节点的子节点并压入事务栈。每个线程通过线程局部存储CATTHREADLOCAL持有独立的CatContext含消息树与事务栈因此多线程埋点互不干扰。嵌套与弹出嵌套 Transaction 遵循后进先出LIFO。complete时catMessageManagerEndTrans将节点从栈中弹出当栈空最后一个 Transaction 完成时消息树被复制、flush上下文 reset。这也解释了文档强调的不要忘记 complete——未完成的 Transaction 会停留在栈中导致消息树永不完整、内存无法回收。发送flush 的消息树进入 sender 的双优先级无锁队列normal/high见 message_sender.csender 线程批量取出、按配置的encoderType二进制或文本编码、合并缓冲CAT_MERGEBUF_SIZE为 60KB通过 TCP 写往 CAT 服务端。包含 problem非CAT_SUCCESS状态或 Error 的消息树会进入 high 队列并被标记为不可丢弃保证异常数据优先且完整送达。资源回收程序退出前调用catClientDestroy()三个后台线程安全退出所有队列与内存被清理。关于消息树的最大深度与容量在 client_config.c 中有默认约束maxContextElementSize为 2000单个上下文最多元素数maxChildSize为 2048单个 Transaction 最多子节点数messageQueueSize为 10000sender 队列容量。当元素数量超过maxContextElementSize或消息跨小时边界时context.c 会执行truncateAndFlush截断并提前发送避免消息树无限增长。小结与最佳实践最后汇总 ccat 使用中的关键实践要点初始化启动时用catClientInit(appkey)或catClientInitWithConfig完成初始化程序退出前必须调用catClientDestroy()释放资源并排空发送队列事务树Transaction 必须成对出现newTransaction与complete嵌套必须严格 LIFO不要同时设置setDurationInMillis与setDurationStart异常记录错误统一走logError它会自动标记消息树不可丢弃确保异常链路完整上报指标采集计数用logMetricForCount同秒求和耗时用logMetricForDuration同秒求平均可跨线程安全调用若关闭采样enableSampling 0指标会即时以独立 Metric 消息上报心跳默认开启、每分钟自动上报如需自定义心跳内容先设置config.enableHeartbeat 0关闭内置心跳再使用newHeartBeat手动上报构建ccat 需要 C99 编译器与cmake/make在仓库lib/c目录执行mkdir -p cmake cd cmake cmake .. make -j即可构建出libcatclient.soOSX 下为.dylib安装后用gcc -lcatclient x.c链接使用详见 lib/c/README.zh-CN.md。通过合理组合 Transaction、Event、Metric 与 Heartbeat 四类消息并遵循上述生命周期与线程模型约束你可以在 C 服务中构建出与 CAT 服务端无缝对接的完整监控埋点体系。【免费下载链接】catCAT 作为服务端项目基础组件提供了 Java, C/C, Node.js, Python, Go 等多语言客户端已经在美团点评的基础架构中间件框架MVC框架RPC框架数据库框架缓存框架等消息队列配置系统等深度集成为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。项目地址: https://gitcode.com/gh_mirrors/ca/cat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门