七猫日志接收系统之架构设计(上)

前言

七猫日志接收系统系列文章将会向大家介绍七猫日志接收系统及相关的埋点 SDK,总共分为四篇:

  • 七猫日志接收系统之架构设计(上)(本篇)
  • 七猫日志接收系统之架构设计(下)
  • 七猫日志接收系统之客户端埋点 SDK
  • 七猫日志接收系统之服务端埋点 SDK

本文为系列的第一篇,将带大家一起回顾七猫 2020 年至今,七猫日志接收系统近四年来的演化历程。如果您之前未了解过统计埋点系统,强烈推荐您阅读本站发布的七猫统计埋点实践一文。


背景说明

自公司七猫免费小说立项以来,日志接收系统便一直存在,初始版本的日志接收系统使用 PHP 实现,随着客户端上报的日志量越来越多,整体处理上不够高效稳定,此后我们使用 Go 对其进行重新实现并运行了一段时间。

自 2019 年以来,七猫免费小说业务迎来了爆发式增长,日活突破 2000w 大关,旧架构下的日志接收系统无法承载如此大规模的流量,2020 年年初,研发中心的客户端、基础平台和大数据等团队一起重新梳理、规划并设计了新的客户端埋点日志上报流程,并对日志接收系统进行了重构,下文将基于此次重构的新系统进行说明。

当前的七猫日志接收系统基于 Go 语言开发,使用 k8s 进行云上部署,结合 Filebeat、Kafka 等组件的使用,完成了从日志接收、格式校验、日志补全、数据落盘到分流转发等一系列数据处理逻辑。日志接收系统作为基础服务,既需要保障日志的处理效率,又需要有极高的稳定性,接下来,我们来一起回顾这四年来它的演化历程,看它是如何演化成长的。

注:为使整体架构清晰明了、便于理解,本文中我们对系统架构做了一定程度的简化,更加完整的系统架构将在后续发布的七猫日志接收系统之架构设计(下)中进行分析。


时间线

v1 版本

v1 版本只实现了接收客户端聚合日志逻辑(客户端埋点日志并非在事件触发时进行实时上报,客户端埋点 SDK 会收集、存储埋点事件,并在满足一定条件下进行批量上报,从而实现聚合日志上报,其具体的实现逻辑将在后续文章中进行分析,敬请期待),整体功能和部署都比较简单。

架构图示

v1 版本

图注:v1 版本实际为云上双可用区部署,图示中只画了单个区的部署架构。

数据流程

  1. 客户端埋点 SDK 收集、存储并通过请求 HTTP 接口上报聚合日志,请求通过 k8s 中的 Ingress 网关转发到主机上的日志处理服务实例;
  2. 日志处理服务(Service)经过认证、解密等处理后,将日志写入到主机磁盘的指定目录下的文件中;
  3. 主机中 Filebeat 实例读取磁盘中的日志文件数据并写入 Kafka 集群中;
  4. 下游归因系统等业务服务消费处理 Kafka 数据,后经一系列处理后,数据最终会写入到大数据系统中。

技术要点

  1. 如前文所述,因为历史原因,我们需要对历史老版本接口进行兼容,以及对老版本日志进行格式转换和清洗;
  2. 为了防止日志数据丢失,经过日志处理服务的数据会写入文件,从而实现持久化存储(存储周期为三天);日志数据未成功落盘时,会让客户端进行重试上报;
  3. 为了避免 Filebeat 更新、重启时重复读取日志文件,Filebeat 的读取记录也存在运行主机的磁盘上(图示中未体现);
  4. 通过创建本地持久化存储卷时配置 nodeAffinity 指定 2-3 个存储主机,确保使用该存储卷的 Pod 全部分布在指定主机上;
  5. 日志处理服务使用 Deployment 方式部署,默认滚动更新 RollingUpdate 策略,保证高可用,可弹性伸缩;
  6. Filebeat 使用 DaemonSet 方式部署,来确保每个节点上只运行一个 Filebeat Pod 的实例;
  7. 为了进一步保证日志系统的稳定性,我们在部署时使用了多分区方式部署,即在阿里云北京部署 G 区集群,在阿里云北京部署 H 区集群,平时流量基本持平,当有一个区不可用时,对另一个区集群进行扩容即可保障业务的快速恢复。

综上所述,整个 v1 架构通过容器化服务、k8s 集群部署和数据本地磁盘持久化等方式,在保证数据完整性的同时,通过解耦使整个系统中的各环节可以弹性扩展,提供了很高的可用性,基本满足了流式计算系统稳定、实时、高可用、易扩展的需求。


v2 版本

v1 版本持续运行了一段时间,随着业务进一步发展,七猫开始自建 AB 测试、推荐引擎等基础平台,需要对服务端埋点日志也进行收集处理,于是我们基于 v1 迁出了 v2 版本代码(使用 Go Mod 多版本方式),在 v2 版本的代码里面实现服务端日志接收逻辑。此时 v1 和 v2 两个版本共存,主要从职责上来划分版本:

  • v1 服务接收并处理客户端埋点日志(v1 Serivce)
  • v2 服务接收并处理服务端埋点日志(v2 Service)

架构图示

v2 版本

图注:图示中只画了一个主机(Node A)细节,实际为多主机实例部署,下文同,不再单独说明。

数据流程

v2 版本整体数据流程与 v1 版本基本一致,主要是引入了 v2 Service 独立处理服务端埋点日志:

  1. v2 版本引入 v2 Service,用于承接服务端埋点日志(由 AB 测试等系统产生);
  2. 客户端日志和服务端日志经过对应的 Service 处理落盘进入不同的文件目录,对应的 Filebeat 实例读取指定的日志文件内容,并通过不同 Topic 写入到 Kafka 中;
  3. 下游对应的业务系统消费日志数据进行处理并最终落到大数据系统。

技术要点

  1. 将客户端日志(v1 Service)和服务端日志(v2 Service)分开处理,双端日志进行业务隔离;
  2. 使用独立的 Ingress 来分别承接客户端和服务端日志数据,隔离网络相互间的影响;
  3. 基于维护成本和效率上考量,我们将之前的双分区(G 区和 H 区)部署方式改为单分区单集群部署方式(降低了一定可用性,但总体收益为正)。

因为在同一代码仓库维护两个分支的方式存在很大的不便利性,且随着算法业务的发展,我们需要接收客户端实时埋点日志,期间我们尝试将代码拆分到多个不同仓库中进行独立维护,但总体上各类埋点日志处理的业务逻辑基本一致,所以最终我们又将代码进行了梳理和整合,重新划分了服务职责,并以此为契机,日志接收系统进入到了 v3 版本时代。


v3 版本

随着七猫的业务发展,日志接收系统需要接收客户端(聚合和实时)埋点、服务端埋点、服务端通用、前端埋点(Web 和 H5)等各种类型的日志,我们着重对代码进行整合,重新划分了各个服务的职责。

架构图示

v3 版本

从图中我们可以看到,我们根据数据来源,将日志接收系统服务划分出了以下服务:

  • aggregate-client 接收客户端聚合埋点日志
  • realtime-client 接收客户端实时埋点日志
  • frontend 接收前端(Web、H5 等)埋点日志
  • server 接收服务端埋点日志(AB 测试等)
  • backend 接收服务端通用日志(其他业务需要上报和收集的日志)

数据流程

  1. 从 v3 版本开始,我们引进了 Envoy 替换 Ingress 作为网关来承接所有流量,Envoy 接收到流量后,根据请求 Path 将其转发到不同服务;
  2. 不同的服务独立处理不同类型的日志数据,并写入到对应的文件目录中;
  3. 不同的 Filebeat 实例读取日志文件并将数据写入 Kafka(不同实例或不同 Topic);
  4. 下游归因等业务系统读取并处理 Kafka 数据,数据最终落到大数据系统;
  5. 其中客户端实时埋点日志主要是算法相关业务使用,我们将这部分日志直接写入独立 Kafka,下游服务直接消费 Kafka。

技术要点

  1. 因为随着业务发展,客户端埋点日志上报越来越多,Ingress 在流量高峰时 RT(P99) 比较高,请求成功率在高峰时段跌落到 98% 左右,通过调研,我们引入了 Envoy 网关,并将 k8s 的网络插件从 Flannel 换成了 Terway,成功地解决了这个问题;
  2. 因为算法对某些客户端埋点事件的实时性要求更高,我们通过独立的客户端实时埋点服务(realtime-client)将日志直接写入 Kafka 供下游消费,这部分的数据链路相对独立;
  3. 这些服务除了请求参数、路由不同,其他如加解密、写文件等后续逻辑基本一致,所以我们将其整合在一个代码仓库中,并且将公用部分抽象成公用包。

然而随着时间的推移,处理数据、存储数据等成本越来越高,旧的数据链路也无法满足业务的快速发展,为了解决这些问题,响应公司降本提效的政策,由大数据团队牵头,我们对整个大数据链路进行了升级改造(主要针对客户端聚合日志),日志接收系统也需要进行同步升级,由此我们来到了 v4 版本。


v4 版本

架构图示

v4 版本

数据流程

v4 版本的数据流程基本与之前版本相同,主要针对客户端聚合埋点日志进行了以下优化:

  1. 根据公司大数据区分的业务线,将下游 Kafka Topic 以业务线聚合;服务将接收到的日志根据项目标识字段,将日志归属到不同的业务线,Filebeat 在消费文件时,根据业务线将日志写入下游 Kafka 不同的 Topic 之中;
  2. 为了减轻下游任务依赖以及耦合度,将通用的逻辑从下游的归因系统中抽离,前置到了日志接收系统中,由日志接收系统完成下文 3~6 的逻辑;
  3. 日志格式校验:实现日志通用字段约束等校验逻辑,并将校验不通过的日志写入特殊的 Topic 中,以供后续日志分析使用;
  4. 埋点事件合法性校验:日志接收系统定时同步埋点后台中注册的事件,对日志中未进行埋点或已下线的事件进行剔除,并将剔除掉的事件写入下游特殊 Topic,以供后续对埋点事件分析使用;
  5. 新老用户判断:根据日志中的设备标识相关字段,判断设备为活跃还是新增,生成该条日志对应设备的唯一标识信息,并写入日志,传入下游;
  6. 日志信息补全:如 IP 信息等,通过解析请求来源 IP,生成 IP 地址相关的地区信息,写入日志,传入下游;
  7. 为了进一步提高下游数据处理效率,在日志接收系统针对不同的埋点事件类别进行甄别,将广告相关的埋点事件日志写入独立目录,由一组独立的 Filebeat 消费,并直接传递至大数据广告系统中。

技术要点

  1. 由于不同的业务需要的校验和解析功能(比如一些内部项目不需要关心归因逻辑中推广渠道等信息,也不需要校验埋点事件),我们将日志中不同类别字段的校验和解析抽象成了校验器和解析器,通过配置化的形式,可以做到动态控制业务项目使用不同解析或校验功能;
  2. 对于校验器和解析器,他们各自处理的是不同的字段,为了提升性能,我们对日志字段的各部分校验进行并发处理,对各部分的解析也进行并发处理,进一步提升了响应速度,整体 RT(P99) 可以控制在 10ms 以下;
  3. IP 解析服务我们集成了内部自研的 IP 解析功能,参看本站之前发布的文章:IP 库 - IP 段生成 Trie 树过程的剪枝实现,可以做到对 IPv4 和 IPv6 两种 IP 的高效解析;
  4. 简化下游归因系统逻辑,归因系统属于业务系统,不应该处理如日志预处理、IP 解析、非法日志过滤、新老设备判断等通用基础逻辑;
  5. 将广告相关的埋点事件进行分流处理,下游归因系统不需要关心的事件不会再流入到该系统中,分流处理大大降低了归因系统及后续服务器服务器负载,降本效果明显。

总结

回顾七猫日志接收系统近四年的发展,它的成长似乎也从侧面见证了我们七猫的茁壮成长。从最开始的纯 HTTP 接口请求处理,到使用 Filebeat + Kafka 的模式兼顾效率和持久化,到不同类型埋点日志的日益丰富,再到升级 Envoy 网关以保证请求成功率,最后到 v4 的前置通用逻辑和按业务线分流。这也正是我们七猫业务的发展轨迹,从极速成长到逐步成熟稳定,从单一业务到业务多元的发展轨迹。

关于七猫日志接收系统的更多细节,我们将基于当前的 v4 版本,在下篇中围绕日志处理效率成本控制以及系统的高可用等方面进行分析和讨论。随着七猫的继续壮大,日志接收系统必将迎来越来越多的挑战,届时我们也会对后续的 v5 迭代版本进行规划与展望。