七叶笔记 » golang编程 » 干货长文:网易游戏数据流平台搭建全过程曝光!

干货长文:网易游戏数据流平台搭建全过程曝光!

【IT168 专稿】本文根据洪增林老师在2018年5月12日【第九届中国数据库技术大会】现场演讲内容整理而成。

讲师简介:

洪增林, 网易游戏 资深开发工程师,网易游戏数据中心开发负责人,专注于统一数据流建设、大数据作业平台设计开发和大数据组件优化等工作。

摘要:

1. 网易游戏的数据业务特点和数据流的挑战

2. 数据流采集架构和实践 (1). 数据流平台架构 (2). 服务器日志采集的设计与实现 (3). 客户端数据提交入口设计 (4). 后续迭代计划

3. 基于数据流支撑的业务介绍 (1). 游戏数据集成 (2). 程序/运维类日志归集与应用 (3). 通用数据处理和转换

4. 总结展望 技术点:日志归集、数据集成、数据流平台、golang、高并发、性能优化 面向人群:大数据平台设计和开发人员

正文:

一、网易游戏统一数据流背景

首先,我简单介绍一下网易游戏开发部门,我们目前运营在线的游戏有上百款。从2014年开始,公司从端游市场转向手游,与端游市场相比,手游市场的研发周期大幅缩短,上线周期和迭代速度加快,对服务器的需求量大增,以阴阳师为例,上线之前内部规划采购几十台服务器,上线之后预计砸了上千台服务器,基础设施数据流波动非常大。

此外,手游生命周期相对较短,大部分游戏半年之内热度会明显下降,如何有效回收利用资源成为一大难点。

总结来说,我们面临的最大挑战是应对突增数据(流量高峰)的能力,每天的新增数据大概在百TB级别,这些数据主要来自玩家运营数据、客户端性能监控数据以及基础架构数据。

以上是网易游戏数据分析业务部分示意图,我们团队主要负责绿色模块以下的部分,我们在数据管道建设以及整个组件封装上投入了很大精力,主要提供机器学习服务、实时计算、离线计算和OLAP能力。

其中,OLAP部分主要使用Kylin和Presto实现。在此之上,我们不希望组件和计算存储过多直接暴露给用户,所以封装了一些作业平台以及SaaS服务。

对于数据分析服务,下游通常会使用各种高大上的存储和分析引擎以支持上游数据分析业务,所以我们需要一个稳健的数据流支撑。接下来,我主要想跟大家分享网易游戏的数据流转过程。

如果你对日志和数据流有所了解,那你对上图一定不陌生。如果一家公司没有统一的数据流,那么数据生产方和使用方会非常混乱,这也是网易游戏最开始的生存现状。

我们的数据大概有两种来源,一是 玩家客户端 ;二是 游戏服务器 ,但我们下游有非常多使用方,使用方之间相互调用混乱,我们希望最终可以出现统一的中间层(如下图),目前我们也在用这种方式实践整体架构。

如果要做统一数据流层,我们就需要思考基本要求是什么。在网易游戏,我们做了如下几点思考:一是 数据归集分发 ,我们希望将数据传送到统一的中间层,因此需要对下游使用方进行统一分发;二是 保证数据不重不丢 ;三是 传输低延迟 ,因为下游有很多实时业务包括实时推荐,因此整体延时性不能太高;四是 低成本 ,因为我们有很多海外节点,所以需要考虑节点之间传输不能占用太多传输带宽,需要把控整体成本不能过高。

二、统一数据流架构

上图为网易游戏数据流平台示意图,我们的数据来源主要有三种途径——服务器日志、客户端日志和数据库,客户端日志存放了各种用户行为日志,比如玩家副本信息等,数据库可能会存储一些业务数据,主要涉及MySQL数据库、 MongoDB 等,三种来源的数据最终经过统一数据流平台,将整个细节屏蔽最终放到Kafka集群中。

下游,我们主要用到了一些组件,比如实时性相关的Storm、Spark Streaming和Flink;存储、离线部分主要应用了HDFS、Hive、HBase三大组件。目前业务系统使用较多的是SQL组件,比如MySQL、MongoDB和Redis,这三者在我们内部基础架构中有相应的SaaS服务。

最后是Kafka集群,我们的Kafka中涉及多个使用方,每个使用方的数据可能都是一个子集,因此我们需要进行额外分发,将某一个子集转发到专门的Kafka topic中。

如果想把数据从客户端和服务器日志流转到数据中心,我个人认为大概需要两个维度配合:客户端和服务器。我们希望可以将客户端与服务器统一放到服务器日志Agent层,也就是说,我们需要把客户端玩家上传的数据放到服务器上。

首先,我们需要一个外网数据API或者Proxy收集数据,之后统一用服务器日志Agent上传。每个游戏拥有自己的服务器Agent,我们现在大概有几万台游戏服务器,我们需要在每一台重新部署Agent。

日志到达Agent之后,Agent负责统一定义要采集的信息,然后动态扫描哪些文件已经更新,读取更新文件的增量更新,传递到Kafka Proxy。整个过程中的大部分组件都是我们自研的,接下来我会具体介绍这些组件分别解决了哪些问题。

对于外网数据API,我认为最基本的两个要求是高可用和高性能。如果做不到高可用,玩家上传数据就可能会丢,即便客户端可以重试,但重试也有上限。

其余两个要求是全球主要Region和管理系统,因为网易游戏是全球战略,因此全球主要的Region,比如日本、北美、欧洲以及东南亚等均有发行,外网数据API也会发布到相应节点,底层直接使用AWS服务器即可。至于管理系统,主要作用是降低整体成本。

外网数据API主要通过web服务器收集日志请求并落盘,我们自研了高性能web端,可支持动态加载配置;日志按照业务落盘,复用日志Agent数据流;日志rotate,监控metrics上报。一些开源组件也可实现上述操作,我们目前的整个流程如上图所示。

首先,客户端通过https传输数据,这部分数据经过LB入口(海外走elb)到达服务器后,我们会做三件事情,一是 数据校验 ,拦截非法请求和预期之外的数据提交;二是 自定义数据转换 ,我们会将全球所有数据放在一起分析,在前端根据域名直接将各区域的时区自动转换好,三是 持久化到文件 ,第二步的数据转换完成之后,我们就可以通过go channel将其持久化到文件中,然后管理它的rotate。

上图为我们定义外网数据采集时自动生成的配置页面,Web接收数据端会根据生成的配置监听各域名提交数据的方法、磁盘位置以及rotate配置。

这里需要说明的一个问题是version,如果我们在中央区配置更新,这个版本号会自动刷新,web端每隔一分钟会进行一次轮询,如果发现版本与内存版本不同就会触发reload。我们通过这种方式解决配置自动加载问题,但并不是实时配置更新。当然,我们认为一分钟时效是可以接受的。

在性能层面,我们内部使用的是32核、64G的服务器,Goland 1.9.1,fasthttp,单机50w qps左右,整个资源占用率较低,至少目前可以满足我们的内部需求,并保有很大余量。

通过自研web端,数据从玩家端上传到服务器,这时,我们要用日志Agent接力上传。日志Agent需要保证日志不重不丢,在运行过程中保持传送状态,这个状态是事务更新的;降低配置成本,我们会做中央化管理,根据机器tag分配任务;支持多种业务同时传输。

因为无论是系统日志分析还是程序日志检索,入口都是Agent,因此我们要在一台服务器上支持多种业务同时传输,并可根据任务优先级做出有效调整,最后是高效运维,具备完善的管控平台。

以上是我们自研的日志Agent架构设计,基本原理与开源Flume等类似。首先,这里会涉及一个 Scanner 的概念,Scanner的字面意思就是扫描,我首先需要弄清楚一台游戏服务器上到底有多少文件和目录需要扫描上传,我们会在配置中心定义这些东西,文件扫描使用FileScanner,DB方面使用DBScanner,目前主要涉及MySQL和MongoDB。

Scanner扫描数据初始化时要加载offset记录,增量扫描所监听的文件和目录,如果获取到更新消息可使用batch读的方式传到Reader中,我们额外会有单独的线程周期性发一个心跳,这是内部监控中用的。

最后,所有数据会传到 Queue中,这是一个数据缓冲区的大小,如果有网络IO或者磁盘IO的场景,你可能需要缓冲区来解耦生产者和消费者,不然吞吐量上不去。

之后,我们会把Queue封装成一个优先队列,高优先级任务数据率先通过并发Sender读取,Thrift RPC会将这些日志发送到kafka proxy中。

为了保证数据可靠性,整个过程必须是同步的,如果发送成功,就更新offset;如果发送失败,则进入等待重试队列,如果重试了几次均失败,就报警给 SRE 的同学。

整个过程中需要配置的部分同样在配置中心完成,配置中心同样有版本号的概念,每隔一分钟刷新一次。主要的核心配置,比如数据源配置、缓冲区大小、并发数等是为了调整发送效率,数据路由会根据业务特性进行调整。

除了上述提到的扫描更新方式外,Scanner还有一种显而易见的方式就是事件通知,可以通过系统方法启动进程并监听文件,事件更新之后对外发出通知。当处于某些极端场景下时,这种方式的性能就会变得很差。

假设我现在需要扫描的文件夹下有一万个文件,我们的做法是对文件进行冷热tag标记,如果这个文件在一段时间内没有被更新,我们就会把它标记为冷文件,冷文件的扫描周期比热文件长。当然,冷热文件列表一直处于交换更新状态,通过这种方式可以达到更优的性能并避免实时监听的性能损耗。

上图为中央化任务发布流程图,SRE和业务方会基于机器tag定义任务,CMDB会管理整个tag列表,比如某台机器要采集某路径上的某个文件,CMDB会对该类任务进行完整定义和管理,机器会在启动或者轮询时发起请求,我们将SRE与CMDB的请求打通,根据IP映射到具体tag列表,最终合并出机器任务列表返回日志Agent。我们也会根据合并列表生成版本号与Agent进行比对,确定是否需要Reload。

在配置中,我们将元数据称为Meta,通过日志Agent进行管理,这里涉及两个名词需要解释——Timestamp和offset,Timestamp并不是指日志里面的时间,因为日志本身可能并没有时间,Timestamp指Agent读的时间,而offset是指Agent按照batch方式读时,每行可能都有一个offset位置。

如果你需要对日志进行精准排序,Timestamp和offset组合索引可以还原日志顺序。当然,不同的应用场景可以不断扩展meta。

发送效率如何呢?在网易游戏的场景下,主要有四大因素影响发送效率:一是发送并发数;二是数据缓冲区大小;三是batch读取大小;四是冷文件扫描周期。当Agent性能处于正常情况下时,batch读取大小和冷文件扫描周期不会过多影响发送效率;当日志数量突然猛增,二者才会产生影响。虽然,我们可以提供多种组合配置,但我们内部并未对SRE和产品完全开放,目前提供的默认配置是35MB/S,高性能配置是70MB/S。这里的前提是不对原始服务器也就是用户服务器有过高资源占用。

我之前一直在强调保证日志不重不丢,所以日志可靠性是我们非常关注的事情。对于日志异常监控,我们主要做了三件事情:Heartbeat心跳;心跳锚点计算传输延迟;文件传输延迟LAG。具体来说,如果Heartbeat停止并且持续一段时间没有恢复,我们就认为Agent可能出现了问题,我们会立刻监督Agent的状态。

其次,心跳信息不仅仅是一个心跳,还包括服务器、主机名、负载以及触发心跳的时间等信息,我们在下游设置了统一实时Flink程序处理信息,当Flink收到心跳触发时间后,它会根据当前时间计算当前从游戏服务器到数据中心的实时延迟。如果延迟过大,我们会关注是否出现异常。

对于上文提到的日志数突然猛增的情况,我们做的主要工作是通过元数据记录查询并计算文件传输延迟,同时也在尝试使用自动化的方式解析每个日志的时间。如果延迟过大,会触发报警机制。

上图为数据传送流程,数据通过Agent上报Kafka Proxy,我们内部会有多个Kafka集群,我们希望在Proxy层隐藏上层细节,不希望Kafka集群的访问权限或者端口开放给数万台游戏服务器,我们希望Kafka集群继续放在纯内网环境中运行,与游戏服务器的外网连接隔离。

经过近一年的努力,我们形成了统一的数据流,支撑了网易游戏大概50+产品,有1万多台服务器日志上报;上线Docker平台日志采集;通用类日志采集;支持Windows服务器。

三、数据流业务实践

以上是我们在数据采集、数据流转、数据分发方面做的事情,我们的下游业务最主要还是与数据流紧密相关,也可以说是紧接着kafka内层做的事情。第一层就是实时日志清洗与分发,这是我们技术团队直接接管的,简单来说,我们会管理下游日志到达Kafka之后的去向和使用者等信息。目前,我们的大部分任务还是通过Storm完成,最近部分切换成了Flink。

实时计算主要指业务方向的实时计算。业务方将实时计算逻辑打成包,放到托管平台上运行。此外,我们基于Spark Streaming封装了一套ETL框架,不需要写代码,只需要进行一些配置就可以转换读取数据,包括开放自定义接口并将数据写到不同的存储中,比如Kafka、HDFS等。

最后是通用日志检索,我先简单介绍一下程序日志检索的概念,狭隘来说,程序日志检索就是通过服务器查看日志信息。我以微服务场景为例讲解一下整个概念,微服务的好处在于我们可以将服务器拆分为不同组件,每个组件由不同的人开发,拥有独立的技术栈、迭代周期和上线更新频率。

最大的问题在于请求调用链变得非常长,查询用户异常信息的过程异常痛苦。我们最终的解决方案是在每个请求的入口注入trace ID或者在前端和proxy网关上做,所有请求都会有自己的ID,我们通过这个可以很容易得检索一条请求链。

总结来看,大规模程序日志检索主要有三点要求:检索效率高,低延迟和查询时保证顺序。目前的程序日志检索流程如下图所示,我们会对数据流进行实时清洗切分,然后将结果放到ES之中,最后通过查询Server实现查询。

四、想法与总结

以上是我今天分享的全部内容,通过整个过程,我自己对技术开发有如下三点感受:

追求简单、可靠、灵活的解决方案;

解决方案需要针对具体的场景,没有一种技术可以解决所有问题;

架构设计和选型有很多的Trade offs。

相关文章