特殊进程是通过proc_lib来启动的进程,并实现了system消息处理进程。
包括但不限于常用的gen_server, gen_statem, gen_event等标准Behavior。
为啥自己实现
虽然Behavior很好很强大,可以满足绝大部分的需求,但是它们也存在缺点,那就是过于通用。为了达到通用的目的,
标准Behavior包含了大量处理边界条件的逻辑,一般情况下不会成为问题,但是当你的进程成为瓶颈的时候,可能需要考虑自己实现一个。
比方说:
- 有一个supervisor进程监控大量work进程,还有另一个gen_server进程来控制work的数量,那么这两个进程有一些工作是重复的。
- 有一个gen_server只会被local进程使用到,但是他包含了大量call,那么通用的call机制可能成为瓶颈。
为啥不用普通进程
除了一些需要异步进行的临时任务,尽量不要使用普通进程。特殊进程可以为你提供:
- 告诉你哪个进程是它的父进程
- 父进程退出时优雅的退出
- 异常退出时生成log
- 能够查看或者替换进程状态
这些好处值得多花几分钟来实现。
如何实现
特殊进程必须通过proc_lib和sys来实现。
proc_lib
通过proc_lib启动的进程总是会把两个信息写入进程字典:
- $ancestors
- $initial_call
这两个信息被各种调试工具用到。如果开启了SASL,那么proc_lib启动的进程崩溃的时候会生成崩溃日志。
1 | $ erl -boot start_sasl |
从上面log可以看到父进程以及初始函数都出现在崩溃日志中。
最后,用pro_lib还提供一个可选的特征,使用确认函数来同步启动进程。
sys
通过proc_lib启动的进程必须实现sys协议。
sys能够为你的进程带来更多调试以及跟踪机制。
- sys:get_status/1
- sys:get_state/1
- sys:replace_state/2
除此之外,实现sys协议的进程还能够暂停以及恢复。
异步启动
- 通过proc_lib:spawn_link/1..4或者proc_lib:spawn_opt/2..5启动进程。
- 写一个receive的循环。
- 父进程退出时退出,这意味着如果trap exit消息,需要处理父进程退出消息。
- 处理系统消息。
- 实现system_continue/3, system_terminate/4, system_code_change/4回调函数。
1 | start_link() -> |
同步启动
- 使用proc_lib:start_link/1..4启动进程
- 在进入循环之前调用proc_lib:init_ack/1
- 其他跟异步启动类似
1 | start_link() -> |
Call
OTP的call实现考虑了很多特殊情况,整个实现非常复杂。
1 | do_call(Process, Label, Request, Timeout) -> |
我们自己实现的时候可以去掉那些特殊情况,针对我们自己的情况优化冗余的检查和逻辑。
比如,我们没有注册名字,就不需要解析名字;如果没有用到C或者Java节点,可以去掉处理这部分的代码。
实例
参考ranch的ranch_conns_sup模块。很经典的例子。
参考文档
- The Erlanger Handbook
- Ranch