我们每天收集超过20亿个分析事件。 因此,我们可以找到一堆必要的东西:他们对心脏的点击是否多于对星星的点击,他们在什么时间撰写更详细的说明,在哪些区域他们经常错过绿色按钮。
事件收集和分析系统通常可以称为clickstream。 我将向您介绍Avito点击流的技术方面:事件的安排,事件的发送和发送,分析,报告。 如果有Google Analytics(分析)和Yandex.Metrica,您为什么要拥有自己的人,他们的点击流开发人员会因此而牺牲生命,并且为什么Go编码人员无法忘记php。

关于我
德米特里·哈萨诺夫(Dmitry Khasanov),从事网络开发十年,在阿维托(Avito)三年。 我在平台团队中工作,开发通用的基础结构工具。 我喜欢黑客马拉松 。
挑战赛
业务需要对站点上正在发生的流程有深入的了解。 例如,在注册用户时,我想知道用户从哪个区域,从哪个设备以及通过哪个浏览器登录。 表单字段是如何填充的,无论是提交的还是用户放弃的。 如果您放弃了,那么该走哪一步。 以及花费了多少时间。
我想知道如果将按钮重新涂成绿色,他们是否会更频繁地单击按钮。 移动应用程序或网站的用户是否会在摩尔曼斯克或符拉迪沃斯托克(白天或晚上)更频繁地按下绿色按钮? 来自主要用户或搜索用户; 谁曾在Avito上购买过商品或首次来过。
所有这些标志:操作系统,用户ID,请求时间,设备,浏览器,字段中的值-必须可供分析。 收集,整理和快速访问数据。
此外,通常需要拆分事件流。 当某些事件发生时,项目需要采取行动。 例如,以这种方式,获得反馈以重新训练用于模式识别和自动调节的模型,并编译实时统计信息。
借助clickstream作为产品,程序员应该很容易发送项目中的事件,而分析人员则可以轻松地管理收集到的事件并生成各种显示趋势和支持假设的报告。
基于事件流的报告。
例子1

例子2

成品工具
我们了解Yandex Metric和Google Analytics(分析),我们将其用于某些任务。 在他们的帮助下,从前端收集分析数据既好又快速。 但是要将数据从后端导出到外部分析系统,您必须进行棘手的集成。
使用外部工具,您必须独立解决拆分事件流的问题。
分析信息非常有价值。 我们已经收集了很多年,它使我们能够详细了解用户的行为。 我不想与外界分享这种知识。
立法规定必须在俄罗斯境内存储数据。
这些原因足以开发我们自己的解决方案,作为收集和处理分析数据的主要工具。
解决方案
通过存储(数据仓库,DWH)中的高性能传输(事件流处理,ESP)调度事件。 根据存储库中的数据,构建分析报告。
大事记
中央实体。 就其本身而言,这意味着事实。 在指定的时间单位内发生了一些具体的事情。
有必要将一个事件与另一个事件区分开。 这是事件的唯一标识符。
也对事件发生的时间感兴趣。 我们在任何事件中都以微秒的精度传输它。 在从前端到达的事件中,我们还会在客户端设备上固定时间,以便更准确地恢复操作顺序。
领域
事件由字段组成。 字段是分析系统的最小语义单元。 在上一段中,有一些字段示例:事件标识符,发送时间。
字段属性:类型(字符串,数字,数组),必填。
环境
同一事件可能发生在系统的不同部分:例如,可以在站点或移动应用程序中进行授权。 在这种情况下,我们发送相同的事件,但始终在其中添加事件源的唯一标识符。
来源明显不同。 它可以是内部恶魔和王冠,前端或后端服务,移动应用程序。 必须与特定来源的每个事件一起发送部分字段。
这里有“环境”的概念。 这是按源对事件进行逻辑分组,并能够为所有源事件设置公共字段。
环境示例:“服务A的后端”,“服务A的前端”,“服务A的ios应用程序”。
活动目录
开发人员和分析人员可以编辑的目录中描述了所有现有事件。 事件按环境进行逻辑分组,每个事件都有一个所有者,并保留目录中的更改日志。
目前,该目录描述了数百个字段,数十个环境和上千个事件。
朗派
我们拒绝酷刑,也不再强迫开发人员手动编写事件分发代码。 而是根据目录为公司支持的每种服务器语言生成一组文件:php,go或python。 这样生成的代码称为“ langpack”。
langpack中的文件尽可能简单,它们不了解项目的业务逻辑。 这是每个事件的一组获取器和字段设置器,以及用于发送事件的代码。
为每个环境创建一个langpack。 它分解成一个软件包存储库(对于php是satis,对于python是pypi)。 更改目录后,它将自动更新。
您不能停止用PHP编写。 用于生成langpack的服务的代码是用Go编写的。 该公司有足够的PHP项目,因此我必须记住我最喜欢的三字母编程语言,并在Go上生成PHP代码。 如果您有些失落,还可以生成测试以使用这些测试来测试生成的代码。
版本控制
参考可以编辑。 战斗中的代码无法破解。 我们根据目录生成战斗代码。 危险地。
每次更改事件后,都会在目录中创建一个新版本。 所有曾经创建的事件版本都永久存在于目录中。 因此,我们解决了特定事件不变性的问题。 项目始终指示我们正在处理的事件的版本。
如果langpack代码发生更改(例如,只有setter,但现在我们还决定添加getter),请创建langpack的新版本。 她也将永远活着。 项目始终会为其环境要求特定版本的langpack。 因此,我们解决了langpack接口不变性的问题。
我们使用semver。 每个langpack的版本均由三个数字组成。 第一个总是零,第二个是langpack代码的版本,第三个是增量。 在每次事件更改后,第三位数字更改最多。
两级版本控制使您可以编辑目录,而不会在战斗中破坏代码。 它基于两个原则:不能删除任何内容;不能删除任何内容。 您不能编辑创建的实体,而只能并排创建修改后的副本。
交通运输
与LSD上Badoo的人不同,我们从来没有学过写精美的文件 。 而且我们相信NSQ不仅是队列服务器 ,而且还是事件的传输。
他们将NSQ隐藏在一小段go代码后面,使用守护程序集为Kubernetes集群中的每个节点安排了收集器,并编写了可以将事件添加到不同源的消费者主义者。
目前,交通运输每天发生约20亿个事件。 在这样的负载下,三十个收集器以一定的余量工作。 每一个都消耗更多的处理器核心和超过1 GB的内存。
事件路由
事件发送者可以是生活在集群内部或外部的项目。 在集群内部,这些是服务后端,王冠,守护程序,基础结构项目和Intranet。 在外部,事件来自公共项目的前端,移动应用程序和合作伙伴项目。
要接收集群外部的事件,我们使用代理。 一个通用的入口点,对事件流的过滤很小,并且可能会丰富事件。 根据一般方案进一步发送运输。
通用路由方案:每个事件可以有一组收件人。 可能的接收者包括对某些事件感兴趣的共享分析存储库(DWH),rebbits或monga项目。 例如,后一种情况用于重新训练广告的自动审核模型。 模型会监听某些事件,并获得必要的反馈。
从项目的角度,没有关于路由的知识。 他们使用langpack发送事件,在其中缝制了公共收集器的地址。
贮藏
主要事件存储库是HP Vertica,几十TB。 具有适合我们的分析师的功能的列库。 界面-用于报表的Tableau。
批量记录事件在我们的存储中效率更高。 在存储器的前面是Mongo形式的缓冲区。 每小时自动创建一次自动删除集合。 收集内容会存储几天,以便在出现问题时能够在Vertica中重新启动校对。
从缓冲区Mongo读取宠物脚本。 脚本以参考为指导,我们尽量不要在此处保留业务逻辑。 在此阶段,事件丰富是可能的。
发展历程
在黑暗中跳舞的手
记录事件的需要早于意识到需要维护目录的时间。 每个项目的开发人员都想出了一种发送事件,寻找交通工具的方法。 这样就产生了许多不同语言的代码,它们位于不同的项目中,但解决了一个问题。
通常在事件分发代码中,存在一些业务逻辑。 具有此知识的代码不能移植到其他项目。 重构时,业务逻辑需要返回到项目中,而事件代码中仅符合指定的数据格式。
在此阶段,没有事件目录。 为了了解已经记录了哪些事件,事件具有哪些字段,只有通过查看代码才有可能。 要了解开发人员意外停止在必填字段中写入数据,可以在构建报告时特别注意这一点。
没有太多的事件。 根据需要添加了mongos中的缓冲区集合。 随着事件数量的增加,有必要将事件手动重定向到其他集合,以创建必要的集合。 在发送时,在项目侧决定将事件放置在特定的缓冲区集合中。 运输是流利的,它的客户是td-agent。
意识到异步
决定创建一个包含所有现有事件的目录。 我们解析了后端的代码,从那里提取了一些信息。 我们要求开发人员在事件代码中进行任何更改,都必须在目录中对此进行记录。
手动描述了来自前端和移动应用程序的事件,有时会从传输级别的事件流中捕获必要的信息。
开发人员知道如何忘记。 这导致目录和代码不同步,但是目录显示了一般情况。
缓冲区收集的数量已大大增加,维护它们的手动工作已大大增加。 一个不可替代的人出现了关于缓冲存储器的一堆秘密知识。
新运输
他们创建了一个共享传输ESP,以了解所有事件传递点。 他们把它作为一个单一的接收点。 这样就可以控制所有事件流。 项目直接停止访问缓冲区存储。
开明的点击流
基于该目录,生成了langpacks。 它们不允许创建无效事件。
引入了自动检查前端和移动应用程序到达的事件的正确性的功能。 在这种情况下,我们不会停止编写事件以免丢失数据,而是会记录错误并向开发人员发出信号。
后端中难于重构且仍未通过langpack发送的罕见事件由单独的库根据目录中的规则进行验证。 如果发生错误,则抛出一个阻止部署的异常。
得到了一个倾向于匹配目录的系统。 优点:透明性,可管理性,事件创建和更改的速度。
后记
主要的困难和教训是组织上的。 很难将涉及多个团队的计划联系起来。 更改大型旧项目的代码并不容易。 与其他团队沟通,将任务划分为相对独立和预先考虑的集成以及独立发布帮助的技巧。 当新解决方案的集成阶段开始时,Clickstream开发人员不再喜欢产品团队。 如果界面更改,则工作将添加到每个人。
创建目录是一个非常好的主意。 他成为唯一的真理来源,如果代码有差异,您可以随时向他求助。 目录具有许多自动化功能:检查,事件路由,代码生成。
基础架构不需要了解业务逻辑。 业务逻辑出现的迹象:事件从项目到存储库的过程中发生变化; 改变运输而不改变项目变得不可能。 在基础架构方面,应该了解有关事件的组成,字段类型及其强制性的知识。 在产品方面,这些字段的逻辑含义。
总有成长的空间。 从技术上讲,这是事件数量的增加,从事件创建到数据记录开始的时间减少,并且消除了所有阶段的手动工作。
有两个大胆的想法。 获取用户转换的详细图表,在不推出服务的情况下即时配置事件。 但在以下文章中将对此进行更多介绍。
附言:我在后端联合#1会议上谈到了这个话题。 香醋。 可以看到
会议上的演示文稿或视频 。