简介
古茗的前端数据中心包含了前端监控、性能、日志、埋点等能力,还支持错误分析、埋点分析报表等功能 不仅支持小程序、web 还支持客户端 flutter、服务端 nodejs 等。由于我们有不少的 nodejs 应用,所以 nodejs 的监控也是必不可少的。
功能设计
在功能设计上分为了 8 个大模块,包含了前端所有需要的数据,故命名为大前端数据中心。
架构设计
由于平台设计包含了大量数据上报/分析的能力,所以架构设计上需要满足以下一些要求:
- 实时性:异常发现、异常告警需要较高的实时性
- 高可用:服务集群需要具有较高的可靠性,具备快速恢复、容灾等能力
- 稳定性:具备一定弹性、有处理高并发、高峰值、流量波动的能力
- 高吞吐:服务有接收大量数据日志和埋点数据,需要具备大量吞吐能力
- 客户端容错:服务故障/SDK 报错,不应该影响业务正常运行
所以我们初版架构设计如下:
客户端
客户端 SDK 中,我们设计了部分资源采样能力,这是因为我们监控了资源加载、请求、性能等数据,对于正常的数据,例如正常请求、正常加载的资源、正常性能数据,我们可以配置开启采样来减少服务端压力、当然也可以根据业务需求开启全量上报。
另外我们还支持了配置下发能力,例如采样率,上报开关、上报队列、上报通道等等。
数据网关
在数据网关中,我们也支持采样(服务端采样),用户可以选择其中之一开启,与客户端采样的区别是服务端采样更加精确和实时。
数据分流
我们将数据分流为3个通道,大数据通道、持久化通道和实时计算通道,用于满足不同的业务需求:
- 大数据通道:用于埋点业务分析、商业分析等
- 实时计算通道:用于实时计算指标,例如产品创建的分析图表、平台内置的指标等等
- 持久化通道:用于将数据写入 ES 集群,便于后置的查询和分析原始数据
埋点分析
对于错误和性能等等常规监控,大家一般都大同小异,这里介绍一下我们是如何做埋点分析的。
对于上报的埋点数据,不管是无痕埋点还是手动埋点,我们要分析就需要去查看各种图表,所以我们设计了自定义图表创建功能,可以让产品主动创建分析图表,要达到这个目的我们就需要走一个埋点的流程。
在这之前我们的流程是这样的:产品需要先和开发者提出埋点的需求,等埋点上线后,再和大数据提出数据分析的需求,最后才能根据需求定制图表。
但是在数据平台上我们就变成了这样: 产品自助创建埋点需求(直接创建点位指定给对应开发者),然后产品可以同时根据点位创建图表,上线后就可以直接查看图表了。
那么,在技术上我们做了哪些能力来支持这个功能呢:
- 预解析:我们对每个图都做了实时计算指标,可以做到创建后即可以查看,因为是计算好的指标,出图速度可以达到几十毫秒左右(直接查询 influxdb)
- 历史数据分析:对于没有事先计算好指标的历史数据,我们也提供了速度较慢的实时分析能力(查询ES)
- 另外,对于 web 端,我们支持了点位管理 、事件管理 、属性管理等等能力
日志查询
在 SDK 侧我们可以通过调用 logger
API 来上报数据,SDK 会自动采集上下文和环境信息并且还可以关联用户埋点数据:
ts
logger().debug(`xxxxx`)
logger().warn(`xxxxx`)
logger().error(`xxxxx`)
logger().info(`xxxxx`)
在平台侧我们就可以通过事件、环境信息、用户信息、日志内容等等维度来搜索日志,还可以直接指定设备来实时输出日志(规划中)。
数据串联
我们对所有数据都进行了关联,例如查询日志的时候可以通过 userId/traceid
等标记信息来串联日志、请求、埋点、错误等等。
所以不管是发现了日志不对、代码错误、请求错误等信息,我们就可以将所有链路串联起来排查问题。
Nodejs
Nodejs 模块是一个相对独立的模块,我们的 SDK 也是直接整合在框架内部,由于 nodejs 是服务端应用,所以我们针对 nodejs 做了很多单独的模块,例如 cpu 分析、GC 监控分析、服务数据监控等等。
CPU分析
主要是用包 GitHub - davidmarkclements/0x: 🔥 single-command flamegraph profiling 🔥 来进行 cpu 火焰图分析,cpu 数据使用 c++ 编写的扩展(调用 v8 api)来实现,直接使用 node node_modules/0x/cmd.js --visualize-cpu-profile
命令来生成图表(需要收集的 cpu 数据符合 0x 的格式)。
收集流程如下:
cpp
// v8::CpuProfiler 实例
static CpuProfiler *current_cpuprofiler = CpuProfiler::New(Isolate::GetCurrent());
// 启动 v8 cpu profiling
void StartCpuProfiling(const FunctionCallbackInfo<Value> &info) {
HandleScope scope;
Local<String> profilerTitle = New<String>("cpu_profiler").ToLocalChecked();
current_cpuprofiler->StartProfiling(profilerTitle, true);
}
// 停止 v8 cpu profiling
void StopCpuProfiling(const FunctionCallbackInfo<Value> &info) {
HandleScope scope;
const CpuProfile *profile = current_cpuprofiler->StopProfiling(profilerTitle);
/// - 后续对 profile 数据进行解析写入等等操作
}
GC 分析
GC 数据也是使用 c++ 编写的扩展(调用 v8 api)来实现:
cpp
NAN_GC_CALLBACK(GCTracerPrologueCallback) {
/// - 记录GC
}
NAN_GC_CALLBACK(GCTracerEpilogueCallback) {
/// - 记录GC
}
void StartGC(const FunctionCallbackInfo<Value> &info) {
/// - 做一些初始化操作
/// - 例如判断文件在不在什么的
InitGcStatusHooks(); /// - 初始化
AddGCPrologueCallback(GCPrologueCallback);
AddGCEpilogueCallback(GCEpilogueCallback);
}
void StopGC(const FunctionCallbackInfo<Value> &info) {
RemoveGCPrologueCallback(GCPrologueCallback);
RemoveGCEpilogueCallback(GCEpilogueCallback);
RemoveGcStatusHooks();
/// - 做一些收尾操作
/// - 比如重置数据之类的
}
和 cpu 数据一样,收集完成后上传到 oss 保存,然后解析数据进行图表等等分析。
结尾
古茗大前端数据平台才刚上线不久,已经扮演了不可或缺的角色,但是还需要不断打磨,希望后续可以在业务治理当中发挥重要的作用,由于平台功能繁多,架构复杂,这篇文章也是做一个简简单单的介绍,如果有什么问题欢迎大家提问和讨论。
最后
📚 小茗文章推荐:
关注公众号「Goodme前端团队」,获取更多干货实践,欢迎交流分享~