欢迎来到我们关于公司在生产中使用Elixir的系列案例研究。请看我们迄今为止发布的所有案例。
Slab是一个知识库和团队维基,使知识民主化。Jason Chen在2016年8月创办了Slab,在挑选了Elixir和Phoenix作为构建实时协作应用程序的最佳工具之后。从那时起,公司已经发展到6名工程师,分布在世界各地,被7000多家公司和Asana、Discord和Glossier等客户所依赖。如果你有兴趣帮助公司成为学习和目标的来源,特别是在这个远程协作至关重要的时代,Slab正在招聘。
为什么是Elixir?
Slab并不是Jason第一次编写协作式网络应用程序。他之前有在Rails和Node.js中构建它们的实践,他认为在开发经验上有很多需要改进的地方,特别是在使用WebSockets时。这两种技术在生产中也很麻烦,因为团队面临着纵向和横向扩展的问题。
我希望有一个与Django和Rails一样的开发者体验的框架,但它是为实时应用设计的。
- 首席执行官Jason Chen,关于Phoenix网络框架
Jason不认为自己是一个总是在寻找新事物的人,但他知道在创办Slab时,他必须调查周围的选择。在这期间,他主要探索了两种语言。Go和Elixir。最后,Jason选择了Elixir,这得益于Phoenix网络框架。"我一直在寻找一个能提供完整的工具集来构建网络应用的框架。我对低级别的选择不感兴趣,比如使用哪种ORM,选择哪种库来解析请求等等。我想要一个与Django和Rails具有相同开发经验的框架,但它是为实时应用设计的。
Jason给自己两周的时间来建立一个概念验证。他写了一个协作博客,多个用户可以同时写一篇文章,评论也是实时添加的--所有这些都是在学习Elixir和Phoenix框架的过程中进行的。
试验比预期的要好,Jason的Slab之旅正式开始。
与平台一起成长
不久之后,Slab就进入了私人测试阶段,有一些公司成为了用户。对于他们一路走来的每一个主要功能,Elixir和Phoenix都提供了相应的构建模块。当他们实现实时评论时,他们使用了Phoenix Channels和Phoenix PubSub。这个模式继续下去。"对于异步处理,我们只是使用Elixir任务"。后来,为了跟踪编辑文档的用户,并给每个人不同的光标颜色,他们使用了Phoenix Presence,这是其他网络框架没有提供的开箱即用工具。
在Jason使用Slab和Elixir的过程中,另一个飞跃是他不得不学习Erlang/OTP,这是一组行为,作为Erlang标准库的一部分,用于构建分布式和容错的应用程序。
为了改进作为Slab一部分的实时协作编辑器,Jason实施了操作转换。客户端在浏览器中运行,用React实现。当用户对文本进行修改时,他们的差异被发送到服务器上,服务器对这些更新进行仲裁,并在不同的客户端之间进行同步。
解决同步问题并非易事,尤其是当应用程序在多个节点上运行时。以下是他们面临的挑战。想象一下,用户Alice有一个连接到X节点的WebSocket,用户Bob连接到Y节点,Alice和Bob都在处理同一个文本。Slab如何保证两个用户的修改都能被应用,从而在完成编辑后都能看到同一个文档?
我们可以尝试通过保持服务器的无状态来解决这个问题。每次服务器收到来自客户端的差异,服务器就会从数据库中读取文档,应用变化,将结果规范化,然后广播给客户端的更新。这种方法的问题是,在每次客户端更新时,从数据库中加载文本会很快变得昂贵,尤其是当它们的大小增加时。响应时间会变得更高,用户体验会下降。
当使用Node.js工作时,Jason尝试了一种不同的方法。如果Alice和Bob在写同一个文档,负载均衡器将保证两者被路由到同一个节点。在尝试了Apache和Nginx之后,他在Node.js中实现了平衡器。整个解决方案需要耗费大量的时间,并且引入了复杂的操作。
幸运的是,这些问题是Erlang/OTP的面包和黄油。Jason知道他需要一个有状态的抽象来保持服务器上的这种状态。他已经听说了该平台提供的选项,但他不确定该选哪一个。杰森回忆说。"我记得我曾问过社区,我应该使用代理还是GenServer,每个人都非常乐意提供指导。"他们很快就选择了GenServer作为他们的工具。
默认情况下,GenServer和Agent都是每个节点的本地设备。然而,他们也支持:global 选项,该选项在整个集群中注册一个给定的名称。为了使用这个选项,他们需要Erlang分布,他们已经在使用Phoenix PubSub和Presence,所以这是一个简单的改变。这保证了Alice和Bob都能与同一个GenServer对话,无论他们是加入X节点还是Y节点。
后来,在生产中运行该系统时,该平台继续给他留下深刻印象。每次他们增加机器资源的时候,都能看到运行时有效地利用它的一切可用资源,而不需要对代码进行修改。
学习和工具
在Slab的堆栈中还有其他一些值得注意的工具。
早在2017年,他们就迁移到了由Elixir的Absinthe驱动的GraphQL。对于采用查询语言有一些担忧,因为它是一个相对较新的技术。然而,他们认为这将解决一个真正的问题:他们的应用程序中有不同的组件需要不同的数据,而管理所有这些可能的组合正变得复杂。这是GraphQL被设计用来解决的主要问题之一。
他们也在谷歌云上运行,有Kubernetes(K8s),和许多Elixir工程师一样,他们想知道Erlang虚拟机如何适应有Docker和K8s的世界。今天,他们在6个节点上运行,其中5个运行应用程序代码。第六个处理cron作业,并为新的部署保持待命状态。他们使用peerage库在各节点之间建立分布式Erlang连接。
我们真的很看重Elixir使用较少的活动部件来构建复杂系统的能力。代码更简单,系统也更容易操作。
- Sheharyar Naseer,工程师
总的来说,Slab团队的目标是保持较低的依赖性,他们认为这是平台所能做到的,并对新开发者的入职产生了积极的影响。他们工程团队的成员Sheharyar Naseer解释说。"我们真的很看重Elixir使用较少的活动部件建立复杂系统的能力。代码更简单,系统更容易操作,使有经验的和新的工程师都更有效率。我们在生产中运行了3年多,没有使用Redis。我们最近才加入Redis,因为我们希望我们的缓存能够在不同的部署中存活。许多其他堆栈从第一天起就强加了Redis这样的技术。"
这种方法在更新库时也会产生好处。Sheharyar继续说。"在大多数情况下,升级Erlang、Elixir和Phoenix是很简单的。我们通过CHANGELOG,它总是强调我们需要执行的主要变化,并且我们在一两个小时后就准备好了拉动请求。唯一一次我们不能立即升级的是当Erlang/OTP删除旧的SSL密码时,这破坏了我们的HTTP客户端,我们在开发过程中及早发现了它。"
在工程师入职时,Slab向他们推荐了不同的书籍和视频课程--其中许多可以在我们的学习资源页面中找到--因此他们可以灵活地选择一个他们最有成效的媒介。新工程师也在Slab上工作,并通过拉动请求获得指导。他们从小任务开始,通常是在客户端和GraphQL层,然后慢慢解决围绕数据库和Erlang/OTP的更复杂的问题。如果你对改善远程协作感兴趣,请在他们的网站上了解更多关于他们的机会。