专栏名称: 程序人生
十年漫漫程序人生,打过各种杂,也做过让我骄傲的软件;管理过数十人的团队,还带领一班兄弟姐妹创过业,目前在硅谷一家创业公司担任 VP。关注程序人生,了解程序猿,学做程序猿,做好程序猿,让我们的程序人生精彩满满。
目录
相关文章推荐
极客之家  ·  2.3k star,开源的一体化监控利器! ·  3 天前  
腾讯技术工程  ·  让小程序从“能用”到“好用”——还差一个Bu ... ·  4 天前  
程序猿  ·  彻底崩塌!美国IT业裁员狂飙35% ·  3 天前  
阿里云云栖号  ·  阿里云全栈AI技术引擎驱动SaaS企业全球化 ... ·  4 天前  
伯乐在线  ·  年薪 7000 万!扎克伯格大撒币,挖 AI 大牛 ·  3 天前  
伯乐在线  ·  年薪 7000 万!扎克伯格大撒币,挖 AI 大牛 ·  3 天前  
51好读  ›  专栏  ›  程序人生

上帝说:要有一门面向未来的语言,于是有了 erlang

程序人生  · 公众号  · 程序员  · 2017-04-13 21:19

正文

请到「今天看啥」查看全文


routing(路由)

当系统中有了无数的独立的 process 后,一个很迫切的问题是如何找到我们需要与之互操作的 process:这是个路由的过程。对于 erlang 来说,我们可以通过唯一的 pid 来定位到目标 process,就像打电话时直播电话号码;我们也可以通过和 pid 一一对应的名字来定位到目标 process,就像你背不下来程序君的手机号但可以从手机地址簿里调出呼叫一样;我们还可以通过呼叫转接的服务,在手机上没有记录我电话号码的情况下,通过第三方呼叫到我 —— 在 erlang 里,这个第三方,叫 name registry。

说两句题外话。

路由在系统设计里是一个很厚重的主题,处处闪现它的影子。我们知道,一切系统工作的基础都是事件触发,而事件触发的本源是路由。你看这篇文章时指尖上下滑动,触发了 tap/swipe 这样的事件,这事件需要被准确无误地路由给其 handler,才能完成正确的操作。DynamoDB 的本质是什么?就是你给我一个 key,我把它路由到 cluster 下面某个 node 下面的某个 value,要读要写,要杀要剐你说了算。至于其使用到的 gossip,consistent hashing,都是为路由服务的手段和算法而已。C++ 里你调用一个纯虚函数,底层通过一张 vtable 将你的调用路由到合适的子类的函数的实现上。Virtual memory 是虚地址到实地址转化的一张「路由表」,VMA 等概念不过是这道路由大餐的提拉米苏。所以我们学习一个系统,先从其路由开始;我们设计一个系统,路由也是核心的一环。子曾经曰过: 参透路由表,走遍天下都不怕。

error handling(错误处理)

erlang 的错误处理机制非常独特 —— error 的 bubble up 是靠 process 之间的 link 完成的。link 是一个作用域双方互相监视的机制,有点像自战国商鞅变法以降,古代中国常有的保甲连坐制度:一人犯法,连坐者也要受刑。两个 link 起来的 process —— 我们暂且赋予他们名称为小明和小红 —— 如果小明挂了,小红会收到 EXIT signal,如若不作处理,正常情况下也会挂掉,而小红之死,会进一步触发和小红 link 起来的其他 processes,从而引发和小明有关的整条利益链上的震荡。这便是著名的 let it crash 思想。这种独特的机制让 error handling 实现了真正意义上的 non-local:一切与其相关的 process,都会受其影响,就像是对待癌细胞一样,除非有对症的法子,否则相关的细胞统统杀死,绝不姑息。反观其他语言的 error handling,与其说是 handling,不如说是 hide —— 就像边关报急,烽火连天,兵部却将其压下,回报圣上天下太平,然后马照跑,舞照跳。

link 是双向的连坐机制,有时候对于一些影响不大的问题,我们并不需要如此强有力的手段,于是有了 monitor。monitor 像是战场上的斥候,监视一个 process 的异动,一旦有变,立即回禀一个消息。收到消息的 process 可以选择按兵不动,或者随机应变。

erlang/OTP 的 supervision tree 就是基于 link/monitor 这样非常简单的机制构建起来的。而之所以 erlang 可以任性的 let it crash,其中一个很大的原因是 process 非常轻量,构建和销毁的代价无非就是创建和回收若干个 C structs。

golang 的 error handling 没有类似的 link/monitor 的机制,我觉着是其一个不大不小的败笔(当然也可能是为了性能考量之后的妥协,毕竟没有理想的语言,只有为了特定目的而妥协出来的语言)。至于如果 golang 要采用类似的机制 —— 那么,goroutine 和 channel,goroutine 和 goroutine,channel 和 channel 间,是否需要 link/monitor,如何 link/monitor,不在本文讨论的范围之内。

concurrency(并发)

前面讲到,erlang 的并发模型使用了 actor model。actor model 是说每个 actor 和其他 actor 通讯的方式能且仅能通过 message passing 而完成。因而,每个 actor 有其独立的 mailbox,用于接收消息。在 erlang 下,process 就是 actor。为了调度数量庞大的 processes,erlang VM 有自己的 scheduler —— 在 SMP(可以简单理解为多核)环境下,每个 CPU core 有一个 scheduler。和 OS 的 scheduler 类似,它有 run queue 和 wait queue,scheduler 从 run queue 上取出下一个 process,执行其代码。如果 process 没有通过诸如 receive message 这样的动作显式地将自己阻塞并被调度出去,scheduler 会在一个固定的 cycle 后(在 erlang 里叫 reduction)把 process 调度出去。所以从 erlang 程序员的角度来看,erlang 是 preemptive scheduling,并且,这种每个 process 获得的 CPU 时间几乎固定,事件在可预知的时间内得到调度,被称为 soft realtime。鲜有带 GC 的语言能够达到 soft realtime,Java 不行,golang 不行,因为它们的全局 GC 都会有臭名昭著的 STW(stop the world)问题(GC 一工作,世界就清净了,GC 会工作多久,天知道)。

既然提到了 soft realtime,那么,什么事 hard realtime 呢?CPU 的时钟中断是一种 hard realtime。它会在固定的间隔被触发,丝毫不差,风雨无阻。

OK,有了这些最基础的认知,我们可以进入到下一个环节 —— 谈谈 message passing,scheduling,以及 OTP。

Message passing

erlang 的 message passing 在很多文章里被当做是理所当然的,无需深究的事情,就像水啊,电啊这样的东西,谁关心它们是怎么来的?程序君在研习 erlang 时,对此却有好多疑问:

  • message 怎么从一个 process 传递到另一个 process?这中间有数据拷贝么?有锁么?锁是什么粒度?

  • message 会否在传递的过程中丢失?如果丢了,咋办?

  • 那些巨型的,从 TCP/IP stack 上收来的 message,从小明传递给小红,是否也要拷贝?拷贝的话,效率该多低?

  • exit,link,monitor 这些 signal 究竟和 message 是什么样的关系?

  • ...







请到「今天看啥」查看全文