跳到主要内容

76 篇博文 含有标签「system design」

查看所有标签

Lyft 的营销自动化平台 -- Symphony

· 阅读需 3 分钟

获取效率问题:如何在广告中实现更好的投资回报率?

具体来说,Lyft 的广告应满足以下要求:

  1. 能够管理区域特定的广告活动
  2. 以数据驱动的增长为指导:增长必须是可扩展的、可衡量的和可预测的
  3. 支持 Lyft 独特的增长模型,如下所示

lyft growth model

然而,最大挑战是管理跨区域营销的所有流程,包括选择竞标、预算、创意、激励和受众,进行 A/B 测试等。您可以看到数字营销人员一天的工作:

营销者的一天

我们发现 执行 占用了大部分时间,而 分析,被认为更重要的,所花的时间要少得多。一个扩展策略将使营销人员能够专注于分析和决策过程,而不是操作活动。

解决方案:自动化

为了降低成本并提高实验效率,我们需要

  1. 预测新用户对我们产品感兴趣的可能性
  2. 有效评估并在各渠道分配营销预算
  3. 轻松管理成千上万的广告活动

营销绩效数据流入 Lyft 的强化学习系统:Amundsen

需要自动化的问题包括:

  1. 更新搜索关键词的竞标
  2. 关闭表现不佳的创意
  3. 按市场更改推荐值
  4. 识别高价值用户细分
  5. 在活动之间共享策略

架构

Lyft Symphony Architecture

技术栈包括 - Apache Hive、Presto、ML 平台、Airflow、第三方 API、UI。

主要组件

生命周期价值(LTV)预测器

用户的生命周期价值是衡量获取渠道效率的重要标准。预算由 LTV 和我们愿意在该地区支付的价格共同决定。

我们对新用户的了解有限。历史数据可以帮助我们在用户与我们的服务互动时更准确地进行预测。

初始特征值:

特征值

随着互动历史数据的积累,预测会得到改善:

根据历史记录判断 LTV

预算分配器

在预测 LTV 之后,接下来是根据价格估算预算。一个形式为 LTV = a * (spend)^b 的曲线拟合数据。在成本曲线创建过程中将注入一定程度的随机性,以便收敛到全局最优解。

预算计算

竞标者

竞标者由两个部分组成 - 调整器和执行者。调整器根据价格决定特定渠道的确切参数。执行者将实际竞标传达给不同的渠道。

一些在不同渠道应用的流行竞标策略如下所示:

投放策略

结论

我们必须重视自动化过程中的人类经验;否则,模型的质量可能会是“垃圾进,垃圾出”。一旦从繁重的任务中解放出来,营销人员可以更多地专注于理解用户、渠道以及他们想要传达给受众的信息,从而获得更好的广告效果。这就是 Lyft 如何以更少的时间和精力实现更高的投资回报率。

设计 Airbnb 或酒店预订系统

· 阅读需 3 分钟

需求

  • 对于客人
    • 按位置、日期、房间数量和客人数量搜索房间
    • 获取房间详情(如图片、名称、评论、地址等)和价格
    • 按日期和房间 ID 从库存中支付并预订房间
      • 作为访客结账
      • 用户已登录
    • 通过电子邮件和移动推送通知进行通知
  • 对于酒店或租赁管理员(供应商/房东)
    • 管理员(接待员/经理/租赁所有者):管理房间库存并帮助客人办理入住和退房
    • 清洁工:定期清理房间

架构

组件

库存 <> 预订 <> 用户(客人和房东)

供应商在库存中提供他们的房间详情。用户可以相应地搜索、获取和预订房间。在预订房间后,用户的付款也会更改 reserved_roomstatus。您可以在 这篇文章 中查看数据模型。

如何查找可用房间?

  • 按位置:使用 空间索引 进行地理搜索,例如 geo-hash 或四叉树。
  • 按房间元数据:在查询数据库时应用过滤器或搜索条件。
  • 按入住和退房日期及可用性。两种选择:
    • 选项 1:对于给定的 room_id,检查今天或更晚的所有 occupied_room,将数据结构转换为按天的占用数组,最后在数组中找到可用的时间段。这个过程可能会耗时,因此我们可以建立可用性索引。
    • 选项 2:对于给定的 room_id,始终为占用的日期创建一个条目。这样更容易按日期查询不可用的时间段。

对于酒店,同步数据

如果这是一个酒店预订系统,那么它可能会发布到 GDS、聚合器和批发商等预订渠道。

酒店预订生态系统

为了在这些地方同步数据,我们可以

  1. 使用幂等性重试来提高外部调用的成功率,并确保没有重复订单
  2. 向外部供应商提供 webhook 回调 API,以在内部系统中更新状态。

支付与记账

数据模型:复式记账

为了执行支付,由于我们调用外部支付网关,如银行或 Stripe、Braintree 等,保持不同地方的数据同步至关重要。我们需要在交易表和外部银行及供应商之间同步数据。

提醒/警报的通知者

通知系统本质上是一个延迟调度器(优先队列 + 订阅者)加上 API 集成。

例如,每日定时任务将查询数据库以获取今天要发送的通知,并按日期将其放入优先队列。订阅者将从优先队列中获取最早的通知,并在达到预期时间戳时发送。如果没有,则将任务放回队列,并让 CPU 空闲以进行其他工作,如果今天有新的警报添加,可以中断此过程。

设计 Memcached 或内存中的 KV 存储

· 阅读需 2 分钟

需求

  1. 高性能,分布式键值存储
  • 为什么分布式?
    • 答:为了存储更大规模的数据
  1. 用于小数据对象的内存存储
  2. 简单的服务器(将复杂性推给客户端),因此可靠且易于部署

架构

大局:客户端-服务器

  • 客户端
  • 给定一组 Memcached 服务器
  • 根据键选择服务器
  • 服务器
  • 将 KV 存储到内部哈希表中
  • LRU 驱逐

键值服务器由固定大小的哈希表 + 单线程处理程序 + 粗粒度锁组成

哈希表

如何处理冲突?主要有三种解决方法:

  1. 分离链:发生冲突的桶链表中包含相同索引的多个条目,您可以始终将新发生冲突的键值对附加到列表中。
  2. 开放寻址:如果发生冲突,转到下一个索引,直到找到可用的桶。
  3. 动态调整大小:调整哈希表的大小并分配更多空间;因此,冲突发生的频率会降低。

客户端如何确定查询哪个服务器?

请参见 数据分区与路由

如何使用缓存?

请参见 键值缓存

如何进一步优化?

请参见 Facebook 如何扩展其社交图存储?TAO

Lyft 的营销自动化平台 Symphony

· 阅读需 3 分钟

获客效率问题:广告投放如何花更少的钱用更少的人得到更高回报?

具体来讲,Lyft 的广告投放要服务如下特点

  1. 管理基于地域的 campaign
  2. 数据驱动的增长:增长必须是规模化的、可测量的、可预测的
  3. 支撑起 Lyft 独特的增长模型,如图:

lyft growth model

主要的挑战是:难以规模化管理跨地域营销中的各个环节,广告竞标、预算、素材、激励、选择受众、测试等等。下图是营销者的一天:

营销者的一天

我们可以发现“执行”占去了大部分的时间,而更少的时间花在了更重要的“分析和决策”上。规模化意味着减少繁复的操作,让营销人员专注于分析与决策。

解决方案:自动化

为了降低成本,提高做实验的效率,需要

  1. 预测新用户是否对产品感兴趣
  2. 多渠道优化,有效评估和分配预算
  3. 方便地管理上千个 campaigns

数据由 Lyft 的 Amundsen 系统做增强学习。

自动化的部分包括:

  1. 更新 bid 的关键词
  2. 关掉效果不好的素材
  3. 根据市场改变 referrals values
  4. 找到高价值的用户 segment
  5. 在多个 campaign 中共享策略

构架

Lyft Symphony Architecture

技术栈:Apache Hive, Presto, ML platform, Airflow, 3rd-party APIs, UI.

具体的组成模块

LTV 预测模块

用户的终身价值是衡量渠道的重要标准,预算由 LTV 和我们愿意为该地区的获客付出的价格共同决定。

我们对新用户的认知有限,随着交互的增多,所提供的历史记录会更准确地预测。

一开始的特征值:

特征值

随着历史上的交互记录的积累,做出的判断就会越准确:

根据历史记录判断 LTV

预算分配模块

搞定了 LTV,接下来是根据价格定预算。拟合出 LTV = a * (spend)^b 形式的曲线以及周围的区间里类似参数的曲线。为了找到全局最优,需要付出一些随机性的代价。

预算计算

投放模块

分为两部分,一部分是调参者,一部分是执行者。调参者根据定价,设定基于渠道的具体的参数;执行者把这些参数执行到具体的渠道上。

有很多流行的投放策略,在各色的渠道中,是共通的:

投放策略

总结

要注意人的经验在系统中的重要性,否则会 garbage in, garbage out. 当人从繁琐的投放任务解放出来,专注于理解用户、理解渠道、理解自身要传达给受众的信息之后,就能够获得更好的投放效果——花更少的时间达到更高的 ROI。

如何编写稳健的代码?

· 阅读需 1 分钟

他喜欢它

  1. 同理心 / 视角转换是最重要的。

    1. 意识到代码首先是为人类阅读而编写的,然后才是为机器执行。
    2. 软件是如此“柔软”,有很多方法可以实现同一目标。关键在于做出适当的权衡,以满足需求。
    3. 发明与简化:Apple Pay RFID 与 微信扫码二维码。
  2. 选择可持续的架构,以减少每个功能的人力资源成本。

  1. 采用模式和最佳实践。

  2. 避免反模式

    • 缺少错误处理
    • 回调地狱 = 意大利面条代码 + 不可预测的错误处理
    • 过长的继承链
    • 循环依赖
    • 过于复杂的代码
      • 嵌套的三元操作
      • 注释掉未使用的代码
    • 缺少国际化,特别是 RTL 问题
    • 不要重复自己
      • 简单的复制粘贴
      • 不合理的注释
  3. 有效的重构

    • 语义版本
    • 永远不要对非主要版本引入破坏性更改
      • 两腿变更

设计度量系统

· 阅读需 17 分钟

需求

日志与度量:日志是发生的事件,而度量是对系统健康状况的测量。

我们假设该系统的目的是提供度量——即计数器、转化率、计时器等,用于监控系统性能和健康。如果转化率大幅下降,系统应警告值班人员。

  1. 监控商业指标,如注册漏斗的转化率
  2. 支持各种查询,如在不同平台上(IE/Chrome/Safari,iOS/Android/桌面等)
  3. 数据可视化
  4. 可扩展性和可用性

架构

构建系统的两种方式:

  1. 推送模型:Influx/Telegraf/Grafana
  2. 拉取模型:Prometheus/Grafana

拉取模型更具可扩展性,因为它减少了进入度量数据库的请求数量——没有热路径和并发问题。

服务器农场

服务器农场

写入

写入

Telegraf

Telegraf

InfluxDB

InfluxDB

REST API

REST API

Grafana

Grafana

InfluxDB 推送模型

InfluxDB 推送模型

Prometheus 拉取模型

Prometheus 拉取模型

应用程序

应用程序

导出器

导出器

客户端库

客户端库

第三方


应用程序

第三方<br>应用程序

拉取

拉取

Prometheus

Prometheus

检索

检索

服务发现

服务发现

存储

存储

PromQL

PromQL

告警管理器

告警管理器

Web UI / Grafana / API 客户端

Web UI / Grafana / API 客户端

PagerDuty

PagerDuty

邮件

邮件

特性与组件

测量注册漏斗

以移动应用的四步注册为例

输入手机号码 -> 验证短信代码 -> 输入姓名 -> 输入密码

每一步都有 IMPRESSIONPOST_VERIFICATION 阶段。并发出如下度量:

{
"sign_up_session_id": "uuid",
"step": "VERIFY_SMS_CODE",
"os": "iOS",
"phase": "POST_VERIFICATION",
"status": "SUCCESS",
// ... ts, contexts, ...
}

因此,我们可以查询 iOSVERIFY_SMS_CODE 步骤的整体转化率,如下所示:

(counts of step=VERIFY_SMS_CODE, os=iOS, status: SUCCESS, phase: POST_VERIFICATION) / (counts of step=VERIFY_SMS_CODE, os=iOS, phase: IMPRESSION)

数据可视化

Grafana 在数据可视化工作中已经相当成熟。如果您不想暴露整个网站,可以使用 嵌入面板与 iframe

设计 Square Cash 或 PayPal 转账系统

· 阅读需 25 分钟

澄清需求

设计一个类似于 Square Cash(以下称为 Cash App)或 PayPal 的转账后端系统,以实现:

  1. 从银行存款和支付
  2. 账户之间转账
  3. 高扩展性和可用性
  4. 国际化:语言、时区、货币兑换
  5. 非幂等 API 和至少一次交付的去重
  6. 跨多个数据源的一致性

架构

AWS CloudHSM

AWS CloudHSM

演示层

演示层

SDK/文档

SDK/文档

移动仪表板

移动仪表板

网页仪表板

网页仪表板

仪表板客户端

仪表板客户端

移动钱包

移动钱包

网页钱包

网页钱包

钱包客户端

钱包客户端

商户 


用户

商户 <br>用户

最终用户

最终用户

网页 Chrome 扩展

网页 Chrome 扩展

操作员

操作员

支付

支付

任务队列

任务队列

财务报告

财务报告

支付网关

支付网关

银行 / 


供应商

[不支持的查看器]

副作用制造者

副作用制造者

帮助服务门户

帮助服务门户

用户


配置文件


AuthDB


[不支持的查看器]

API 网关


单体


API 网关<br>单体<br>

支付


数据库


支付<br>数据库<br>

Aurora

Aurora

风险控制

风险控制

风险控制

风险控制

事件
队列

[不支持的查看器]

功能和组件

支付服务

支付数据模型本质上是“复式记账”。每个账户的每一笔入账都需要在另一个账户中有相应的对立入账。所有借方和贷方的总和等于零。

存款和支付

交易:新用户 Jane Doe 从银行向 Cash App 存入 100 美元。这一笔交易涉及以下数据库条目:

记账表(用于历史记录)

+ 借方, 美元, 100, CashAppAccountNumber, txId
- 贷方, 美元, 100, RoutingNumber:AccountNumber, txId

交易表

txId, 时间戳, 状态(待处理/已确认), [记账条目], 叙述

一旦银行确认交易,更新上述待处理状态和以下资产负债表,均在一笔交易中完成。

资产负债表

CashAppAccountNumber, 美元, 100

在 Cash App 内部账户之间转账

与上述情况类似,但没有待处理状态,因为我们不需要慢速外部系统来更改其状态。所有记账表、交易表和资产负债表的更改都在一笔交易中完成。

国际化

我们在三个维度上解决国际化问题。

  1. 语言:所有文本,如文案、推送通知、电子邮件,均根据 accept-language 头部进行选择。
  2. 时区:所有服务器时区均为 UTC。我们在客户端将时间戳转换为本地时区。
  3. 货币:所有用户转账交易必须使用相同货币。如果他们想要跨货币转移,必须先以对 Cash App 有利的汇率兑换货币。

例如,Jane Doe 想以 0.2 的汇率将 1 美元兑换为 6.8 人民币。

记账表

- 贷方, 美元, 1, CashAppAccountNumber, txId
+ 借方, 人民币, 6.8, CashAppAccountNumber, txId, @7.55 人民币/美元
+ 借方, 美元, 0.1, ExpensesOfExchangeAccountNumber, txId

交易表、资产负债表等与存款和支付中讨论的交易类似。主要区别在于银行或供应商提供兑换服务。

如何在交易表和外部银行及供应商之间同步?

  • 使用幂等性重试以提高外部调用的成功率并确保没有重复订单
  • 检查待处理订单是否已完成或失败的两种方法。
    1. 轮询:定时作业(SWF、Airflow、Cadence 等)轮询待处理订单的状态。
    2. 回调:为外部供应商提供回调 API。
  • 优雅关闭。银行网关调用可能需要数十秒才能完成,重启服务器可能会从数据库恢复未完成的交易。该过程可能会创建过多连接。为减少连接,在关闭之前停止接受新请求,并等待现有的外发请求完成。

去重

为什么去重是一个问题?

  1. 不是所有端点都是幂等的
  2. 事件队列可能是至少一次的。

不是所有端点都是幂等的:如果外部系统不是幂等的怎么办?

对于上述 轮询 情况,如果外部网关不支持幂等 API,为了不淹没重复条目,我们必须记录外部系统给我们的订单 ID 或参考 ID,并通过订单 ID 查询 GET,而不是一直使用 POST

对于 回调 情况,我们可以确保实现幂等 API,并且无论如何我们将 待处理 更改为 已确认

事件队列可能是至少一次的

  • 对于事件队列,我们可以使用一个完全一次的 Kafka,生产者吞吐量仅下降 3%。
  • 在数据库层,我们可以使用 幂等性密钥或去重密钥
  • 在服务层,我们可以使用 Redis 键值存储。

可用性和扩展性

设计支付 webhook

· 阅读需 4 分钟

1. 澄清需求

  1. 一旦支付成功,webhook 将回调商家。
    1. 商家开发者向我们注册 webhook 信息。
    2. 可靠且安全地向 webhook 发起 POST HTTP 请求。
  2. 高可用性、错误处理和故障恢复。
    1. 异步设计。假设商家的服务器分布在全球,可能会有高达 15 秒的延迟。
    2. 至少一次交付。幂等密钥。
    3. 顺序无关。
    4. 强大且可预测的重试和短路机制。
  3. 安全性、可观察性和可扩展性。
    1. 防伪造。
    2. 当商家的接收器出现故障时通知商家。
    3. 易于扩展和规模化。

2. 概述高层设计

异步设计 + 重试 + 排队 + 可观察性 + 安全性

3. 功能和组件

核心功能

  1. 用户访问仪表板前端向我们注册 webhook 信息 - 例如要调用的 URL、他们希望订阅的事件范围,然后从我们这里获取 API 密钥。
  2. 当有新事件时,将其发布到队列中,然后被调用者消费。调用者获取注册信息并向外部服务发起 HTTP 调用。

webhook 调用者

  1. 订阅由支付状态机或其他服务发布的支付成功事件队列。

  2. 一旦调用者接受事件,从用户设置服务获取 webhook URI、密钥和设置。根据这些设置准备请求。为了安全...

  • 所有来自用户设置的 webhook 必须使用 HTTPS。

  • 如果负载很大,预期延迟很高,并且我们希望确保目标接收器是活跃的,我们可以通过携带挑战的 ping 验证其存在。例如,Dropbox 通过发送带有“challenge”参数(一个随机字符串)的 GET 请求来验证 webhook 端点,您的端点需要将其回显作为响应。

  • 所有回调请求都带有头部 x-webhook-signature。这样接收者可以验证请求。

    • 对于对称签名,我们可以使用 HMAC/SHA256 签名。其值为 HMAC(webhook secret, raw request payload); Telegram 使用此方法。
    • 对于非对称签名,我们可以使用 RSA/SHA256 签名。其值为 RSA(webhook private key, raw request payload); Stripe 使用此方法。
    • 如果是敏感信息,我们还可以考虑对负载进行加密,而不仅仅是签名。
  1. 向外部商家的端点发起带有事件负载和安全头的 HTTP POST 请求。

API 定义

// POST https://example.com/webhook/
{
"id": 1,
"scheduled_for": "2017-01-31T20:50:02Z",
"event": {
"id": "24934862-d980-46cb-9402-43c81b0cdba6",
"resource": "event",
"type": "charge:created",
"api_version": "2018-03-22",
"created_at": "2017-01-31T20:49:02Z",
"data": {
"code": "66BEOV2A", // 或用户需要履行的订单 ID
"name": "主权个体",
"description": "掌握信息时代的过渡",
"hosted_url": "https://commerce.coinbase.com/charges/66BEOV2A",
"created_at": "2017-01-31T20:49:02Z",
"expires_at": "2017-01-31T21:49:02Z",
"metadata": {},
"pricing_type": "CNY",
"payments": [
// ...
],
"addresses": {
// ...
}
}
}
}

商家服务器应以 200 HTTP 状态码响应以确认收到 webhook。

错误处理

如果没有收到确认,我们将使用幂等性密钥和指数退避重试,最长可达三天。最大重试间隔为 1 小时。 如果达到某个限制,则短路/标记为损坏。向商家发送电子邮件。

指标

Webhook 调用者服务将状态发射到时序数据库以获取指标。

使用 Statsd + Influx DB 还是 Prometheus?

  • InfluxDB:应用程序将数据推送到 InfluxDB。它有一个用于指标和索引的单体数据库。
  • Prometheus:Prometheus 服务器定期从运行的应用程序中拉取指标值。它使用 LevelDB 进行索引,但每个指标存储在自己的文件中。

或者如果您有宽裕的预算,可以使用昂贵的 DataDog 或其他 APM 服务。

设计股票价格变动的智能通知

· 阅读需 18 分钟

需求

  • 300 万用户
  • 5000 支股票 + 250 支全球股票
  • 用户在以下情况下会收到价格变动通知:
    1. 订阅该股票
    2. 股票价格变动达到 5% 或 10%
    3. 自 a) 上周或 b) 昨天以来
  • 可扩展性。可能支持其他类型的通知,如突发新闻、财报电话会议等。

构建架构草图

上下文:

  • 什么是清算?清算是金融交易结算的程序——即资金及时准确地转移给卖方,证券转移给买方。通常,清算由一个称为清算所的专业组织作为中介。
  • 什么是证券交易所?一个股票经纪人和交易者可以买卖证券的场所。

苹果推送通知服务


(APNs)

苹果推送通知服务<br>(APNs)

Google Firebase 云消息传递


(FCM)

Google Firebase 云消息传递<br>(FCM)

电子邮件服务


AWS SES /sendgrid/etc

电子邮件服务<br>AWS SES /sendgrid/etc

通知者

通知者

外部供应商



市场价格

[不支持查看器]

Robinhood 应用

Robinhood 应用

API 网关

API 网关

反向代理

反向代理

批量写入

批量写入

价格


监测器

[不支持查看器]

时间序列数据库


influx 或 prometheus

时间序列数据库<br>influx 或 prometheus

每 5 分钟一次

[不支持查看器]

定期读取

定期读取

价格


监测器

价格<br>监测器

用户设置

用户设置

通知队列

通知队列

限流缓存

限流缓存

cronjob

cronjob

这些组件是什么,它们如何相互作用?

  • 价格监测器
    • 数据获取策略
      • 选项 1 初步:每 5 分钟获取一次数据,并批量写入时间序列数据库。
      • 选项 2 高级:如今外部系统通常直接推送数据,因此我们不必一直拉取。
    • 每次请求或每次价格变动约 6000 个点。
    • 数据保留 1 周,因为这只是 lambda 架构的加速层。
  • 价格观察者
    • 读取过去一周或过去 24 小时内每只股票的数据。
    • 计算波动是否超过 5% 或 10% 在这两个时间段内。我们得到的元组如 (股票, 上涨 5%, 1 周)。
      • 边缘情况:我们是否应该规范化价格数据?例如,某人错误地将 UBER 以 1 美元的价格出售。
    • 速率限制(因为 5% 或 10% 的变化可能在一天内发生多次),然后发出事件 PRICE_CHANGE(STOCK_CODE, timeSpan, percentage) 到通知队列。
  • 定期触发器是 cron 作业,例如 Airflow、Cadence。
  • 通知队列
    • 在用户和股票数量较少时,可能不一定会引入。
    • 可能接受通用消息事件,如 PRICE_CHANGEEARNINGS_CALLBREAKING_NEWS 等。
  • 通知者
    • 订阅通知队列以获取事件
    • 然后从用户设置服务中获取通知对象
    • 最后根据用户设置,通过 APNs、FCM 或 AWS SES 发送消息。

设计股票交易所

· 阅读需 20 分钟

需求

  • 买入卖出 订单的订单匹配系统。订单类型:
    • 市场订单
    • 限价订单
    • 止损订单
    • 完全成交或取消订单
    • 订单有效期
  • 为数百万用户提供高可用性和低延迟
    • 异步设计 - 广泛使用消息队列(顺便提一下,副作用:工程师在一个服务上发布到队列,不知道下游服务具体在哪里,因此无法做坏事。)

架构

反向代理

反向代理

API 网关

API 网关

订单匹配

订单匹配

用户存储

用户存储

结算

结算

订单

订单

股票元数据

股票元数据

认证

认证

缓存

缓存

余额与记账

余额与记账

外部定价

外部定价

清算


清算<br>所

银行,ACH,Visa 等

银行,ACH,Visa 等

支付

支付

审计与报告

审计与报告

组件及其相互作用

订单匹配系统

  • 按股票代码分片
  • 订单的基本数据模型(其他元数据省略):Order(id, stock, side, time, qty, price)
  • 订单簿的核心抽象是匹配算法。有许多匹配算法(参考 stackoverflow参考 medium
  • 示例 1:价格-时间 FIFO - 一种将二维向量投射或展平为一维向量的方式
    • x 轴是价格
    • y 轴是订单。价格/时间优先队列,FIFO。
      • 买方:按价格升序,按时间降序。
      • 卖方:按价格升序,按时间升序。
    • 换句话说
      • 买方:价格越高,订单越早,越接近匹配中心。
      • 卖方:价格越低,订单越早,越接近匹配中心。

x 轴

价格线

y 轴投射到 x 轴上

Id   Side    Time   Qty   Price   Qty    Time   Side
---+------+-------+-----+-------+-----+-------+------
#3 20.30 200 09:05 卖出
#1 20.30 100 09:01 卖出
#2 20.25 100 09:03 卖出
#5 买入 09:08 200 20.20
#4 买入 09:06 100 20.15
#6 买入 09:09 200 20.15

来自 Coinbase Pro 的订单簿

单一股票交易所模拟器

  • 示例 2:按比例分配

纯按比例分配

如何实现价格-时间 FIFO 匹配算法?

  • 按股票分片,CP 优于 AP:一股一个分区
  • 有状态的内存树图
    • 定期迭代树图以匹配订单
  • 使用 Cassandra 进行数据持久化
  • 订单匹配服务的进出请求通过消息队列进行
  • 故障转移
    • 内存树图快照到数据库
    • 在错误情况下,从快照恢复并与缓存去重

如何实时将订单簿数据传输到客户端?

  • websocket

如何支持不同类型的订单?

  • 在树图中使用相同的 卖出或买入:数量 @ 价格,但具有不同的创建设置和匹配条件
    • 市场订单:以最后市场价格下单。
    • 限价订单:以特定价格下单。
    • 止损订单:以特定价格下单,并在特定条件下匹配。
    • 完全成交或取消订单:以特定价格下单,但仅匹配一次。
    • 订单有效期:以特定价格下单,但仅在给定时间范围内匹配。

订单服务

  • 保留所有活动订单和订单历史。
  • 接收到新订单时写入订单匹配。
  • 接收匹配的订单并与外部清算所结算(异步外部网关调用 + 定时任务同步数据库)

参考文献

构架入门

· 阅读需 4 分钟

什么是构架

构架是软件系统的形状。拿建筑物来举例子:

  • 范式 paradigm 是砖块
  • 设计原则是房间
  • 组件是建筑

他们共同服务于一个特定的目的,就像医院治疗病人,学校教育学生一样。

我们为什么需要架构?

行为 vs. 结构

每一个软件系统提供两个不同的价值给利益相关者:行为与结构。软件开发者必须确保这两项价值都要高

==由于其工作的需要,软件架构师更多地聚焦于系统的结构而不是特性和功能。==

终极目标——==减少每加一个新特性所需要耗费的人力成本==

架构服务于软件系统的整个生命周期,使其易于理解,开发,测试,部署和操作。 其目标是最小化每个业务用例的人力资源成本。

O’Reilly 出版的《软件架构》一书很好地介绍了这样五种基本的构架。

1.分层架构

分层架构是被广泛采用,也是被开发者所熟知的一种架构。因此,它也是应用层面上事实上的标准。如果你不知道应该使用什么架构,用分层架构就是不错的选择。

示例

  • TCP/IP模式:应用层 > 运输层 > 网际层 > 网络接口层
  • Facebook TAO网络层 > 缓存层(follower + leader) > 数据库层

优缺点:

  • 优点
    • 易于使用
    • 职责划分
    • 可测试性
  • 缺点
    • 庞大而僵化
      • 想要对架构进行调整、扩展或者更新就必须要改变所有层,十分棘手

2.事件驱动架构

任何一个状态的改变都会向系统发出一个事件。系统组件之间的通信都是经由事件完成的。

一个简化的架构包含中介(mdiator),事件队列(event queue)和通道(channel)。下图所示即为简化的事件驱动架构:

示例

  • QT:信号(signals)和槽(slots)
  • 支付基础设施:由于银行网关通常有较高的延迟,因此银行的架构中采用了异步技术

3.微核架构(aka Plug-in Architecture)

软件的功能被分散到一个核心和多个插件中。核心仅仅含有最基本的功能。各个插件之间互相独立并实现共享借口以实现不同的目标。

示例

  • Visual Studio Code 和 Eclipse
  • MINIX 操作系统

4.微服务架构

大型系统被解离成众多微服务,每一个都是单独部署的单位,他们之间通过RPCs进行通信。

uber architecture

示例

5.基于空间的架构

“基于空间的架构”这一名称来源于“元组空间”,“元组空间“有”分布式共享空间“的含义。基于空间的架构中没有数据库或同步数据库访问,因此该架构没有数据库的瓶颈问题。所有处理单元共享内存中应用数据副本。这些处理单元都可以很弹性地启动和关闭。

示例:详见 Wikipedia

  • 主要被使用Java的架构所采用:例如:JavaSpaces

架构导论

· 阅读需 3 分钟

什么是架构?

架构是软件系统的形状。就好像是一个物理世界的建筑的全景:

  • 范式是砖块。
  • 设计原则是房间。
  • 组件是一幢建筑。

它们共同服务于特定的目的,例如医院是为治愈病人而设,学校是为教育学生而设。

我们为什么需要架构?

行为与结构

每个软件系统为利益相关者提供两种不同的价值:行为和结构。软件开发人员负责确保这两种价值都保持高水平。

==软件架构师由于其工作描述,更加关注系统的结构,而不是其特性和功能。==

终极目标 - ==每个功能节省人力资源成本==

架构为软件系统的整个生命周期提供服务,使其易于理解、开发、测试、部署和操作。目标是最小化每个业务用例的人力资源成本。

O’Reilly的书《软件架构模式》由Mark Richards撰写,是对这五种基本架构的简单而有效的介绍。

1. 分层架构

分层架构是最常见的架构,开发人员广泛熟知,因此是应用程序的事实标准。如果您不知道使用什么架构,请使用它。

示例

  • TCP / IP模型:应用层 > 传输层 > 网络层 > 网络接入层
  • Facebook TAO:网页层 > 缓存层(跟随者 + 领导者) > 数据库层

优缺点

  • 优点
    • 易于使用
    • 职责分离
    • 可测试性
  • 缺点
    • 单体
      • 难以调整、扩展或更新。您必须对所有层进行更改。

2. 事件驱动架构

状态变化将向系统发出事件。所有组件通过事件相互通信。

一个简单的项目可以结合中介、事件队列和通道。然后我们得到一个简化的架构:

示例

  • QT:信号和插槽
  • 支付基础设施:银行网关通常具有非常高的延迟,因此在其架构设计中采用异步技术。

3. 微内核架构(即插件架构)

软件的职责被划分为一个“核心”和多个“插件”。核心包含最基本的功能。插件彼此独立,并实现共享接口以实现不同的目标。

示例

  • Visual Studio Code,Eclipse
  • MINIX操作系统

4. 微服务架构

一个庞大的系统被解耦为多个微服务,每个微服务都是一个单独部署的单元,它们通过RPCs相互通信。

uber architecture

示例

5. 基于空间的架构

该模式得名于“元组空间”,意为“分布式共享内存”。没有数据库或同步数据库访问,因此没有数据库瓶颈。所有处理单元在内存中共享复制的应用数据。这些处理单元可以弹性启动和关闭。

示例:见维基百科

  • 主要在Java用户中采用:例如,JavaSpaces