最近用 erlang 写了一个简单的 TCP server,功能很简单就不
详述了,其实写 erlang 我自己有三点感受,第一点是我自己很
少用 try catch 这个语法;第二件事情,是整个 erlang/OTP
几乎都是用 gen 或 gen_server 这个模组写的;第三点,永远
要有 plan B,在谈这点时,我会介绍如何用 erlang 写一个简
单的监控程式来恢复这个 TCP server。
在 erlang 中,只要不是再起不能这种严重的错误,基本上执行
错误应该都会出现类似 {error,Reason} 这样的计算结果,利用
这种结果,我可以故意在回传值写上 {ok, Result} 利用内建的
pattern match 来找出自己的逻辑错误,加上错误格式几乎都是
上述说的那种,可以直接用 case of 的方式处理,我整个程式
中只有用到一次 try catch: 为了提供两种不同格式的字串比对
方式,先用完整字串比对第一种格式,比对失败才跑第二种,类
似下面这样的处理方式。
handle_cast(string()) -> ok | {error, Reason}.
handle_cast("ping this machine") -> do_something.
handle_info([string()]) -> ok | {error, Reason}.
handle_info(["give", User, Something]) -> User! Something.
这部份如果直接用 case of 的方式处理,可能在 handle_cast
那边就喷出 pattern not match 之类的错误,而让连线断掉或是
程式整个挂掉,所以必须用 try catch 处理,虽说是个人风格,
但其实写 erlang 真的很少用到 try catch 就是。
关于第二点,erlang/OTP 其实才是整套 erlang 最关键的点,没
有这个,erlang 真的没办法自称 concurrency 最佳选择,它的基
本想法就是把 concurrency 中通用的抽出来,不通用的让使用者
自己写,所以也可以用 callback module 这样的方式来理解。
然后 erlang/OTP 其实只要会 gen_server,几乎就能自称懂 OTP
了,因为其他几个 OTP module 几乎都是用 gen_server 或 gen
这两个 module 写出来的,连 supervisor 这个监控模组都是。
然后我自己就写了两个自订 OTP module,功能跟使用上没那么通
用,有针对自己的问题去写,想来 erlang/OTP 其实有点通用过了
头.....
第三点是 plan B,其实正确来说是指“erlang 所谓的容错”。
翻开 erlang 相关的资料,常常会见到 NASA 被 erlang 使用者
抓出来做示范,这不是说 NASA 有用 erlang 来开发程式,而是
NASA 用 C 开发程式时,采用的观念跟 erlang 几乎是一样的,
也就是“至少一只监控程式可以恢复另一个程式”。
那我原本是把监控的部份做在同一个节点上,但后来拿掉改成一
台机器跑两个节点,需求从原本“不同机器互相监控”变成“自
己玩自己的”。
这个 TCP server 很简单,没有太复杂的需求,因此监控跟恢复
TCP server 很相对很简单,只要确保 TCP 可以正常运作即可,
直觉上就是监控程式自己连过去,只要断线就重新连线确认到底
还有没有办法连上,没办法就重开 TCP server ,有办法就继续
正常运作。
关于重开 TCP server ,这边还可以细分两类,第一类是 OS 层
级的重开,第二类是 erlang application 层级的重开,关于 OS
层级的重开就不多提了,第二类会用到 erlang 内建的 rpc 跟
cluster 的功能,对,erlang 有内建 cluster 的功能,而且使
用上还简单到我写出来连 P 币都骗不了的程度,只要确保两个节
点的 cookie 相同,从其中一台使用 net_adm:ping('[email protected]')
去连另外一个节点,回传 pong 就会形成 cluster,回传 pang 就
代表失败,没了,建立 cluster 真的就这样.........
好啦,其实建立 cluster 还是要注意一些事情啦,如果你有用过
erlang 写的 Riak、ejabberd、CouchDB 之类工具,建立 cluster
如果发现变成两个以上不同的 cluster,代表这两个 cluster 没
有互 ping,所以被切开了,只要从任何一台机器去 ping 另一个
cluster 中的任意一台机器,两个 cluster 就会连成一个。
erlang 中对于 cluster 的处理是每台机器有各自的节点纪录,过
一段时间时间就自己去 ping 全部节点,没回应就把它删掉,有回
应就保留,然后这个功能基本上是做在 erlang 或 net_kernel 这
两个 module 里面,详细我就没看了。
使用上虽然很方便,但想想那个指数级成长的 ping,其实颇可怕,
使用上真的要注意就是。
当两个节点连起来后,使用 rpc:call 或 rpc:cast 这两个函数,
就可以让 remote 节点执行某些程式了,application restart 这
种功能就一定要用 rpc 的方式跑,至于 call 跟 cast,一个是寄
情书后站在对方前等对方回应,另一个寄完情书就跑走,过一段时
间回来问对方回应。
明明打了一大串,感觉自己发了一整篇的废文...XD
总之如果是要建立简单、小型的 cluster,用 erlang 其实蛮不错
的,利用内建机制也可以做出简单的恢复机制。