用户向服务器发出搜索联系人的请求时,搜索结果可能是很大的数据,必须分页传递给客户端。在当前的查询中,负责数据库查询的进程不可中断,用户请求下一页时,仍然必须由该进程来完成。该进程也需维护一个超时,当用户一定时间没新的搜索请求到达,或者查询数据到达数据库末尾,则都应该结束该查询进程。
每个用户账号唯一地对应于一个查询进程,用ets表管理用户账号Acc及其对应的动态创建的查询数据库的进程Pid之间的映射关系。设计时,考虑过使用用户账号转成原子,再用原子构建注册进程,但考虑到Erlang系统里,原子不会被回收,动态创建大量原子可能造成内存的泄露,所以放弃了这种设计。现在的设计是,将用户账号和对应的查询进程Pid写入ets表,这样当某用户请求到达时,即可从该ets表里查询获取对应的查询进程的Pid。(但此种设计的缺陷是,一旦保存AccPid映射关系的进程崩溃,所有正在搜索联系人的用户都会受到影响,希望以后能有更好的解决方案)
思路理清,现在开始开工。
此模块既可使用OTP框架,也可以不采用。但为了工程的健壮性,还是采用OTP行为模式,这样可以将其纳入监督者的管理之下。
cs_accToSearchDBPid.erl
-module(cs_accToSearchDBPid).
-behaviour(gen_server).
-export([
start_link/0,
insert/2,
lookup/1,
delete/1
]).
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-define(SERVER,?MODULE).
-define(TABLE_ID,?MODULE).
start_link()->
gen_server:start_link({local,?SERVER},?MODULE,[],[]).
insert(Acc,Pid)->
gen_server:cast(?SERVER,{insert,{Acc,Pid}}).
lookup(Acc)->
gen_server:call(?SERVER,{lookup,Acc}).
delete(Pid)->
gen_server:cast(?SERVER,{delete,Pid}).
init([])->
ets:new(?TABLE_ID,[private,named_table,set]),
{ok,state}.
handle_call({lookup,Acc},_,State)->
Value=ets:lookup(?TABLE_ID,Acc),
{reply,{ok,Value},State}.
handle_cast({insert,{Acc,Pid}},State)->
ets:insert(?TABLE_ID,{Acc,Pid}),
{noreply,State};
handle_cast({delete,Pid},State)->
ets:match_delete(?TABLE_ID,{'_',Pid}),
{noreply,State}.
handle_info(_,State)->
{noreply,State}.
terminate(_Reason,_State)->
ok.
code_change(_OldVsn,State,_Extra)->
{ok,State}.
cs_accToSearchDBPid模块中导出了3个接口函数:insert是插入新的Acc-Pid对,lookup是通过Acc查询对应的进程Pid,delete是删除一个Acc-Pid对。
具体的查询进程模块,请见下篇文章。