为啥 Erlang 没有像 Go、Scala 语言那样崛起? - 知乎
问:
成熟的杀手级产品,技术栈完整,社区活跃,易上手使用的人比较多。
scala 目前业界应用广泛,技术栈完整,社区活跃,还有 spark 这个重量级杀手锏;go 类似,也有 docker;erlang 呢?为什么没流行起来?从语言特性,成本,生态圈,推广代价等分析讨论。
Worse is Better (
http://dreamsongs.com/RiseOfWorseIsBetter.html
)能比较好地解释题主的「为什么」。注意这篇文章的写作背景,这是一位 LISP 大佬在九十年代初反思为什么 LISP 这么牛的语言日渐式微,C 和 UNIX 这么烂的东西却起来了。概括下来大概是这样的:
软件设计有以下四大目标:简单、正确、一致、完整,但两大流派 MIT Style (MIT AI Lab 是 LISP 重镇) 和 New Jersey Style (C 和 UNIX 的老家贝尔实验室所在地) 对这些目标的优先级排序不同。MIT Style 认为软件正确性要绝对保证,然后优先级 正确 ~= 一致 > 完整 > 简单,简单这一条还得分,为了接口简单,可以忍受实现复杂。而 New Jersey Style 是正好反过来:首先软件实现得简单,做不到宁愿让接口复杂点,为了简单显然可以牺牲完整性,而正确、一致,那就尽力吧…… 反正得简单。Worse is Better 前面的 Worse 指的就是像 UNIX 这样为简单甚至能放弃「正确」这种有绝对标准的好的东西,后面的 Better, 指的是更好的生存适应性,这里面不带价值判断,文章作者也为 "Worse Is Better Is Worse" or "Worse is Better is Still Better" 一直在纠结,但这是一个能解释很多现象的准确观察。
没错,Erlang 就是 MIT Style, do the right thing 那个。它跟 LISP 一样产生深远的影响,会被无数后世语言技术借鉴 —— 即使不用 Erlang 开发我还是会对每个新人都会高度推荐 Erlang paper (
http://erlang.org/download/armstrong_thesis_2003.pdf
) —— 可是能火起来的,还会是那帮新泽西佬做出来的敢连 generics 都没有的烂货。
一个东西火起来的关键在于传播。文章把 C 和 UNIX 比作病毒:它实现(而不是接口)很简单,所以能很容易移植(感染)到别的平台,迅速跟原有平台的东西整合,因为它东西少,不拘泥于「正确」、「本质」之类的东西,能根据平台和需求快速演化,也能不断吸(chao)收(xi) 别人的东西让自己变得更强大。
但是 Erlang 不行,阻碍 Erlang 传播的除了爱立信作死,还有它自己的特性。A History of Erlang 里数次提到,Erlang 很难移植到别的语言/运行平台,因为 Erlang 的运行模型太特别了(真可惜只有Erlang是对的),所以一切都只能自己搞,同样原因要调用宿主平台的原有模块也很困难。Erlang 有一个简单、正确、不妥协的接口,但是底层实现就不得不非常复杂精巧,当底层实现的优化都不能满足你的特定需求时,你很难绕过统一美好的模型做case by case, quick and dirty的优化。某个算法 Erlang 跑太慢了你要引入 C 模块,难(望向 PHP & Python),复制消息传递通常不是瓶颈但如果变成瓶颈能传指针么?NO, YOU'RE DOING IT WRONG! 当然实际上 Erlang 内部对这个是有优化的,大的 binary 会自动变一个引用计数 buffer 放共享堆然后就只用传引用啦,但是这马上导致一个问题,因为 Erlang 没有(不需要有)全局 GC, 如果有进程已经不引用这个 binary, 但因为各种原因触发 GC 迟了,共享 heap 里引用计数一直不清零这段 binary 就僵在那流量冲击大你就等OOM吧,这是实际线上系统会碰到的问题,Binary是不是refc又不是你说了算,所以还是时不时自己强行 GC 一下…… 这其实就是一个接口简单导致实现复杂优化困难并且难以做对结果还是要用户自己动手的例子。Go 的 STW GC 虽然简陋,但它不限模型,鼓励复制但不禁止传指针,对这种少数的 case 很容易解决,再拿个 pool 来对付一下蠢萌的 GC 就可以了。模型不纯粹,实现极简陋,但解决问题。
Worse is Better 也能解释别的语言或技术的崛起。你不用很优秀但要有一个点做得好打到痛点,你不用设计完美但至少别犯大错,然后保持简单,保证好上手易移植易与现有系统整合,就可以了。包括 PHP(别打我),它架构上首先基于 CGI: 每个请求一个独立进程,share nothing,只用管道跟 httpd 通信,这天然保证错误隔离,也让 CGI 应用完全不需要管网络交互问题,短连接模型处理完请求就进程结束所以 PHP 甚至不需要 GC, memory pool 就行,更重要的是它也天然支持了热替换。你可以说这些全部是 Erlang 的设计点,也完全符合 Erlang paper 提倡的把困难问题(HTTP server)做成框架让业务写得简单轻松的思路,同时这也是传统的 UNIX 编程方式 —— 这就是所谓设计上别犯大错。然后再做一个亮点:直接 HTML 代码里嵌入脚本,现在你觉得这很傻,但当时互联网刚起来,HTML 还是新鲜事物,更没有什么 AJAX, 不用你自己组装字符串直接把代码嵌到 HTML 里太打动人心了。是,这没什么难的,但别人没有,PHP 有。至于一台机撑多少连接?速度怎么样?当时有个人访问你网页你都兴奋半天了,谁跟你研究这种吃撑了怎么办的问题?
如果 Worse is Better 作为一个定律是正确的,这听起来很可悲,我们就活该没有好东西用了。我感觉可以稍微修正一下:对于开发技术这种存在网络效应的东西,除非一个方案有革命性的优势,否则都会服从 worse is better 定律。
在电信行业,Erlang 可能真的拥有革命性的优势,我不熟悉的领域不评论,可惜电信行业本身相对狭窄封闭再加上爱立信作死,没网络效应可言。而在其他地方,革命性的优势,它没有。
提到的好 8 倍,我记忆中来自 A History of Erlang 描述一个早期项目,对比对象是一个没什么公开信息的内部语言 PLEX,而且 Joe 说了原始文件从未公开,且当时内部争议就很大;Erlang 标志性项目 AXD 301 的报告结论 (
http://www.erlang.se/publications/Ulf_Wiger.pdf
) 是 4 倍,对比对象是 C/C++,方法是对比代码长度(所有动态脚本语言都能赢好吗)和bug密度(相同),更重要的是,它并没有分析 Erlang 的语言特性在开发效率提高中的必然作用,甚至没有代码对比样例(报告里倒是有一堆组织架构管理方法内容),也没说明有 C++ 使用什么能跟 OTP 对应的框架,这种论据拿到知乎上是会被喷死的。
Erlang 的高并发只能说它是生不逢时。90年代,除了电信领域,这种高并发需求实在没有,要真有,QQ算?人家用 UDP 木有连接保持问题。当时的硬件情况,机械硬盘、网络带宽、CPU 内存逐个撑死了都还没轮到并发。Telnet-based BBS 是 TCP 长链接模型,用最基本的 UNIX fork 进程模型(操作系统级进程隔离,要不是这样那十几二十万行sh*t还真跑不起来),中山大学逸仙时空用一台 98 年的 HP Alpha(以前的DEC)机器 2G 内存好像?能撑 3000 在线,水木PTT等大站有好硬盘好CPU 4G乃至8G内存20000在线不在话下,注意是在线,发呆会被踢的,所以这些连接的活动率很高(作为对比 AXD 301 用的是 Sparc Ultra 2 跑 Solaris 活跃进程有200-4000)。系统的瓶颈一是硬盘二是内存,远远没轮到模型问题。到编程模型真有问题的时候,Worse is Better 已经发挥作用,各种现有开发平台借鉴 Erlang 模型可以搞出很好的东西 —— 为什么是借鉴而不是直接用 Erlang? 答案 Joe 自己已经说了,他在 erlang paper 里鼓励separate of concern, 框架模型这么复杂的东西应该由专业人士一次过搞定,模型之上的应用就很轻松而且根本不用关注并发问题;在 A History of Erlang 中,他提到一个想法,Erlang 是一个运行平台,进程里跑的是什么语言并没有太大关系。那也就是说,虽然用别的语言山寨一个并发运行环境很难(而且很难做对),但是这只是一个一次性的工作而且可以根据业务特性定制,框架完成后,你还是可以用熟悉的语言写业务,完全不用管并发问题,但同样能达到好很多的并行度(所谓Actor / Reactor模型blah blah blah, 或者像MapReduce这种特定框架),而且只需要对少数关键模块动手而不是全局替换,性价比高很多吧。
分布式支持的故事也一样,Erlang 从原理上天然支持分布式,也很早就有了内置分布式支持。然而,这个支持所基于的假设也一样老:几个到十几个节点,均质且可靠全互联的网络,单一的全 Erlang 系统。Joe 怎么也不会想到有人要用几万台破 PC 做集群还妄想跨洲跨机房热备。到我们真的需要分布处理能力的时候,你会发现实现的设计假设限制了它的扩展性(
http://release-project.softlab.ntua.gr/documents/ifl12.pdf
),CAP 的基本限制在,不可能有一致、透明的分布式通信和状态管理方案(
[erlang-questions] State Management Problem
这个 thread 可以看到最后 Robert Virding 的总结陈词),你还得去连不按 Erlang 规则来的别的服务,你也没法把 mnesia 扩展到 GFS 的规模,因为它根本不是为这个设计的,Erlang 的这些先发优势没有变成劣势已经很不错了。现在回去看 GFS 和 MapReduce 的论文,你会觉得他们简陋得可笑,里面大篇幅的 fault tolerence 的东西,都是 Erlang 设计原则的特化和简化,可是至少在那时,Erlang 没有发挥它的威力,而 New Jersey guys 蛮干硬上把东西做出来了。你甚至可以认为整个 Google 集群管理系统 Borg 就是一个大号 Erlang 平台,一堆 supervisor 在管着一堆 share nothing RPC 消息通信随时会挂的服务(流行用语叫 micro service),绝大部分服务无状态可以随时挂,Goolge C++ 不允许抛异常但是规定你如果发现完全没预料到的东西你应该直接打log crash掉等重启处理,自动均衡部署容灾一应俱全,几乎无限扩展,处处闪耀着 Erlang 的思想光芒,但它们都是用 C++ 写的。开源版 Kubernets 就是 Go 了,Go 没有内置分布式支持,因为整个体系在,它自己已经不需要了。
这完全就是 Worse is Better 的原文:“The good news is that in 1995 we will have a good operating system and programming language; the bad news is that they will be Unix and C++.”
Google Trends, 全球数据,erlang vs. golang
最后还是多嘴一句,这不是说我们就不应该用 Erlang. Paul Graham 在
里提到,Common Lisp 是他当年创业的秘密武器,让他们获得了比用 C++ 或 Java 的竞争对手高的多的开发效率。没人用又怎样?你就是要用普通人不用的东西才能战胜普通人。同样的,如果你相信 Erlang 是你能打败庸众的秘密武器,Go for it (pun NOT intended).
作者:布丁
链接:https://www.zhihu.com/question/38032439/answer/84176970