21.1 Heroku:一个使用 Go 的高度可用一致数据存储
Heroku 是一家位于美国旧金山的硅谷公司,最近被 Salesforce.com 公司(它为 Ruby 和 Rails、Java、Clojure 和 node.js 应用程序提供强大的、可扩展的、特别是非常易于管理的云主机)收购。Heroku 的两位工程师,Keith Rarick 和 Blake Mizerany 设计了一个开源的 “分布式启动系统”,名为 Doozer,用于管理跨集群机器的进程,并从实例故障和网络故障中优雅地恢复分区。其中一个需求是,他们需要可靠地同步和在许多服务器之间共享信息。
系统中的每台服务器都需要有很多关于系统整体的信息(配置数据、锁 (lock) 等),以便能够进行协调,而且这些信息需要保持一致,即使在数据存储失败时也可以使用。因此他们需要一个具有坚实一致性保证的数据存储。为此,他们开发了 Doozer,一个用 Go 语言编写的、新的、一致的、高度可用的数据存储,并且仿照谷歌的(封闭源码)Chubby 程序来管理他们的后端基础设施。
Doozer 以 Paxos 为基础(Paxos 是一个由不可靠节点组成的的不可靠网络中解决共识问题的协议族),虽然 Paxos 对运行容错系统至关重要,但它因难以实现而臭名昭著。即使是在网上可以找到的实例实现也很复杂,很难遵循,尽管已经为了教育目的而被简化过。而现有的生产系统以更糟糕而闻名。
Doozer 是作为建立分布式系统的一个坚硬的基础而被开发的。
- 一个高度可用的(在网络分区期间工作)。
- 一致性(没有不一致的写入)。
- 数据存储(用于少量的数据)。
正如开发人员所说:
“Doozer 是你放置家族珠宝的地方”。
它提供了一个单一的基本同步元素:比较-设置对 (compare-config)。
用例:
- 数据库主选 (Databases master election)
- 命名服务
- 配置
为什么选择 Go,Go 的特点如何使其成为一个成功的产品:
Paxos 是以独立的、并发的进程来定义的,这些进程通过传递消息进行通信。这正是 Go 的并发原语(goroutines 和 channel,见 第 14 章)所擅长的问题。在 Doozer 中,这些进程被实现为 goroutines,他们的通信被实现为通道操作。就像 Go 的垃圾收集器将内存使用量降到最低一样,Doozer 的开发者发现 goroutines 和通道改进了基于锁的并发方法。这些工具让他们避免了复杂的“记账” (bookkeeping) 方式,并将注意力集中在手头的问题上。他们仍然惊讶于只用了几行代码就实现了以困难著称的东西。
Go 中的标准包是对于 Doozer 的另一个大成功,其中最值得一提的是 websocket
包。
下面是开发人员自己的一些使用后的感受:
“……例如,我们很快就发现
websocket
是一个有用的包。一旦我们有了一个工作的数据存储,我们就需要一个简单的方法来反省 (introspect) 它并将活动可视化。使用websocket
包,Keith 能够在回家的火车上添加 web 浏览器,而且不需要外部依赖。这就是 Go 将系统和应用编程完美结合的一个真实证明。“部署 Doozer 的过程简单得令人满意。Go 构建静态链接的二进制文件,这意味着 Doozer 没有外部依赖;它是一个单一的文件,可以复制到任何机器上,并立即启动,加入运行中的 Doozer 集群。
“最后,Go 对简单性和正交性 (orthogonality) 的狂热关注与我们对软件工程的看法是一致的。和 Go 团队一样,我们对 Doozer 的特性也是固执的 (pragmatic)。我们关注细节,更倾向于改变现有的功能而不是引入新的功能。在这个意义上,Go 是一个与 Doozer 的完美匹配。我们已经有了关于 Go的未来项目。Doozer 只是一个更大的系统的开始。”
他们还喜欢自动格式化工具 gofmt,以实现一致的代码风格和布局,从而避免了对这些话题的讨论。
其他语言也提供了一些类似的并发机制——比如 Erlang 和 Scala,但 Go 的设计也是为了提供最大的效率和控制。在另一篇文章中(引用 12)Keith Rarick 指出:
“Go 来自于 C 和 C++ 这样的系统编程语言,所以它让你有能力真正控制性能特性。当需要测量事物并确保其运行速度的时候,你有足够的灵活性来真正进入那里并做你需要的事情。当你发现你的程序运行缓慢的原因时,你就可以真正控制你所需要的东西来解决它。Go 给了你一个独特的组合:C 语言给了你控制权,但它并不适合于并发。它甚至没有给你提供垃圾收集。Go 为你提供了并发性和垃圾收集,但它仍然让你控制内存布局和资源使用。”
在 Doozer 中,Go 主要作为一种系统编程语言使用。更多的技术描述可以在(引用 38)找到;代码可在 https://github.com/ha/doozer 找到。