第6章 简单缓存系统源码
simple_cache.app应用系统元数据
%% ==========================================================================================
%% 说明:除了mod的value用{},其他的都用[]
%% ==========================================================================================
{application, simple_cache,
[
{vsn, "0.1.0"},
{description, "A simple cacheing system"},
{modules, [sc_app,
sc_sup]},
{registered, [sc_sup]}, %% 此处不应该是{registered, sc_sup}
{mod, {sc_app, []}}, %% 此处不应该是{mod, [sc_sup, start_link, []]}
{applications, [kernel, stdlib]}
]
}.
sc_app.erl实现应用程序行为模式
-module(sc_app). %% 注意module中的名称不能带.erl
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
sc_store:init(),
case sc_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Other ->
{error, Other}
end.
stop(_State) ->
ok.
sc_sup.erl顶层监督者
-module(sc_sup).
-behaviour(supervisor).
-export([init/1]).
%% 手写错误版
% -export([start_link/0, start_child/0]).
% start_link() ->
% supervisor:start_link({local, ?MODULE}, ?MODULE, []).
% start_child() ->
% supervisor:start_child({local, ?MODULE}, ).
% init([]) ->
% Strategy = [sc_element,
% {sc_element, start_link, []},
% temporary,
% bruch_kill,
% worker,
% [sc_element]],
% {simple_one_for_one, 0, 1, [Strategy]}.
%% 正确版
-define(SERVER, ?MODULE).
-export([start_link/0, start_child/2]).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child(Value, LeaseTime) ->
supervisor:start_child(?SERVER, [Value, LeaseTime]).
init([]) ->
Element = {sc_element,
{sc_element, start_link, []},
temporary, %% 注意是temporary
brutal_kill, %% 注意不是bruch_kill
worker,
[sc_element]},
Children = [Element],
RestartStrategy = {simple_one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}. %% 注意返回值元组样式
sc_element.erl进程与缓存Value的关联
-module(sc_element).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).%% 感觉似乎只是为了不至于编译时警告这些函数没有被使用???
-export([start_link/2, create/1, create/2, fecth/1, replace/2, delete/1]).
-define(DEFLEASETIME, 60). %%默认超时时间
-record(proState, {value, lease_time, start_time}).
%% ==========================================================================================
%% 对外API
%% ==========================================================================================
start_link(Value, LeaseTime) ->
gen_server:start_link(?MODULE, [Value, LeaseTime], []). %% 此处的参数不明白为什么非得这样???????
create(Value) ->
io:fwrite("ready creating...~n"),
create(Value, ?DEFLEASETIME).
create(Value, LeaseTime) ->
sc_sup:start_child(Value, LeaseTime).
fecth(Pid) ->
gen_server:call(Pid, fecth). %% 注意Pid放在前面,标示调用的是自己,特别注意是gen_server:call不是gen_server:handle_call
replace(Pid, Value) ->
gen_server:cast(Pid, {replace, Value}). %% 特别注意是gen_server:cast不是gen_server:handle_cast
delete(Pid) ->
gen_server:cast(Pid, delete). %% 特别注意是gen_server:cast不是gen_server:handle_cast
%% ==========================================================================================
%% 模式API接口
%% ==========================================================================================
init([Value, LeaseTime]) ->
Now = calendar:local_time(),
StartTime = calendar:datetime_to_gregorian_seconds(Now), %% 进程创建即开始计时
TimeLeft = time_left(StartTime, LeaseTime), %% 进程启动后距离超时的时间间隔
io:fwrite("create successfully...~n"),
{ok, #proState{value = Value, lease_time = LeaseTime, start_time = StartTime}, TimeLeft}. %% 注意record类型的使用都是{}, 不是()
%% 计算进程启动后是否达到超时的时间限制
time_left(_StartTime, infinity) ->
infinity;
time_left(StartTime, LeaseTime) ->
Now = calendar:local_time(),
CurTime = calendar:datetime_to_gregorian_seconds(Now),
TimeElapsed = CurTime - StartTime,
case LeaseTime - TimeElapsed of
Time when Time =< 0 -> 0;
Time -> Time * 1000
end.
%% ==========================================================================================
%% 返回值形式:{reply, Reply, NewState} | {reply, Reply, NewState, Timeout}
%% reply: 标示为带有返回值
%% {ok, Value}:具体返回值
%% State: 操作执行完成后的新状态
%% TimeLeft: 剩余超时时间
%% ==========================================================================================
handle_call(fecth, _From, State) ->
#proState{value = Value, lease_time = LeaseTime, start_time = StartTime} = State,
TimeLeft = time_left(StartTime, LeaseTime),
{reply, {ok, Value}, State, TimeLeft}. %% 因为是同步调用,所以必须有返回值,所以为reply
handle_cast({replace, Value}, State) ->
#proState{lease_time = LeaseTime, start_time = StartTime} = State,
TimeLeft = time_left(StartTime, LeaseTime),
{noreply, State#proState{value = Value}, TimeLeft};
handle_cast(delete, State) ->
{stop, normal, State}. %% 发送正常关闭消息,会触发调用handle_info
handle_info(timeout, State) -> %% 注意此处的参数timeout是个原子类型,直接注明并匹配超时消息
{stop, normal, State}.
terminate(_Reason, _State) ->
sc_store:delete(self()). %% 因为关闭操作需要解除Pid与Key的关联,这个在这里默认就能处理,进程退出了,则关联关系也就结束了,但是还有Pid与Value的关联需要在sc_store中解除
code_change(_OldVsn, State, _ExtraCondition) ->
{ok, State}.
sc_store.erl进程与缓存Key的关联
-module(sc_store).
-export([init/0, insert/2, delete/1, lookup/1]).
-define(TABLE_ID, ?MODULE).
init() ->
ets:new(?TABLE_ID, [named_table, public]), %% 注意是named_table,不是table_named
ok.
insert(Key, Pid) ->
ets:insert(?TABLE_ID, {Key, Pid}).
delete(Pid) ->
ets:match_delete(?TABLE_ID, {'_', Pid}).
lookup(Key) ->
case ets:lookup(?TABLE_ID, Key) of
[{Key, Pid}] -> {ok, Pid}; %% 查询操作返回的是列表形式
[] -> {error, not_found_123}
end.
simple_cache.erl
-module(simple_cache).
-export([lookup/1, add/2, delete/1]).
lookup(Key) ->
try
{ok, Pid} = sc_store:lookup(Key),
{ok, Value} = sc_element:fecth(Pid),
{ok, Value} %% 输出查询结果,虽然我们在sc_element中的最后返回是{reply, {ok, Value}, State, TimeLeft},但是那只是行为框架的返回值
catch
_Class:Exception ->
{error, Exception} %% 输出查询结果
end.
add(Key, Value) ->
case sc_store:lookup(Key) of
{ok, Pid} ->
sc_element:replace(Pid, Value);
{error, _} ->
{ok, Pid} = sc_element:create(Value),%% 为什么这个地方返回的是{ok, Pid},在sc_element中并没有地方组合这种返回值类型??????????
sc_store:insert(Key, Pid)
end.
delete(Key) ->
sc_element:delete(Key). %% 此处的调用会调用到sc_element中的删除和sc_store中的删除