百度垂类离线计算系统发展历程( 五 )

  • 本地队列表达:如图左下角部分表达的就是一个简单的拓扑表达,拓扑关系是通过本地的无锁队列进行串联的,每个队列下面挂载一个业务算子,业务算子处理完成数据后,算子拓扑配置管理分发到下游的本地队列中,如果是最后一个算子,数据结果全部处理完成后输出到远程队列Producer中 。左侧这边是框架内部的通用功能:
  • 远程队列表达:每个蓝色大框表示一个具体实例,实例之间使用远程队列交互,如果每个实例里面只有一个业务算子,这种的交互方式类似于远程队列的表达方式 。
  • 混合方式表达:更多使用方式是混合使用,一个巨型拓扑会拆分成多个子拓扑,每个子拓扑使用本地队列的方式进行表达,而子拓扑之间使用远程队列的方式进行表达 。
  • 多语言引擎执行层:语言执行层整体框架整体根据不同的语言有不同实现模式,大体上分成三部分:编译型、解释型、原生C++ 。
解释型:最典型的就是Python实现方式,这种方式也是过去同学最喜欢用的开发语言之一 。图上图最上面,左边灰色部分是C++开发的部分,最右边黄色部分是业务代码,中间这部分就是C++转Python的交互引擎调用 。这种方式的本质就C++的开发引擎使用PyBind实现指定接口服务解释器,根据指定的数据序列化和反序列化方式进行操作,在函数调用时再实时转化为python的Dict 。
编译型:典型是Golang的使用方式 。与Python的实现类似,左边灰色部分是C++开发的部分,最右边黄色部分是Golang业务代码,中间是C++转Golang的交互引擎 。Golang的实现相比Python的方式有点复杂,本质是通过原生CGO作为用户接口,为了统一用户接口层,其实分成两部分:左半部分是直接跟C++交互,直接用C++实现负责把原生C++转化为基础的C类型的函数指针进行调用 。右半部分使用Golang实现负责把原生序列化好的函数反序列化成业务结构体,然后再进行真正调用 。
原生C++:这里很多人可能觉得奇怪,C++不是也是编译型的么,已经有编译型的实现模式何必画蛇添足,增加这么一种实现模式,其实本质不然,golang虽然是编译型语言,底层框架的实现由于尽量考虑通用性,数据传递的过程中势必需要进行序列化和反序列操作,而在原生的C++的实现过程中我们在实现的过程完全摒弃所有的序列化和反序列操作,数据在本地队列中的传递完全是业务的数据指针(而非序列化数据),而每个业务算子在处理数据过程中,直接根据原始的数据指针通过反射机制(实现方式有很多最简单的 map)可以直接获取对应列的数据项,整个过程无锁的超高效率 。这里的核心优化思路数据链式处理过程 。拉链上的每个算子,都共享同一个输入和输出 。每个算子,其实就是一个接口相同的函数,这样就可以随意地调整函数指针的组合,形成不同的处理链 。比如:一个请求走 A 模型排序,一个请求要走 B 模型排序,他们可以共享前序的算子,只在最后一个算子有所不同 。在链式处理基础上,算子也可以是继承同一基类接口的派生类,或者 lambda 表达式 。结合工厂模式等一些编程技巧,处理链的调整可以配置化、动态化、脚本化 。实测执行业务单算子普通的纯数据项带分支的多节点的拓扑计算单机(单线程/算子)可以达到数十万的处理能力 。
通过新计算引擎的实现从使用上完全兼容上一代计算系统的使用方式,将不常使用的功能做精简,同时优化复杂拓扑执行方式,通过架构层也业务层解耦,支持多语言高效执行方式,对于老业务平迁的数据框架平均计算效率提高5~10倍,同时业务由于针对性建设业务框架业务开发效率进一步提升 。
智能控制实现
自动化问题分析引擎是整个智能控制系统的大脑 。它上游接收观测提供的原始数据,进行自动的分析决策后,通过系统提供的自愈能力处理 。自动化问题分析引擎的核心思路: 只要历史上出现过的问题,RD同学能找到问题和解决方案,就可以转化为系统规则和后置函数梳理 。那当下一次遇到问题则无需人工干预 。规则引擎的核心分析过程是2段式的:
阶段1: 传统配置化的规则引擎的配置(上图中右上角黄色部分),配置多个采集指标项的逻辑关系(与或交非),这里主要是针对问题的基础分析功能,判定规则是否触发 。
阶段2: 基于这个基础分析的结果,进行后置Function的执行分析,这个主要是针对复杂问题的分析补充,最终执行引擎根据这个返回结果进行函数执行 。


推荐阅读