Erlang监视并自动恢复子进程
前段时间做过一个监视子进程并在子进程异常退出时自动重启子进程的实验,下面整理一下代码做个记录。
监视器模块
主要原理是通过process_flag(trap_exit, true)让父进程可以接收到子进程退出时的{'EXIT', Pid, Reason}消息,然后通过Pid找到子进程的入口函数,重新spawn以实现重启子进程的目的。
以下是监视进程的代码,通过start函数先启动监视进程,然后通过spawn_and_watch函数启动并注册受监控的子进程。
-module(proc_watcher). -export([start/0, spawn_and_watch/4, proc_watcher_loop/0]). start () -> register(proc_watcher, spawn(?MODULE, proc_watcher_loop, [])). spawn_and_watch (ProcName, Module, Function, Args) -> proc_watcher ! {spawn, self(), {ProcName, Module, Function, Args}}, receive {spawn_ok, Pid} -> {ok, Pid} end. proc_watcher_loop () -> process_flag(trap_exit, true), receive {spawn, From, {ProcName, Module, Function, Args}} -> case catch spawn_link(Module, Function, Args) of {'EXIT', Reason} -> From ! {spanw_error, Reason}, proc_watcher_loop(); Pid -> put(Pid, {ProcName, Module, Function, Args}), register(ProcName, Pid), From ! {spawn_ok, Pid}, proc_watcher_loop() end; {'EXIT', From, _} -> case erase(From) of undefined -> proc_watcher_loop(); {ProcName, Module, Function, Args} -> Pid = spawn_link(Module, Function, Args), register(ProcName, Pid), put(Pid, {ProcName, Module, Function, Args}), proc_watcher_loop() end end.
测试模块
以下是测试模块的代码,用于模拟一个会异常退出的进程,这个进程等待两个消息:{add, A, B} 和 hi。收到{add, A, B}时,进程输出A + B的值然后递归继续等待消息,由于没有类型判断,所以当A+B类型不合法时,进程将会异常退出。收到hi的时候,进程输出当前进程ID并自然退出,因为没有继续递归调用。这两种退出情况都会被监视进程捕获,重新启动新进程并用原来的名字注册进程。
-module(test). -export([test_proc/0]). test_proc () -> receive {add, A, B} -> io:format("A + B = ~p~n", [A + B]), test_proc(); hi -> io:format("Hi! I'm ~p~n", [self()]) end.
实验过程示例
启动监视进程:
proc_watcher:start().
启动实验进程:
proc_watcher:spawn_and_watch(the_test, test, test_proc, []).
执行正常操作(确定进程正常启动):
the_test ! {add, 1, 2}.
正常结束进程(观察当前进程ID):
the_test ! hi.
执行正常操作(确定进程有被重启):
the_test ! {add, 1, 2}.
异常退出进程:
the_test ! {add, a, 2}.
正常结束进程(观察进程ID变化):
the_test ! hi.