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_sup
和 dets_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).