《elixirinaction》书评及作者问答录(作者sergiodesimone,译者邵思华发布于9月29日)

author author     2022-09-27     328

关键词:

《Elixir in Action》是由Manning所出版的一本新书,本书为读者介绍了Elixir这门语言以及Erlang虚拟机,同时也讨论了与并发编程、容错以及与高可用性相关的话题。InfoQ有幸与本书的作者Sa?a Juri?进行了一次访谈。

《Elixir in Action》的内容源自于Juri?在Erlang方面的经验,他为此特意创建了一个博客,为来自面向对象背景的程序员展现Erlang的优势。Juri?之后转而使用Elixir,这是一种函数式的并发编程语言,它的目标是提供一种比起以Prolog为启发创建的Erlang更简便的语法,以及更高等的抽象能力。

本书的内容采取了一种渐进式的方式,首先介绍了Elixir的语法与它的基本特性,例如宏、模式匹配、模块、多态等等,随后介绍了如何创建一个容错的、高可用的、并发的分布式系统。在全书的一些核心章节中涵盖了Erlang平台的大量知识,其中的主题包括管理进程、持久化、通过supervision树进行运行时错误处理的多种方式,以及“任其崩溃”(let it crash)等设计哲学。

 

总的来说,《Elixir in Action》一书不仅为如何使用Elixir语言与Erlang VM,同时也为读者如何迈进高可用性系统这一领域打下了坚实的基础。InfoQ与Sa?a Juri?进行了一次访谈,以了解这本书背后的更多知识。

InfoQ:是什么原因促使你编写了本书,它与现有的一些针对Elixir的书籍又有何不同之处呢?

我相信,通过一种更为专注的方式,初学者的学习过程将能够得到极大的简化。因此,我并不打算编写一本完整的参考书,而是决定专注于那些对于多数目标读者来说有些不寻常的核心概念:函数式编程、并发,以及OTP框架的核心思想。我相信一旦读者开始对这些主题感觉到“心意相通”,他们在学习本书并未叙述的一些内容时就会更容易。举例来说,如果读者掌握了Erlang中的并发知识,并且理解了大多数最重要的OTP概念(GenServer、Supervisor、Application),那么他们就能够较容易地自行学习其它的抽象概念,例如Task、Agent或GenEvent。

我相信,这是目前唯一一本以这种方式进行撰写的Elixir书籍。任何一位想要使用Elixir/Erlang技术打造可伸缩的、高容错性的分布式系统,都必须学习Elixir in Action一书中所介绍的各种材料。当然,你也可以通过其它资源学习这些内容,但我认为,本书是目前唯一一本面向Elixir介绍以上所有主题的书籍。

InfoQ:你能否简单地介绍一下你在Elixir方面的经验?它对于你实现工作目标提供了哪些程度的帮助?

Sa?a:对我来说,Elixir最重要的一方面实际上是Erlang VM,这是一切功能的基础,这也是我首次接触Erlang时对于我帮助最大的内容。大约5年以前,我当时需要实现一个基于长轮询的服务器推,它需要不断地将频繁变化的数据传输给数以千计的连接用户。经过一段时间的评估之后,我们最终选择了Erlang,我从中也收获了许多经验。Erlang以一种结构化的方式帮助我克服了重重阻碍:使用它创建解决方案的过程非常顺利,即使我对它的开发平台还有些陌生。最终设计出的系统具备了高伸缩性、高效性以及灵活性。我能强烈地感觉到Erlang在支持着我,即使我犯下了各种错误。这套系统能够处理各种预料之外的状况,而我甚至还没有在这方面做过什么计划。

InfoQ:Elixir特别适合于哪些类型的项目?

Sa?a:在我看来,Elixir/Erlang适合于任何类型的服务端系统:即需要持续运行,并且尽可能持续提供服务的软件。这方面一个很明显的示例就是基于web的系统,用于处理传入的HTTP请求,但也需要进行其它活动,例如后台的、周期性的作业或缓存管理。在这种系统中,许多活动在某个具体时间往往处于挂起状态。而Erlang应对这种并发情况的途径让开发者的担子轻了许多。如果每个独立的活动都能够分配至少一个独立的Erlang进程(不要与OS的进程混淆了),我们就不仅能够实现可伸缩性,还能够改善容错性:一个单一的进程故障不会对整个系统的绝大部分产生影响。并且还有许多方法能够检测到这种故障,然后从故障中恢复。

我发现这种处理方式非常直观,并且任意一种类型的后端系统都能够从中受益。我看到某些观点说Erlang只适合于大规模系统,或某些特定的领域,例如电信领域。我不同意这种观点。Erlang能够帮助系统实现大规模化,而这种特质在任何类型的生产系统中都是必要的,无论其规模与领域如何。因为如果某个系统做不到高可用 ,那么它就会频繁地产生故障。即使你不需要实现传说中的9个9的可用性,但你也应当希望让你的系统停机时间尽量减短吧。实现这一点是一个艰难的挑战,而Erlang则能够帮助你实现这个目标。

InfoQ:你怎样描述Elixir与Erlang两者之间的关系呢?

Sa?a:我的看法是,Elixir是在Erlang与OTP所提供的强大基础之上所扩展的能力,旨在提升开发者的生产力。我曾经进行过大量的全职Erlang开发工作,虽然我十分热爱这门语言,但有许多任务也显得过于繁琐,我不得不无谓地处理一些低层次的机械性工作。而Elixir在这方面提供了大量实用的特性,包括语言(例如通过宏进行元编程,以及通过协议实现多态)与工具(例如构建项目的多种工具搭配,以及十六进制包管理器)两方面,这让我们能够专注于处理一些更为实际的问题。

我个人的感受是使用Elixir进行开发比起使用纯粹的Erlang进行开发要简单许多。Elixir使可伸缩性与开发者的生产力之间这种刻意的权衡大大降低了,甚至是完全消除了。仅仅因为某个开发平台允许我们创建高并发、可伸缩、高容错的分布式系统,并不代表它就应当难以使用。同时,Elixir的运行时并没有彻底远离Erlang的哲学。作为一种函数式语言,它的语义与Erlang非常接近。Elixir能够无缝地集成各种Erlang库,因此开发者能够完整地访问整个Erlang生态系统。

InfoQ:在决定直接使用Erlang或Elixir时,这两者有哪些缺陷是开发者需要考虑进去的?

Sa?a:我不认为它们有任何的缺陷。它们所具有的优势主要来自于VM本身,以及已经过充分测试的OTP框架,而你可以从其中任何一门语言中收获这些益处。因此主要的决定因素在于其它的一些附加价值。Elixir加入了一些额外的特性,因此实际上它是一门比起Erlang更加复杂的语言。而这门语言的优势在于它的代码更加简洁,在样板代码方面的负担较少。与之相比,Erlang是一门更为简单的语言,因此所涉及的代码更多,但它也因而显得更为明确。

从我个人来看,Elixir在样板代码的减少与语言的明确性方面找到了一个很好的平衡点。它没有Ruby等语言表面上那么神奇,而仍然提供了各种实用的特性,其中最值得留意的就是元编程与多态性。

InfoQ:创建一个高可用、高容错的并发系统是一项复杂的任务,尤其是从CAP定律的角度来看。要实现这一目标,选择一门合适的语言与运行时环境的重要性有多高?

Sa?a:这实际上取决于个人的观点。人们在各种语言上都实现过大规模的系统,因此不用Erlang也是完全可能的。不过对我来说,问题不仅仅在于是否可能,还在于某个工具能够在多大程度上帮助我们完成这一过程。毕竟,工具的目的就是为我们提供服务。

而这也是为什么我很看重Erlang的一个原因。在我看来,它为系统化地处理编写高可用系统所面临的挑战提供了简单而又非常强大的构建块。它的主要工具是Erlang进程,它让我们能够将工作分解为几千个,乃至上百万个独立的部分。通过使用多个进程,我们就获得了可伸缩性与容错性。它的崩溃传递机制能够让我们有机会处理这些故障:如果某个部分崩溃了,系统中的其它部分会收到它的通知,并进行相应的处理。最后,无共享并发机制能够实现分布式系统,即使在一台独立的机器上也不例外。其实在本质上,通过将整个工作分解为大量隔离的、完全独立的实体(进程),我们已经实现了分布式工作。当然,将系统在多台机器上实现集群仍然不是一件简单的事,毕竟分布式系统在本质上就存在着复杂性。但至少Erlang已经为我们解决了一些低层次的工作,我们可以始终利用相同的基元功能实现协作,即进程与消息传递。因此我们就能够专注于业务上内在的挑战,而不是将大量的精力消耗在低层次的细节上。

总的来说,我认为Erlang能够简化实现高可用性的挑战。你也可以使用Erlang以外的技术应对这些挑战,但很可能会因此付出更多的努力。

InfoQ:Erlang的一个核心思想是使用非常轻量级的进程模型,这使得上下文切换的开销非常低。另一方面,在许多系统中仍然用线程处理可伸缩性,而线程的可伸缩性往往会成为系统的瓶颈。为了避免这种瓶颈,可以使用一个完整的异步模型配合一个小型的线程池,这种做法也有成功案例(这里有一个参考示例)。你能否详细地分析一下Erlang的处理方式与完整的异步方法相比所具有的优势?

Sa?a:我认为Erlang为我们创建高并发的系统提供了一种优秀而整洁的抽象,而任何一种需要持续处理各种不同任务的系统在本质上都是并发的。Erlang的处理方式非常适合于这种类型的问题,你总是可以通过进程来应对各种类型的任务,无论是I/O密集型还是CPU密集型任务,并且你可以信任VM会高效地分派工作。在使用Erlang不太会出现许多顿悟的情况,也不太会搬起石头砸了自己的脚。它能够让减轻我们的负担,让我们专注于实际的业务问题。

反之,如果你打算自行设计一种线程池,那么就不得不自己处理许多问题。打个比方,如果你在某个线程中执行一个时间很长的计算,那么你会阻塞在同一个线程上挂起的其它活动。如果某个线程因为一个bug而产生故障,该线程上运行的所有活动都会失败。这一点当然是能够解决的,但你或许要为投入大量的时间,以实现一种类似于Erlang VM的功能。既然如此,为什么不依赖于某个已被证实的解决方案呢?如果单纯的处理速度或是内存占用确实极端重要,那么采取自定义的实现方案可能还有一些益处,但在我所遇到的这些情形中来看,基本都不属于这种情况。

InfoQ:Erlang的另一个基本原则也被Elixir保留下来了,那就是“任其崩溃”。这种做法目前已经演变为将例行公事般地干掉进程作为一种确保系统能够容忍这一事件的手段了。这种策略对于打造一个具备容错性的Erlang/Elixir系统有多大的重要性?

Sa?a:Erlang设计的一个前提就是在生产环境中的系统有可能产生错误,但系统作为一个整体不能够中止:它应当尽力保留所有的服务,并尽快地从故障中进行自我修复。

任其崩溃在这种场合扮演了一个核心角色,它是一种简单的技术,能够让我们以一种有条不紊的方式处理系统的错误。在这种情形下,我们会选择让进程崩溃,并依靠Supervisor修复问题。这种做法的好处是该进程的主体代码可以不必操心错误处理的问题,例如编写try-catch或“if err != nil”这样的代码块,而只关注主路径上的逻辑。我们甚至还可以通过模式匹配的方式优雅地对各种期望进行断言。

在我看来,这种方式比try-catch-ignore的做法更优秀,因为一旦进程中止,它的状态也就消失了。而问题的根源很可能来自于有问题的状态。在进程重启之后,新的进程会生成全新的、稳定的状态,因此进程能够再次运转。至少它可以稳定地运行一段时间,直到状态再次出现问题为止。这种做法能够让系统中有问题的地方浮现出来,多数服务在这种方式下都会表现出偶尔的故障现象,直至问题的根源解决为止。

与任其崩溃相辅相成的一点是通过Supervisor进行恢复。如果你建立了一个细粒度的supervision树,那么所需重启的部分也相对较少。一旦产生故障,你可以试着重启系统中的一小部分如果问题仍然没有解决,你可以逐渐增加这部分的区域,直到系统中有问题的那部分被重启为止。相反,如果你采用了try-catch-ignore方式,就有可能导致错误的状态始终延续,最终产生了一个永无休止的故障循环。

InfoQ:“任其崩溃”是仅属于Erlang的一种独特功能吗?可否将其移植至其它不使用Erlang VM的环境中呢?

Sa?a:问得好!首先我要强调一点,OTP是用纯粹的Erlang构建的,它依赖于Erlang VM的基础功能。理解这一点非常重要,因为我曾经看到过一些说法,认为OTP能够以某种方式“移植”到其它运行时环境中。但我认为这是不太可能的,除非目标运行时平台能够提供一些严格的保障。

具体到任其崩溃和supervisor来说, Erlang的VM为它们提供了一些重要的保障。

  1. 每个进程的状态都是私有的,一旦进程中止,它不会留下任何垃圾状态,从而也不会干扰其它的进程。
  2. 当一个单一的进程崩溃时,其它进程不会受到影响,它们的运行不会被打断,除非你有意这么做。
  3. 其它进程能够收到某个进程崩溃的通知,并进行一些相应的处理。
  4. 可以无条件地中止一个进程(即使是进程正在进行一个密集的CPU运算)。
  5. 进程可以拥有外部资源(例如文件句柄或socket),一旦进程中止,它所拥有的资源会自动回收。

前两点特性能够帮助我们将故障的后果局限在一定范围内:如果有部分出现问题,整个系统的大部分依然能够继续提供服务。第三点保障能够让我们对某个故障进行响应,当发生崩溃时,Supervisor能够对其进行纠正。最后两点保证了适当的系统清理,如果没有这两点保障,系统可能会产生孤儿进程或是资源无法释放的问题。

如果缺乏这些保障,我认为是无法实现Erlang的容错性能力的。即使你能够尽力接近,但永远也做不到100%的功能,总会有些隐秘的功能是你无法察觉的。这并不是说你必须要使用Erlang VM才能够实现任其崩溃的做法,只是说你需要一种能够提供这些保障的VM。

InfoQ:你是否能够分享一下你对于Elixir目前在业界的使用情况的展望?

Sa?a:虽然Elixir还是一门新生的语言,但它的基础(Erlang)已经非常稳定,其能力近20年来在各种不同的大型系统中都得到了证实。成功的案例包括WhatsApp、RabbitMQ、Riak、实时竞价(AdRoll),以及财务系统(Klarna)等等。至于Elixir,我已经看到它越来越多地出现在各种解决方案的生产环境中,例如游戏的后台物联网(IoT)。可以在这里找到在生产环境中使用Elixir的公司的一个列表,我很期待看到它今后的发展。

关于本书作者

技术分享Sa?a Juri?是一位软件开发者,他在使用Elixir和Erlang打造高负载、高并发的服务端系统方面具有丰富的经验。

 

崛起的超级智能书评:人类智能发展的宏大叙事

作者:南京大学哲学系教授,科学技术与社会研究所所长潘天群 新拿到刚出版的《崛起的超级智能》一书,我一口气读完了。作者刘锋敏锐地观察到,今天的互联网已经不是个人电脑及智能手机等的简单连接ÿ... 查看详情

《python数据科学手册》学习笔记及书评(代码片段)

《Python数据科学手册》学习笔记文章目录《Python数据科学手册》学习笔记写在前面1.食谱数据库数据找不到的问题2.Seaborn马拉松可视化里时分秒转化为秒数的问题3.scikit-learn使用fetch_mldata无法下载MNIST数据集的问题4.GridSearchCV.grid_s... 查看详情

《流畅的python》学习笔记及书评(代码片段)

《流畅的python》学习笔记文章目录《流畅的python》学习笔记写在前面1.Python数据模型1.1特殊方法2.序列构成的数组2.1内置序列类型2.2列表推导和生成器表达式2.3元组不仅仅是不可变的列表2.4切片2.5增量赋值2.6排序2.7数组、内存视... 查看详情

《流畅的python》学习笔记及书评(代码片段)

《流畅的python》学习笔记文章目录《流畅的python》学习笔记写在前面1.Python数据模型1.1特殊方法2.序列构成的数组2.1内置序列类型2.2列表推导和生成器表达式2.3元组不仅仅是不可变的列表2.4切片2.5增量赋值2.6排序2.7数组、内存视... 查看详情

《tensorflow实战google深度学习框架(第二版)》学习笔记及书评(代码片段)

《TensorFlow实战Google深度学习框架(第二版)》学习笔记文章目录《TensorFlow实战Google深度学习框架(第二版)》学习笔记写在前面1.TensorFlow图像处理函数学习总结2.TensorFlow图像预处理完整样例3.TensorFlow多线程输入... 查看详情

3星|《跳槽内参》:论坛问答体,黄铁鹰主编,作者有5页多

...,开头问题是一个职场人士面临的跳槽选择,后面是各位作者对提问者的分析和答复,有时候后面再附上提问者后来实际的选择。书后把这些回答问题的人的姓名和头衔都列出来作为作者,共有5页多。全书没交代这些问答体的... 查看详情

书评第002篇:《汇编语言(第3版)》

本书基本信息作者:王爽(著)出版社:清华大学出版社出版时间:2013-9-1ISBN:9787302333142版次:3页数:337字数:527000印刷时间:2013-9-1开本:16开纸张:胶版纸印次:1包装:平装定价:36.00元 书籍封面 内容简单介绍 ... 查看详情

《我不知道该说什么,关于死亡还是爱情》······

...《我不知道该说什么,关于死亡还是爱情》······书评摘录不知道该说什么~书评每一页都是奇异而残忍的故事,就像那些残留在幸存者身上的辐射。— 查看详情

算法1.1课后问答及习题

1.javabytecodeC/C++编译器把源代码编译成汇编代码,Java编译器把Java源代码编译成字节码bytecode。 Java跨平台其实就是基于相同的bytecode规范做不同平台的虚拟机,我们的Java程序编译成bytecode后就可以在不同平台跑了。 .net框架... 查看详情

书评|《计算机体系结构》读后感

当我完成《一个64位操作系统的设计与实现》的写作时,本以为熟练地控制处理器便是接触到了处理器的真谛,但当我读完《计算机体系结构:量化研究方法》之后,才知道“星辰大海的波澜壮阔”。因为这本书... 查看详情

问答项目---登陆验证码点击切换及异步验证验证码

输出验证方法:publicfunctionverify(){$config=array(‘length‘=>2,‘reset‘=>false,‘useCurve‘=>false,‘useNoise‘=>false,);$obj=newThinkVerify($config);$obj->entry();}调用:<imgsrc="{:U(‘verify‘)}" 查看详情

书评第003篇:《0day安全:软件漏洞分析技术(第2版)》

本书基本信息丛书名:安全技术大系作者:王清(主编),张东辉、周浩、王继刚、赵双(编著)出版社:电子工业出版社出版时间:2011-6-1ISBN:9787121133961版次:1页数:753字数:780000印刷时间:2011-6-1开本:16开纸张:胶版纸印... 查看详情

问答项目---金币经验奖励规则及网站配置写入config文件

具体步骤:引入配置文件——>获取当前数组——>进行合并publicfunctionedit(){//引入config.php配置文件$file=APP_PATH.‘Common/Conf/config.php‘;$config=includeAPP_PATH.‘Common/Conf/config.php‘;$date=I(‘post.‘);$date=array_change_key 查看详情

基于知识图谱的《红楼梦》人物关系可视化及问答系统:人物数据采集(代码片段)

基于知识图谱的《红楼梦》人物关系可视化及问答系统:人物数据采集(一)数据来源代码展示数据来源https://baike.baidu.com/item/贾宝玉代码展示已添加注释:#!/usr/bin/envpython#coding:utf8fromurllibimportrequestfromurllib.parseimportquote 查看详情

基于知识图谱的《红楼梦》人物关系可视化及问答系统:人物数据采集(代码片段)

基于知识图谱的《红楼梦》人物关系可视化及问答系统:人物数据采集(一)数据来源代码展示数据来源https://baike.baidu.com/item/贾宝玉代码展示已添加注释:#!/usr/bin/envpython#coding:utf8fromurllibimportrequestfromurllib.parseimportquote 查看详情

《重学java设计模式》作者开始录视频了

作者:小傅哥博客:https://bugstack.cn沉淀、分享、成长,让自己和他人都能有所收获!😄1.前言哈哈哈,终于对B站下手了!大家好,我是小傅哥,在紧张、羞涩到适应后,哈哈哈,终于开... 查看详情

x96max变砖后usb烧录修复(byquqi99)(代码片段)

...载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明(作者:张华发表于:2019-04-13)因为想在X96MAX盒子上安装sshserver与nfsclient(android内核默认没配置nfs,所以play上也找不着nfsclient),于是装... 查看详情

《重学java设计模式》作者开始录视频了(代码片段)

作者:小傅哥博客:https://bugstack.cn沉淀、分享、成长,让自己和他人都能有所收获!😄1.前言哈哈哈,终于对B站下手了!大家好,我是小傅哥,在紧张、羞涩到适应后,哈哈哈,终于开... 查看详情