[erlang 源码猎人] dets_server

398 阅读1分钟

dets 是 Erlang 里提供类似 mnesia 的数据存储功能的模块, 它没有mnesia 那样分布式的功能, 但能提供通过线性哈希算法实现的数据查询功能.

我们知道, erlang 里所有的程序都是运行在某个轻量进程里的, dets_server 模块是beam vm运行的时候负责执行 dets 相关功能的进程.

这个 gen_server 使用 {local, dets_server} 作为注册名:

start_link() ->
    gen_server:start_link({local, ?SERVER_NAME}, dets_server, [self()], []).

ensure_started/1 这个函数很有意思, 它是一个内部函数, 但它的功能我们很多时候可以用到, 即检查一个注册了的进程是否已经启动. 这这里, dets_supdets_server 都被放在 kernel_safe_sup 下启动.

ensure_started() ->
    case whereis(?SERVER_NAME) of
	undefined -> 
	    DetsSup = {dets_sup, {dets_sup, start_link, []}, permanent,
		      1000, supervisor, [dets_sup]},
	    _ = supervisor:start_child(kernel_safe_sup, DetsSup),
	    DetsServer = {?SERVER_NAME, {?MODULE, start_link, []},
			  permanent, 2000, worker, [?MODULE]},
            _ = supervisor:start_child(kernel_safe_sup, DetsServer),
	    ok;
	_ -> ok
    end.

我们发现这个模块里所有对 gen_server 的调用都使用了这个自定义的 call 函数, timeout 是 infinity. 这至少说明两点: 1. 消息一定会到达 2. 消息一定会返回, 或者是 crash.

call(Message) ->
    ensure_started(),
    gen_server:call(?SERVER_NAME, Message, infinity).

dets_server 启动时, 在 init/1 里做了这几件事: 1. 从配置文件里读取dets 的"verbose"参数, 然后放在进程字典里. 2. trap exit. 3. 新建一个ets set用于存放 注册名 数据. 4. 新建一个 ets set 用于存放 owner 数据. 5. 新建一个 ets duplicate bag 用于存储.

init() ->
    set_verbose(verbose_flag()),
    process_flag(trap_exit, true),
    ?REGISTRY = ets:new(?REGISTRY, [set, named_table]),
    ?OWNERS = ets:new(?OWNERS, [set, named_table]),
    ets:new(?STORE, [duplicate_bag]).
    
verbose_flag() ->
    case init:get_argument(dets) of
	{ok, Args} ->
	    lists:member(["verbose"], Args);
	_ ->
	    false
    end.

set_verbose(true) ->
    put(verbose, yes);
set_verbose(_) ->
    erase(verbose).