2017年,我们赢得了开发阿尔法银行投资业务交易核心的竞赛并开始工作(在 HighLoad++ 2018 上发表了关于投资业务核心的报告)
在开发过程中,系统不断发展并获得了功能,在某种程度上,我们意识到我们正在具体化一些东西,而不仅仅是为了解决严格定义的任务范围而创建的应用程序软件:我们成功了 用于构建具有持久存储的分布式应用程序的系统。 我们获得的经验构成了新产品的基础 -
我想谈谈 TDG 架构和我们在开发过程中提出的解决方案,向您介绍主要功能,并展示我们的产品如何成为构建完整解决方案的基础。
在架构上,我们将系统分为独立的 角色,每个都负责解决一定范围的问题。 单个正在运行的应用程序实例实现一种或多种角色类型。 集群中可以有多个相同类型的角色:
连接器型
Connector负责与外界通信; 它的任务是接受请求,解析它,如果成功,则将数据发送到输入处理器进行处理。 我们支持 HTTP、SOAP、Kafka、FIX 格式。 该架构允许您简单地添加对新格式的支持,并且即将推出对 IBM MQ 的支持。 如果解析请求失败,连接器将返回错误; 否则,即使在进一步处理过程中发生错误,它也会响应请求已成功处理。 这样做是专门为了与不知道如何重复请求的系统一起工作 - 或者相反,过于持久地这样做。 为了不丢失数据,使用修复队列:对象首先进入其中,只有在成功处理后才从其中删除。 管理员可以接收有关修复队列中剩余对象的警报,并在消除软件错误或硬件故障后重试。
输入处理器
输入处理器根据特征特征对接收到的数据进行分类,并调用适当的处理器。 处理程序是在沙箱中运行的 Lua 代码,因此它们不会影响系统的功能。 在此阶段,可以将数据简化为所需的形式,并且如果需要,可以启动任意数量的任务来实现必要的逻辑。 例如,在基于 Tarantool Data Grid 构建的 MDM(主数据管理)产品中,当添加新用户时,为了不减慢请求的处理速度,我们将创建黄金记录作为单独的任务启动。 沙箱支持读取、更改和添加数据的请求,允许您对存储类型的所有角色执行某些功能并聚合结果(映射/减少)。
处理程序可以在文件中描述:
sum.lua
local x, y = unpack(...)
return x + y
然后,在配置中声明:
functions:
sum: { __file: sum.lua }
为什么是卢阿? Lua 是一种非常简单的语言。 根据我们的经验,在了解它几个小时后,人们就开始编写解决他们问题的代码。 这些人不仅是专业开发人员,而且是分析师等。 另外,得益于jit编译器,Lua运行速度非常快。
存储
存储存储持久数据。 保存之前,将根据数据模式验证数据。 为了描述电路,我们使用扩展格式
{
"name": "User",
"type": "record",
"logicalType": "Aggregate",
"fields": [
{ "name": "id", "type": "string"},
{"name": "first_name", "type": "string"},
{"name": "last_name", "type": "string"}
],
"indexes": ["id"]
}
基于此描述,自动为 Tarantula DBMS 生成 DDL(数据定义语言)并
支持异步数据复制(有计划增加同步)。
输出处理器
有时需要通知外部消费者新数据的到达;为此,需要输出处理器角色。 保存数据后,可以将其传递给相应的处理程序(例如,将其变为消费者所需的形式)——然后传递给连接器进行发送。 这里还使用了修复队列:如果没有人接受该对象,管理员可以稍后再试。
缩放
连接器、输入处理器和输出处理器角色是无状态的,允许我们通过简单地添加启用所需角色类型的新应用程序实例来水平扩展系统。 存储用于水平扩展
数据属性
对象可以非常大并且包含其他对象。 我们通过将具有所有依赖项的对象存储在一个虚拟存储桶中来确保添加和更新数据的原子性。 这可以防止对象“分散”到多个物理服务器上。
支持版本控制:对象的每次更新都会创建一个新版本,我们始终可以获取一个时间片并查看当时的世界是什么样子。 对于不需要很长历史记录的数据,我们可以限制版本数量,甚至只存储一个(最新的版本),也就是说,本质上禁用某种类型的版本控制。 您还可以按时间限制历史记录:例如,删除超过 1 年的某种类型的所有对象。 还支持归档:我们可以卸载早于指定时间的对象,从而释放集群中的空间。
任务
在这些有趣的功能中,值得注意的是能够按计划、根据用户的请求或以编程方式从沙箱启动任务:
这里我们看到了另一个角色——跑步者。 该角色是无状态的,可以根据需要将具有该角色的其他应用程序实例添加到集群中。 跑步者的责任是完成任务。 如前所述,可以从沙箱生成新任务; 它们保存在存储上的队列中,然后在运行器上执行。 这种类型的任务称为作业。 我们还有一个名为“任务”的任务类型 - 这些是用户定义的任务,按计划(使用 cron 语法)或按需运行。 为了启动和跟踪此类任务,我们有一个方便的任务管理器。 为了使此功能可用,您必须启用调度程序角色; 该角色有一个状态,因此它无法扩展,但这不是必需的; 同时,与所有其他角色一样,它可以有一个副本,如果主服务器突然拒绝,该副本就会开始工作。
记录仪
另一个角色称为记录器。 它收集集群所有成员的日志,并提供一个通过 Web 界面上传和查看日志的界面。
服务
值得一提的是,该系统使得创建服务变得很容易。 在配置文件中,您可以指定将哪些请求发送到在沙箱中运行的用户编写的处理程序。 例如,在此处理程序中,您可以运行某种分析查询并返回结果。
该服务在配置文件中描述:
services:
sum:
doc: "adds two numbers"
function: sum
return_type: int
args:
x: int
y: int
GraphQL API 会自动生成,并且该服务可供调用:
query {
sum(x: 1, y: 2)
}
这将调用处理程序 sum
这将返回结果:
3
查询分析和指标
为了了解系统的操作和分析请求,我们实现了对 OpenTracing 协议的支持。 系统可以按需向支持该协议的工具(例如 Zipkin)发送信息,这将使您了解请求是如何执行的:
当然,系统提供了可以使用 Prometheus 收集并使用 Grafana 可视化的内部指标。
部署
Tarantool 数据网格可以使用发行版或 Ansible 中的实用程序从 RPM 包或存档进行部署,还支持 Kubernetes (
实现业务逻辑(配置、处理程序)的应用程序通过 UI 或通过我们提供的 API 使用脚本以存档的形式加载到已部署的 Tarantool Data Grid 集群中。
示例应用程序
使用 Tarantool 数据网格可以创建哪些应用程序? 事实上,大多数业务任务都在某种程度上与处理、存储和访问数据流相关。 因此,如果您有大量数据流需要安全存储和访问,那么我们的产品可以为您节省大量开发时间并专注于您的业务逻辑。
例如,我们希望收集有关房地产市场的信息,以便将来我们可以获得有关最佳报价的信息。 在这种情况下,我们将突出以下任务:
- 从开源收集信息的机器人将成为我们的数据源。 您可以使用现成的解决方案或用任何语言编写代码来解决此问题。
- 接下来,Tarantool 数据网格将接受并保存数据。 如果不同来源的数据格式不同,那么您可以在 Lua 中编写代码来执行到单一格式的转换。 例如,在预处理阶段,您还可以过滤重复的报价或另外更新数据库中市场中代理商的信息。
- 现在,您在集群中已经有了一个可扩展的解决方案,可以填充数据并进行数据选择。 然后您可以实现新功能,例如,编写一个服务来请求数据并每天提供最优惠的报价 - 这将需要配置文件中的几行和一些 Lua 代码。
接下来是什么?
我们的首要任务是提高开发的便利性
我们也非常注重安全问题。 目前我们正在接受俄罗斯FSTEC的认证,以确认其高安全水平,满足个人数据信息系统和政府信息系统中使用的软件产品的认证要求。
来源: habr.com