Sanfor实习-数据信道
0 介绍
0.1 背景
SAWF的数据信道模块目前主要是指的接口层,由许多标准化的restful API组成,其价值如下:
在AIOps算法服务上解耦数据和计算,对于数据的读写操作均通过API进行,屏蔽各种数据库的具体细节,使得算法能专注于模型和计算;
具备一定的数据处理功能,能够将不同数据源(VM、Mongodb、mySQL等)的数据加工成统一的、算法易于使用的数据格式;
利于AI计算结果的存储和查询,缩短从上线到运营结果的时间;
更好的可扩展性,对于后续引入其他算法和数据源,可以加入相应的api来获取和存储数据,不影响其他已有的功能,整体改动小,降低开发成本;
标准化数据的获取和存储,优雅管理、减少维护、便于统计;
0.2 需求场景及问题
目标是将数据信道模块建设成易使用、高可用、易扩展的基础模块。
SAWF数据源:
VictoriaMetrics:时序数据等(主要)
MongoDB:配置数据、结果数据、告警等(主要)
MySQL:配置信息等
ClickHouse:日志信息等
nebula:图数据库
(待扩展)
主要需求:
Read API:
AIOps算法需要从各种数据源拉取各种数据进行计算;
算法的结果数据可被二次查询。
Write API:
算法的计算结果存入aiops的数据库(MongoDB);
产生的告警数据存储到aiops的数据库(MongoDB);
日志服务产生的日志信息需要存入aiops数据库(ClickHouse)。
健康检查:检查后端存储数据库是否健康状态;
批量处理:支持读写的批量操作,提高查询和读写效率。
目标和问题:
易使用:数据的读写更简单易用,数据的格式更标准统一,改变现在各自为战、风格迥异的现状。
高可用:将数据信道模块建设成统一的数据入口和出口,可以复用代码、减少开发成本、避免重复工作,但是也会带来性能、容错等问题,特别是VictoriaMetrics的访问压力较大,需要保证模块的可用性和容错性。
易扩展:无论是引入新的算法模型、新的数据库还是新的数据源,均可以在不影响其他模块和功能的前提下,以较小的改动(例如仅仅修改配置文件)来支撑新的内容。
0.3 选型结论
基于goFrame框架建立一整套覆盖各个数据源的restful风格的API,以HTTP请求的方式读写数据,具备一定的计算能力,支持对数据格式进行灵活的变换。
gf框架支持docker镜像编译,默认提供了Kubernetes集群化部署的Yaml模板,通过kustomize管理。
gf框架的ORM组件功能完备、扩展性强,但仅支持mySQL和redis,其他数据源需根据情况自行开发。
1 开发
1.1 框架选型
Gin | GoFrame | |
---|---|---|
优势 | 轻量级,简单易用,高效 | 活跃的中文社区,详细的中文文档,模块化设计和工程化设计思想,组件丰富 |
社区 | 英语 | 中文(国内) |
缺点 | 缺少配套的开发组件 | 缺少成熟的实践 |
框架性能 | 快 | 较快 |
开发组件 | 基本没有,需要自行实现或者用第三方 | 全面、开箱即用且不断更新优化中 |
路由算法 | 压缩前缀树:快,但是规则苛刻 | 快,精准匹配用哈希表;带参数路由用链表;对象注册路由的方式方便实现mvc |
官方文档 | https://gin-gonic.com/zh-cn/docs/ | https://goframe.org/pages/viewpage.action?pageId=1114399 |
简介 | Go语言写的轻量级HTTP Web框架。它提供了Martini风格的API并有更好的性能。 | 工程完备、简单易用,模块化、高质量、高性能、企业级开发框架。 |
项目地址 | http://github.com/gin-gonic/gin | http://github.com/gogf/gf |
GoFrame框架更加适合,主要优势:
针对业务项目而言,提供了开发规范、项目规范、命名规范、设计模式、开发工具链、丰富的模块、高质量代码和详细的文档,社区活跃。
提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、 并发安全容器等等。
提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、路由注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
1.2 工程组织
gf框架给出了详细且严格的组织目录模板,通过gf命令可自动生成:gf init demo -u
目录/文件名 | 说明 | 描述 | 具体业务 |
---|---|---|---|
- api | 对外接口 | N/A | |
|L v1 | 当前接口版本 | N/A | |
||- victoria.go | vm接口文件 | 路由注册 | |
||-mangodb.go | mangodb接口文件 | 路由注册 | |
|L XXX | 待扩展 | 可扩展mySQL、redis等 | |
|-hack | 工具脚本 | 存放项目开发工具、脚本等内容,主要是CLI工具的配置。 | 暂时不用 |
|- internal | 内部逻辑 | 业务逻辑存放目录。通过Golang internal特性对外部隐藏可见性。 | N/A |
||-cmd | 入口命令 | 项目的总管理目录 | 总路由管理 |
||-const | 常量定义 | 项目所有常量定义。 | 所有常量定义 |
||-controller | 接口处理 | 接收/解析用户输入参数的入口/接口层,转到具体的业务逻辑。 | 转到logic处理数据 |
||-dao | 数据操作对象 | 通过对象方式访问底层数据源,底层基于ORM组件实现。往往需要结合entity和do通用使用。其中的文件按照数据表名称进行命名,一个数据表一个文件及其一个对应的DAO对象。操作数据表即是通过DAO对象以及相关操作方法实现。支持MySQL。 | 暂时不用 |
||-logic | 业务封装 | 业务逻辑实现和封装。 | 业务逻辑:获取数据、处理数据、存储数据等。 |
||-model | 结构模型 | N/A | |
|||-do | 数据转换模型 | 由gf gen dao生成。数据转换模型用于业务模型到数据模型的转换,由工具维护,不能修改。 | N/A |
|||-entity | 数据模型 | 由gf gen dao生成。数据模型即与数据表一一对应的数据结构,不能修改。 | N/A |
|||-victoria.go | 业务模型文件 | 业务模型即是与业务相关的数据结构,自行按需定义。 | 自定义所需的数据结构 |
||Lmango.go | 业务模型文件 | 业务模型即是与业务相关的数据结构,自行按需定义。 | 自定义所需的数据结构 |
|Lservice | 业务接口 | 由gf gen service根据logic包生成。生成后需要1)在每个业务模块中加上接口的具体实现注入;2)在main包的最顶部合适位置引入同步生成的一个接口实现注册文件logic.go | N/A |
|-manifest | 交付清单 | 包含程序编译、部署、运行、配置的文件。 | N/A |
||-config | 配置管理 | 配置文件存放目录。 | 项目配置文件(用toml),会自动解析。 |
||-docker | 镜像文件 | Docker镜像相关依赖文件,脚本文件等等。 | 暂时不用 |
|Ldeploy | 部署文件 | 部署相关的文件。默认提供了Kubernetes集群化部署的Yaml模板,通过kustomize管理。 | 暂时不用 |
|-utility | 工具函数 | 公共函数在这里 | |
Lmango.go | 入口文件 | 程序入口。 | 程序入口。 |
2 设计细节
遵循数据信道模块的目标:易使用、高可用、易扩展的基础模块,仅实现对各数据源的封装,提供restful的API供其他业务模块(前端、算法等)使用。
2.1 易用性
模块设计了一整套满足restful风格的API,逐步覆盖各类数据源,通过HTTP请求获取JSON格式的数据,以HTTP请求方法区分、参数控制。
具体请参考本文档第3节【接口设计】。
2.2 可用性
2.2.1 横向扩展
横向扩展能力主要是指模块水平扩展:主要是指面对业务压力时,能够通过水平扩展达到负载均衡,从而保证服务的可用性。
压力主要集中在Victoria Metrics数据库,VM数据库天然支持水平扩展。而API作为VM数据库的数据出口同样也会面临访问压力,因此,一方面,模块需要保证无状态来支持水平扩展;另外一方面,所选用的开发框架gf默认支持docker镜像部署和Kubernetes集群化部署,通过kustomize管理(默认提供了Kubernetes集群化部署的Yaml模板,细节需要后续深入学习),能够进行水平扩展,通过分布式多节点来实现负载均衡。
2.2.2 查询缓存
AIOps中的算法、前端等业务对数据的操作均已读为主,对于短时间重复的读操作,若能设置查询缓存,将能有效的提升查询性能。
单进程内存缓存:优点是性能非常高效,但是只能在单进程内使用,若服务采用分布式多点部署,多节点之间的缓存可能会失效/产生数据不一致的情况。
分布式查询缓存:更加适用大多数的场景,通过Redis服务器来实现查询数据的缓存,Redis缓存在多节点保证缓存的数据一致性时非常有用。
gf框架支持缓存管理:内置的gcache模块采用了适配器设计模式,提供了Adapter适配器接口,任何实现了Adapter接口的对象均可注册到缓存管理对象中,使得开发者可以对缓存管理对象进行灵活的扩展。并且框架通过社区模块提供了gcache的Redis缓存适配实现,API服务可以直接拿来使用。仓库地址:https://github.com/gogf/gcache-adapter。
2.2.3 流量治理
开发框架gf正在设计自己的微服务熔断和限制模块。
1.服务容错
AIOps中不可避免的会出现一些意外情况,比如数据库不可用/响应超时、API本身崩溃、网络中断等,为了防止整个系统雪崩或者因为错误占用大量服务器资源,服务需要具备一定的容错性才能保证其可用性
问题 | 容错设计思路 | 说明 | 参考实现 |
---|---|---|---|
数据库不可用/响应超时 | 熔断 | 如果某个数据库不可用或者大量超时,此时,熔断数据库的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。 | Netflix的开源项目Hystrix、go-kratos/aegis实现的google_sre算法 |
API模块崩溃 | 重启 | 因为意外情况服务挂掉 | K8S支持监控并重启 |
2.流量控制
作为AIOps的数据出入口,API服务会承受流量压力。算法等上游服务对本服务请求QPS过大时,需要通过一定的策略(如延迟处理、拒绝处理)对上游服务的请求量进行限制,以保证本服务不被压垮,从而持续提供稳定服务。常见的限流算法有滑动窗口、令牌桶、漏桶等。
算法 | 优点 | 缺点 | 参考实现 |
---|---|---|---|
自适应模式(容器化) | 根据硬件配置,设置load或者cpu占用率,根据设置的cpu占用率和load阈值,获取过去一段时间内的请求数量。将这个数量作为限流,如果系统负载达到这个显示,将这个请求数量作为请求阈值。 | 根据系统资源利用率进行限流,会因为其它程序或进程的高cpu占用率触发限流。这部分可以采用docker 封装,获取docker 的相关占用率进行避免。 | alibaba/Sentinel、go-kratos/kratos |
漏桶模式 | 实现简单;基本实现了平滑处理,在单位时间内,可以控制请求数量。 | 1. 不考虑服务器负载,如果有大量请求会对请求进行缓存,按照漏桶流量进行处理;2. 难以配置合适的参数。 | N/A |
滑动窗口模式 | 细分时间精度可以避免请求超过阈值,可以应对简单的突发流量。 | 1. 使用内存或者redis来维护窗口,如果时间颗粒度太细,会造空间容量过大;2. 否决式限流,很难进行阻塞等待处理,无法“削峰填谷”。 | N/A |
令牌桶模式(非容器化) | 实现简单;限制平均流入速率,允许一定程度突发请求(支持一次拿多个令牌)。 | 不好估算流入速度, 需要根据不同机器或环境进行配置。 | N/A |
3.服务降级
API自身服务压力增大时,采取一些手段,增强自身服务的处理能力,以保障服务的持续可用,常用的手段有读旧数据、降低实时性、降低数据一致性等。
需要在后续根据实际业务情况具体分析。
2.3 扩展性
在不影响已有功能和服务其他模块的前提下,以较小的改动(例如仅仅修改配置文件)来引入新的业务需求、新的数据库还是新的数据源。
场景 | 说明 | 解决思路 |
---|---|---|
新增业务需求 | 主要是指当有新的算法模型、业务模块需要使用数据时调用API。 | API的设计在满足易用的前提下,应该提供尽可能全面可用的接口供上层业务(包括算法、前端等)使用,避免接口开发不完备和冗余。 |
新增已有数据库实例 | 能够在不更改代码的情况下通过增加/修改配置文件的方式引入新的已有的数据库实例。 | gf框架的数据库ORM支持mySQL和redis的相关实现,对于AIOps使用较多的mongodb缺乏支持,需要根据gf的源码自行实现/寻找成熟的开源实现(七牛封装了一个)。 |
新增数据源 | 主要是指当业务有需要时引入新的数据库类型,例如后续可能会加入的图数据库nebula。 | 当有此类需求时需要对模块的代码进行迭代,新增代码对相应的数据库类型的功能进行封装,并且不会影响已有的功能和业务。 |
3 接口设计
3.1 Read API(P1)
支持以GET接口的方式获取AIOps算法需要的数据,后续还需要支持获取结果数据(从VM,MongoDB中)供前端、监控等模块使用。
边界定义如下:
单次请求获取数据时间范围不超过7天
单次请求获取数据返回的总条数不超过10万条
单次请求超时时间为3s
3.1.1 Victoria Metrics API
Victoria Metrics数据库/普罗米修斯提供默认且全面的restful api接口,因此数据信道模块只需要在原始接口的基础上进行封装,使得整体接口的风格统一。
配置字段名 | 说明 | 示例 |
---|---|---|
vm_url | 实际普罗米修斯API的url | http://10.109.134.70:30481/select/0/prometheus/api/v1/ |
查询单点时序数据
接口名称 | 查询单点数据 |
---|---|
所属模块 | v1/victoria |
接口功能 | 查询单时间点数据v1/victoria |
方法 | GET |
URL | /v1/victoria/single |
请求参数 | |
参数名 | 类型 |
———— | —————— |
metrics | string |
includeEndpoints | string |
excludeEndpoints | string |
includeTags | string |
excludeTags | string |
aggrFunc | string |
aggrKeys | string |
time | uint64 |
isTransform | bool |
customAdditionalKey | string |
others | map[string]string |
响应参数(转换) | |
参数名 | 类型 |
———— | —————— |
metric | string |
endpoint | string |
tags | map[string]interface{} |
values | []map[string]float |
请求示例:
1 | /v1/victoria/single?metrics=host_net_dp_sess_use&includeEndpoints=host-0894efb78dc1 |
接口正常 (不转换)
1 | { |
响应示例 (转换)
1 | { |
接口异常响应参数:
参数名 | 类型 | 必选 | 说明 |
---|---|---|---|
success | int | Y | 接口异常响应时为0 |
errcode | int | Y | scc错误码 |
message | string | Y | 错误信息(unicode编码) |
接口异常响应示例:
1 | { |
查询范围时序数据
查询标签
3.1.2 MongoDB API
目前的gf框架不支持mongodb,需要额外引入第三方库,go语言常用mongodb驱动是mgo和mongo-go-driver,二者的比较如下:
mgo | mango-go-driver | |
---|---|---|
优点 | 特性丰富 实践丰富 文档充足 简单易上手 |
官方驱动 设计底层,效率更高 支持事务 功能更全面 |
缺点 | 停止维护 | 上手较复杂 |
考虑到代码之后的扩展性,选择功能更加强大且全面的官方驱动mongo-go-driver,并且mongo-go-driver支持在建立连接时可以通过option设置超时时间等加入限制,更加贴合业务需求。
可配置信息
查询文档数量
3.2 Write API(P1)
支持以POST接口的方式写入数据。
主要接收数据如下:
AIOps算法的结果数据,写入MongoDB
告警数据,写入MongoDB
边界定义如下:
单次请求写入数据大小不超过10MB,如果超过,需要以迭代形式处理
单次请求超时时间为3s
3.3 Healthy API(P1)
支持检查后端存储是否健康状态,支持检查的后端存储有:
MongoDB
VictoriaMetrics
ClickHouse
MySQL
3.4 Batch API(P2)
支持批量读写数据,是在Read API 和 Write API上的二次封装。主要用于批量操作,提高查询和读写效率。
4.REST API错误代码
rest api应当有合适的状态码或者响应码来反映错误,通常控制在20个以内常用的,并且出错后需要在响应体补充细化的error信息(包含code和message)。
代码 | 描述 | 说明 |
---|---|---|
200 | 描述正确 | 不明确区分时返回 |
201 | 资源正确创建 | Write API成功写入数据时返回 |
202 | 请求正确,处理中 | 计算/处理中,暂时没法返回结果 |
204 | 请求正确,无返回 | Write API成功删除数据时返回 |
400 | 请求错误 | 参数不正确 |
401 | 未授权/未认证 | 请求的时候没有带上Token/认证失败 |
403 | 禁止访问 | 访问权限不对 |
404 | 找不到资源 | |
405 | 不可使用的HTTP方法 | |
409 | 唯一性的资源冲突等 | |
500 | 服务器错误 | 不明确区分时返回 |
503 | 服务不可用 | 超时/数据库连接失败等 |
5.安全与校验
JWT:Json Web Token
优点:基于JSON,通用;构成简单,便于传送;服务端不保存会话信息,易于横向扩展;可附加一定的非敏感信息。
GoFrame的gvalid。
6.hydra性能测试
本次测试了1)通过hydra连接mongodb和直连mongodb获取数据性能差别;2)hydra分批获取数据性能差别,如下表所示:
说明:查询循环10次。
结论:通过hydra查询数据相较于直连mongodb会有额外时间开销:单次获取数据区别不大,连续获取数据hydra耗时约为直连的2~3倍(因为连续获取同库同集合数据直连会有导管加速)。
结论:分批查询数据会造成较大的额外时间开销。