跳到主要内容

设计以人为本的国际化(i18n) 工程方案

· 阅读需 14 分钟

需求分析

如果问硅谷的公司跟中国最大的不同是什么,我想答案很可能正如吴军所言,硅谷公司的产品大多面向全球市场。陈志武说的好,创造财富能力有三个衡量维度:深度,即生产力,同样的时间提供更好产品或服务的能力;长度,即利用金融杠杆,跨越时间和空间交换价值的能力;广度,即市场大小,开创跨越地域的市场或者新行业的能力。而国际化,也就是产品和服务在语言和文化上的本地化,正是跨国公司征战全球市场的战略要地。

Internationalization 因为字母 i 后面跟了 18 个字母然后以 n 结尾,所以被称为 18n,我们这次设计的 i18n 工程方案主要是解决网站和移动 App 开发过程中的如下问题:

  1. 语言
  2. 时间与时区
  3. 数字与货币

构架设计

语言

逻辑和细节

语言的本质是把消息交付给受众的媒介,不同的语言就是不同的媒介,不同的媒介面向不同的受众。比如,我们要对用户显示文字:“你好,小丽!”,显示的过程就是查一下语言表,根据用户的语言,和当前需要的插值,比如姓名,显示相应的消息:

Message CodesLocalesTranslations
home.helloenHello, ${username}!
home.hellozh-CN你好, ${username}!
home.helloIW!${username}, שלום

不同语言在细节上略有不同,比如一个物品的单数和复数的形式;比如第三人称,在称呼上男性和女性的区别。

这些都是简单的查表无法应对的问题,需要更复杂的逻辑处理。在代码中你可以无脑地使用条件语句去处理这些特例。此外有一些国际化的框架会发明 DSL (domain specific language) 来专门应对这种情况。以 The project fluent 为例:

还有一个新手容易忽略的问题是行文的方向。中文和英文等常用语言是从左至右的,但是还有一些语言,是从右往左的,比如希伯来文和阿拉伯文。

行文方向的不同不仅仅会影响到文字本身,还会影响到输入的方式。中国人如果从右至左输入会觉得非常的奇怪;而我的一位犹太同事就觉得英文和犹太文混着输入轻而易举。

还有一种情况就是布局。整个 UI 的布局、视觉元素比如箭头的方向。都可能会根据语言的方向的不同而发生变化。你的 HTML 需要设置好相应的 dir 属性

如何确定用户的地域?

你可能会问,我们如何知道用户当前的语言设置呢?如果是浏览器的话。在用户请求网页的时候,会有一个 header Accept-Language 标注接受的语言。这些设置来自于用户的系统语言,以及浏览器的设置。移动 App 情况下,通常都会有获取 locale 变量或者常量的 API。还有一种方式是根据用户的IP 或者 GPS 信息知道用户的位置,然后显示相应的语言。如果是跨国公司,用户在注册的时候,通常会标注出用户注册时候的语言习惯、地理区域。

如果用户想要改换语言,网站的做法各有千秋,移动 App 会有相对固定的 API。网页有这样几种方法:

  1. 设置 locale cookie
  2. 使用不同的子域名
  3. 使用专有域名。 Pinterest 有一篇文专门讲他们如何用本地化的域名。 研究表明使用本地域名后缀的点击率会更高。
  4. 使用不同的路径
  5. 使用 query params。这个做法虽然能用,但是对 SEO 不友好。

新手在做网站的时候容易忘记在 HTML 上标注 lang 标签。

翻译管理系统

当你注意到如上种种细节。小心翼翼的实现了文字语言的显示之后。你会发现,翻译库的建立和管理,也是一个麻烦的过程。

通常开发者并不会有多语言的功底。这时候就需要引入外部的翻译官或者是别人已经建立好的翻译库。而这里的难点在于,翻译官往往并不是技术人员。如果让他们直接改代码、或者直接跟开发人员沟通,会极大的增加翻译的成成本。所以在硅谷的公司,这种提供给翻译官使用的翻译管理系统 (translation management system),往往是有一个团队专门来做,或者直接采购现有的方案,比如说,闭源收费的 lokalise.co ,或者是开源的 Mozilla Pontoon。翻译管理系统可以统一管理翻译库、项目、审核、任务分配。

这样一来,开发的流程就会变成,首先设计师根据不同的语言和文化习惯,在设计的时候标志出需要注意的地方,比如这个按钮虽然在英文里很短,但是在俄文里面会非常长,要注意不要溢出。然后,开发者开发团队根据设计的需求实现具体的代码逻辑,并在翻译管理系统中提供消息码、上下文的背景、以及一个开发者熟悉的语言写成的例子。再然后,翻译官团队在管理系统中填上各种语言的翻译。最后,开发团队把翻译库拉回代码库中,发布到产品中。

其中,上下文的背景是容易被忽视而且不容易做好的地方。这个需要翻译的消息在UI界面的什么地方?用作什么用途,如果消息过短的话,还应该进一步的解释这个消息是什么意思。那么翻译官有了这个背景知识之后,就能够更加精准地加上其他语言的翻译。如果翻译官对想要表达的信息。无法透彻的理解。他们还需要拥有一个提供反馈的渠道,能够找到产品的设计和开发者询问问题。

这么多的语言和文字,通常都不是由一个翻译官来解决的,这通常需要很多个国家语言身份的人一起来为这个翻译库添砖加瓦。整个过程耗时耗力,所以翻译官通常是有专门的团队来负责的,比如外包给 smartling。

现在我们已经有了代码逻辑和翻译库。接下来的问题是:如何把翻译库的内容搬到产品中?

具体可以有很多不同的实现方式,最直接的就是,静态的做法,每次更新的时候。交一个 diff,然后在 merge 到代码当中。这样在构建的时候,就会有就已经有了相关的翻译资料在代码里面。

还有一种做法是动态地做。一方面,可以去远程的翻译库“拉取”内容,这种情况在网站流量大的时候,可能会有性能问题。但是好处是,翻译永远是最新的。另一方面,想要做优化的话,可以采取“推送”的方式,每次翻译库有新改动,触发一个 webhook 来把内容推到服务器上。

在我看来,维护翻译会比添加翻译更加的繁琐。我曾经看到一些很大的项目,因为更新翻译之后没能够及时的删除老的翻译,导致翻译库过于的庞大,整个项目变得乱七八糟。这个时候如果有一个好的工具,能够保证数据的一致性。会对清洁的代码,有非常大的帮助。

阿里巴巴的 Kiwi 国际化全流程解决方案就做了 linter 和 VS Code 插件来帮助你检查和抽取代码中的翻译。

时间和时区

谈完了语言,接下来是时间和时区问题。因为是全球化的公司,所以说有很多数据是来自于全球、显示给全球的用户的。举个例子。国际航班在设置开始时间结束时间的时候如何保证这个时间在全局是一致的,并且在不同的时区会相应的显示。这非常的重要。同样的情况还应用于一切跟时间有关的事件,比如预定酒店、预定餐馆、安排会议。

首先时间有这样几种典型的表现形式。

  1. 自然语言,比如 07:23:01, 星期一 28, 十月 2019 CST AM/PM
  2. Unix timestamp (Int 类型),比如 1572218668
  3. Datetime. 注意 MySQL 存datetime 的时候会根据服务器时区转化成 UTC 然后存起来,读取的时候再转换回来。但是呢,服务器时区一般都是设置成 UTC 的。这种情况就是,存储不带时区,默认 UTC。
  4. ISO Date,比如 2019-10-27T23:24:28+00:00,这是带时区信息的。

我对这些形式没有大的偏好,你如果有相关经验,欢迎留言讨论。

具体在显示的时候。有两个可能出现的转化,一个是,从服务器存储的时区转化成当地时区显示的形式;另外一个是,语言上会由机器代码转换成自然语言。后一步流行的做法是使用强大的处理时间和日期的库,比如 moment.jsdayjs

数字与货币

不同国家区域对于数值的显示,其实是天差地别的。数值中间的逗号和点,在不同的国家,有不同的含义。

(1000.1).toLocaleString("en")
// => "1,000.1"
(1000.1).toLocaleString("de")
// => "1.000,1"
(1000.1).toLocaleString("ru")
// => "1 000,1"

阿拉伯数字并不是在所有区域都通用的,比如 Java 的 String.format 中 1、2、3这种数字在真正的阿拉伯语言里,使用的数字是١、٢、٣

价格方面,同样的货物,在不同的国家地区,是否要显示成当地货币的价值?货币的符号是什么?货币能够精确到哪一位?这些问题统统要先做好准备。

总结

本文提到的国际化工具有,翻译管理系统,开源的 Mozilla Pontoon、闭源收费的 lokalise.co,POEditor.com 等等。代码上的一致性 阿里巴巴 Kiwi 国际化全流程解决方案。UI 显示上的 moment.js, day.js

如同一切软件系统的开发一样,国际化这件事情没有银弹,好的作品都是靠基本功一点一滴磨出来的。

设计负载均衡器或 Dropbox 修补程序

· 阅读需 5 分钟

需求

互联网规模的网络服务处理来自全球的高流量。然而,单个服务器在同一时间只能处理有限数量的请求。因此,通常会有一个服务器集群或大型服务器集群来共同承担流量。问题来了:如何路由这些请求,以便每个主机能够均匀地接收和处理请求?

由于从用户到服务器之间有许多跳数和负载均衡器层,因此这次我们的设计要求是

注意:如果服务 A 依赖于(或消费)服务 B,则 A 是 B 的下游服务,而 B 是 A 的上游服务。

挑战

为什么负载均衡很难?答案是很难收集准确的负载分布统计数据并相应地采取行动。

按请求分配 ≠ 按负载分配

随机和轮询通过请求分配流量。然而,实际负载并不是每个请求 - 有些在 CPU 或线程利用率上很重,而有些则很轻。

为了更准确地评估负载,负载均衡器必须维护每个后端服务器的观察到的活动请求数量、连接数量或请求处理延迟的本地状态。基于这些信息,我们可以使用诸如最少连接、最少时间和随机 N 选择等分配算法:

最少连接:请求被传递给活动连接数最少的服务器。

基于延迟(最少时间):请求被传递给平均响应时间最少和活动连接数最少的服务器,同时考虑服务器的权重。

然而,这两种算法仅在只有一个负载均衡器的情况下效果良好。如果有多个负载均衡器,可能会出现 羊群效应。也就是说,所有负载均衡器都注意到某个服务瞬时更快,然后都向该服务发送请求。

随机 N 选择(在大多数情况下 N=2 / 也称为 二选一的力量):随机选择两个并选择两个中的更好选项,避免选择更差的选项

分布式环境

本地负载均衡器对全局下游和上游状态并不知情,包括

  • 上游服务负载
  • 上游服务可能非常庞大,因此很难选择正确的子集来覆盖负载均衡器
  • 下游服务负载
  • 各种请求的处理时间很难预测

解决方案

有三种选项可以准确收集负载统计数据,然后采取相应的行动:

  • 集中式和动态控制器
  • 分布式但具有共享状态
  • 在响应消息或主动探测中附加服务器端信息

Dropbox Bandaid 团队选择了第三种选项,因为它很好地适应了他们现有的 随机 N 选择 方法。

然而,他们并没有像原始的 随机 N 选择 那样使用本地状态,而是通过响应头使用来自后端服务器的实时全局信息。

服务器利用率:后端服务器配置了最大容量并计算正在进行的请求,然后计算利用率百分比,范围从 0.0 到 1.0。

需要考虑两个问题:

  1. 处理 HTTP 错误:如果服务器快速失败请求,它会吸引更多流量并导致更多失败。
  2. 统计衰减:如果服务器的负载过高,则不会将请求分配到该服务器,因此服务器会被卡住。他们使用反向 sigmoid 曲线的衰减函数来解决此问题。

结果:请求更加均衡

设计负载均衡器

· 阅读需 5 分钟

需求分析

互联网服务往往要处理来自全世界的流量,但是,一个服务器只能够同时服务有限数量的请求。因此,通常我们会有一个服务器集群来共同处理这些流量。那么问题来了,怎样才能够让这些流量均匀地分布到不同的服务器上呢?

从用户到服务器,会经过很多的节点和不同层级的负载均衡器。具体来讲,我们这次设计的需求是:

  • 设计第7层的负载均衡器,位于数据中心的内部。
  • 利用来自后端实时的负载信息。
  • 服务每秒千万级的流量以及10 TB每秒级别的吞吐量。

补充:如果服务 A 依赖服务 B,那我们称 A 是 B 的下游服务,而 B 是 A 的上游服务。

挑战

为什么负载均衡会很难做?答案是很难收集准确的负载分布数据。

按照数量分布 ≠ 按照负载分布

最简单的做法是根据请求的数量,随机地或者循环地分布流量。然而,实际的负载并不是根据请求的数量来算的,比如有些请求很重很耗CPU,有些请求很轻量级。

为了更加准确地衡量负载,负载均衡器得保持一些本地状态 —— 比如,存当前的请求数、连接数、请求处理的延迟。基于这些状态,我们能够使用相应的负载均衡的算法 —— 最少连接、最少延迟、随机 N 取一。

最少连接:请求会被导向当前连接数最小的服务器。

最少延迟:请求会被导向最少平均反应时长且最少连接数的服务器。还可以给服务器加权重。

随机 N 取一 (N 通常是 2,所以我们也可以称之为二选一的力量):随机的选两个服务器,取两者之中最好的,能够避免最坏的情况。

分布式的环境

在分布式的环境中,本地的负载均衡器难移了解上下游服务完整的状态,包括

  • 上游服务的负载
  • 上游服务可能超级大,因此很难选择一个合适的子集接入负载均衡器
  • 下游服务的负载
  • 不同种类的请求的具体处理时间很难预测

解决方案

有三种方案能够准确地搜集负载的具体情况并相应地处理:

  • 中心化的一个均衡器,根据情况动态地处理
  • 分布式但是各个均衡器之间要共享状态
  • 服务器返回请求的时候捎带上负载信息,或者是均衡器主动询问服务器

Dropbox 在做 Bandai 的时候选择了第三种方案,因为这很好地适应了现行的随机 N 选一的算法。

然而,与原配的随机 N 选一的算法所不同的是,不是使用本地的状态,而是选择服务器实时返回的结果。

服务器使用率:后端服务器设置了最大负载,数当前的连接,然后计算出使用率,范围是从 0.0 到 1.0.

有两个问题需要考虑:

  1. 处理错误: 如果 fail fast ,由于处理得很快,反而会吸引更多的流量产生更多的错误。
  2. 数据要衰减: 如果服务器的负载太高,没有请求会发到那里。因此,使用一个类似于反 S 曲线的衰减函数来保证老数据会被清理掉。

结果: 服务器接收的请求更加的均衡了

并发模型

· 阅读需 1 分钟

  • 单线程 - 回调、承诺、可观察对象和 async/await:原生 JS
  • 线程/多处理,基于锁的并发
    • 保护临界区与性能
  • 通信顺序进程 (CSP)
    • Golang 或 Clojure 的 core.async
    • 进程/线程通过通道传递数据。
  • 演员模型 (AM):Elixir、Erlang、Scala
    • 本质上是异步的,并且具有跨运行时和机器的位置信息透明性 - 如果您有演员的引用 (Akka) 或 PID (Erlang),您可以通过邮箱向其发送消息。
    • 通过将演员组织成监督层次结构来实现强大的容错能力,您可以在其确切的层次结构级别处理故障。
  • 软件事务内存 (STM):Clojure、Haskell
    • 类似于 MVCC 或纯函数:提交 / 中止 / 重试

世界不是平的 —— 经济学人特别报道:全球供应链

· 阅读需 8 分钟

影响供应链的两种力量:科技与政治

有两种全球化,一种是工业革命带来的大发散。一种是信息革命带来的大收敛,发达国家提供资本和高科技、去工业化,欠发达国家提供廉价劳动力、工业化。后一种全球化造就了供应链的黄金时代,但是它对世界的影响更加地突然而且难以控制。

而现在的我们处于后一种全球化的末尾,增长变慢,经济学人发明了一个词叫全球化趋势放缓 slowbalisation,比如英国脱欧、中美贸易战。

技术上的, AI、机器人、3D 打印、无人车、5G 等让供应链面对前所未有的大变革,这些变革让全球化更可控。然而,这种变革却受到了技术冷战的阻碍。

产业的变化

超过半数的公司认为应该在他们的公司引入供应链方面的 major change,十分之一的企业认为应该彻底推倒重来。这里有两个主要的考虑点:

一个是降低供应链成本带来的风险 - 你只是处在供应链网络的一部分节点上,难移控制网络上相邻节点的相邻节点。11年的日本台风,让一个半导体巨头花了100个 executive 超过一年的时间搞清楚他的供应链的拓展出来的网络具体有哪些公司。

一个是全球贸易不仅是带来了更多的产品,还带来了更多的服务。服务占贸易产值的三分之一,而且服务的增长比产品快 60%,在电信和信息产业能够快到 2 到 3 倍。

具体从三个产业看:

  1. 服装:有一些从中国移向东南亚、越南、孟加拉国、埃塞俄比亚。中国尽管成本有所提高但是熟练工仍然非常有竞争力。

  2. 汽车:区域化和枢纽化:墨西哥辐射北美、东欧和摩洛哥辐射西欧、东南亚和中国辐射亚洲。区域化的一个主要原因是消费习惯的不同,比如皮卡只有在美国很畅销;其他原因还有比如贸易战、汽车的电气化(部件要少很多很多)。

  3. 计算机:有一些公司搬离中国去越南、柬埔寨和墨西哥,甚至搬回美国。有很多公司发现搬离美国很难。深圳还是最牛逼,因为其自动化的能力和高附加值。

亚马逊和阿里巴巴仍然是下一场变革的标杆

亚马逊用数据、算法、机器人做到物流比大部分的竞争对手还要快三分之一。

数字化:新技术如何改造老行业?

  1. 可预测性
    1. 公司有用历史记录预测销售的传统,并相应调整制造和库存等等。AI 能够根据社交媒体用更多的数据、更精准地调和供应链上每个节点的参数。
    2. 调研发现,公司高管认为最应采用的技术是 congnitive analytics 和 AI ,区块链和无人机的排名有所下降。
    3. JDA 用 Blue Yonder 的深度学习的算法,让售空下架的情况减少了 30%,压货时间减少好几天。
    4. ORSAY 用 JDA 的自动定价系统来减少库存。
    5. 英特尔用预测模型省钱省了 $58m
  2. 透明性
    1. 对跨国企业而言,“东西在哪里?”仍然是一个巨大的未解难题。为什么?因为他们对于产品的控制不是最最直接的,他们不造、不运、不存、不卖产品。
      1. 举个例子,17 年德国货运业罢工,把 IBM 昂贵的主机晾在了停机坪上的冰雪里长达一个月,而因为无法精确定位货物,公司还以为被安全地存放在机场的仓库里。
    2. 物联网能够解决这个问题。传感器不仅能够汇报位置,还有方向、温度、适度等等参数。
    3. 新加坡用无人车自动化港口运输。
    4. IBM 和 Maersk 用区块链让运输无纸化和透明化,
  3. 速度
    1. 在硅谷 Tom Linton 的 Pulse command centre,看上去像是五角大楼的指挥中心,能够实时看到供应链上的92个变量,与客户通过手机和电脑共享数据和决策,让库存减少 11 天,释放出 $580m 的现金流。
    2. Li & Fung 让产品设计到上架,从原来的 40 周,减少到了现在的一半。
    3. 3D 打印独角兽 Carbon 帮助福特和阿迪达斯开发和生产产品。
    4. Uber or Airbnb for warehousing

安全上的挑战

供应链管理正在经历从直觉和经验驱动过渡到数据驱动,主要有三大安全方面的挑战:

  1. 华为。替换掉华为的产品会提高成本,而 5G 采取不同的不兼容的技术标准会逼着不同国家站队,而 5G 技术对 IoT 至关重要 。
  2. 黑客攻击。新式的 IoT 设备急于投入使用,没有完备地考虑安全,然而“没有人会愿意开着坦克上班”,因为这实在是太慢了。比较好的应对方法是见招拆招、快速反应。
  3. 贸易战。政策可以一天一个样子,但是工厂可不是想搬就搬。

设计实时联想搜索或自动完成功能

· 阅读需 3 分钟

需求

  • 为社交网络(如 Linkedin 或 Facebook)提供实时 / 低延迟的联想和自动完成功能
  • 使用前缀搜索社交资料
  • 新添加的账户在搜索范围内即时出现
  • 不是用于“查询自动完成”(如 Google 搜索框下拉),而是用于显示实际的搜索结果,包括
    • 通用联想:来自全球排名方案(如人气)的网络无关结果。
    • 网络联想:来自用户的第一和第二度网络连接的结果,以及“你可能认识的人”评分。

Linkedin 搜索

架构

多层架构

  • 浏览器缓存
  • 网络层
  • 结果聚合器
  • 各种联想后端

Cleo 架构

结果聚合器

这个问题的抽象是通过前缀和术语在大量元素中查找文档。解决方案利用这四种主要数据结构:

  1. InvertedIndex<前缀或术语, 文档>:给定任何前缀,找到所有包含该前缀的文档 ID。
  2. 为每个文档准备一个 BloomFilter<前缀或术语>:随着用户输入的增加,我们可以通过检查它们的布隆过滤器快速过滤掉不包含最新前缀或术语的文档。
  3. ForwardIndex<文档, 前缀或术语>:之前的布隆过滤器可能会返回假阳性,现在我们查询实际文档以拒绝它们。
  4. scorer(文档):相关性:每个分区返回所有真实命中及其评分。然后我们进行聚合和排名。

性能

  • 通用联想:延迟 < = 1 毫秒在一个集群内
  • 网络联想(第一和第二度网络的超大数据集):延迟 < = 15 毫秒
  • 聚合器:延迟 < = 25 毫秒

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

· 阅读需 5 分钟

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

具体来说,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 或酒店预订系统

· 阅读需 4 分钟

需求

  • 对于客人
    • 按位置、日期、房间数量和客人数量搜索房间
    • 获取房间详情(如图片、名称、评论、地址等)和价格
    • 按日期和房间 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

· 阅读需 4 分钟

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

具体来讲,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。