用户向服务器发出搜索联系人的请求时,搜索结果可能是很大的数据,必须分页传递给客户端。在当前的查询中,负责数据库查询的进程不可中断,用户请求下一页时,仍然必须由该进程来完成。该进程也需维护一个超时,当用户一定时间没新的搜索请求到达,或者查询数据到达数据库末尾,则都应该结束该查询进程。
每个用户账号唯一地对应于一个查询进程,用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对。
具体的查询进程模块,请见下篇文章。