第七章 添加了自定义消息流的简单缓存系统
simple_cache.app应用系统元数据
{application, simple_cache,
[{description, "A simple cacheing system"},
{vsn, "0.1.0"},
{modules, [sc_app,
sc_sup,
sc_element_sup,
sc_element,
sc_store,
sc_event,
sc_event_logger,
simple_cache]}, %% 此处也可以写少于实际的模块名
{registered, [sc_sup, sc_element_sup, sc_event]},%% 感觉此处可以随便写,不影响运行和结果
{applications, [kernel, stdlib, sasl]},
{mod, {sc_app, []}}
]}.
sc_app.erl
-module(sc_app).
-behaviour(application).
%% ====================================================================
%% API functions
%% ====================================================================
-export([start/2, stop/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
start(_startType, _startArgs) ->
sc_store:init(),
case sc_sup:start_link() of %% 启动顶层监督者进程
{ok, Pid} ->
sc_event_logger:add_handler(), %% 添加自定义事件处理器
{ok, Pid};
Other ->
{error, Other}
end.
stop(_State) ->
ok.
sc_element.erl
-module(sc_element).
%% ====================================================================
%% API functions
%% ====================================================================
-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, fetch/1, replace/2, delete/1]).
-define(DEF_LEASE_TIME, 60).
-record(proState, {value, lease_time, start_time}).
%% ====================================================================
%% Internal functions
%% ====================================================================
start_link(Value, LeaseTime) ->
gen_server:start_link(?MODULE, [Value, LeaseTime], []).%% ????????
create(Value) ->
create(Value, ?DEF_LEASE_TIME).
create(Value, LeaseTime) ->
sc_element_sup:start_child(Value, LeaseTime).%% 注意此处修改为sc_element_sup
fetch(Pid) ->
gen_server:call(Pid, fetch).
replace(Pid, Value) ->
gen_server:cast(Pid, {replace, Value}).
delete(Pid) ->
gen_server:cast(Pid, delete).
%% ====================================================================
%% Extra functions
%% ====================================================================
%% 判断距离超时还有多少毫秒
time_left(_Time, 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.
%% ====================================================================
%% Behavioural functions
%% ====================================================================
init([Value, LeaseTime]) ->
Now = calendar:local_time(),
StartTime = calendar:datetime_to_gregorian_seconds(Now),
TimeLeft = time_left(StartTime, LeaseTime),
{ok, #proState{value = Value, lease_time = LeaseTime, start_time = StartTime}, TimeLeft}.
handle_call(fetch, _From, State) ->
#proState{value = Value, lease_time = LeaseTime, start_time = StartTime} = State,
TimeLeft = time_left(StartTime, LeaseTime),
{reply, {ok, Value}, State, TimeLeft}.
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(timeout, State) ->
{stop, normal, State}.
terminate(_Reason, _State) ->
sc_store:delete(self()).
code_change(_OldVsn, State, _ExtraCondition) ->
{ok, State}.
sc_element_sup.erl
-module(sc_element_sup).
-behaviour(supervisor).
-export([init/1]).
%% ====================================================================
%% API functions
%% ====================================================================
-export([start_link/0, start_child/2]).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
init([]) ->
AChild = {sc_element,
{sc_element,start_link,[]},
temporary,
brutal_kill,
worker,
[sc_element]},
Children = [AChild],
Strategy = {simple_one_for_one, 0, 1},
{ok,{Strategy, Children}}.
%% ====================================================================
%% Internal functions
%% ====================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
start_child(Value, LeaseTime) ->
supervisor:start_child(?MODULE, [Value, LeaseTime]).
sc_event.erl
-module(sc_event).
-export([start_link/0, add_handler/2, delete_handler/2, lookup/1, replace/2, create/2, delete/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
start_link() ->
gen_event:start_link({local, ?MODULE}).
add_handler(Handler, Args) ->
gen_event:add_handler(?MODULE, Handler, Args).
delete_handler(Handler, Args) ->
gen_event:delete_handler(?MODULE, Handler, Args).
lookup(Key) ->
gen_event:notify(?MODULE, {lookup, Key}).
replace(Key, Value) ->
gen_event:notify(?MODULE, {replay, {Key, Value}}).
create(Key, Value) ->
gen_event:notify(?MODULE, {create, {Key, Value}}).
delete(Key) ->
gen_event:notify(?MODULE, {delete, Key}).
sc_event_logger.erl
-module(sc_event_logger).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).
-export([add_handler/0, delete_handler/0]).
-record(proState, {}).
%% ====================================================================
%% Internal functions
%% ====================================================================
add_handler() ->
sc_event:add_handler(?MODULE, []).
delete_handler() ->
sc_event:delete_handler(?MODULE, []).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
init([]) ->
{ok, #proState{}}.
handle_event({create, {Key, Value}}, State) ->
error_logger:info_msg("创建缓存数据(~w, ~w)~n", [Key, Value]),%% 在shell中无法显示中文
{ok, State};
handle_event({lookup, Key}, State) ->
error_logger:info_msg("lookup(~w)~n", [Key]),
{ok, State};
handle_event({replace, {Key, Value}}, State) ->
error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
{ok, State};
handle_event({delete, Key}, State) ->
error_logger:info_msg("delete(~w)~n", [Key]),
{ok, State}.
handle_call(_Request, State) ->
Reply = ok,
{ok, Reply, State}.
handle_info(_Info, State) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _ExtraCondition) ->
{ok, State}.
sc_store.erl
%% @author Administrator
%% @doc @todo Add description to sc_store.
-module(sc_store).
%% ====================================================================
%% API functions
%% ====================================================================
-export([init/0, lookup/1, insert/2, delete/1]).
-define(TABLE_ID, ?MODULE).
%% ====================================================================
%% Internal functions
%% ====================================================================
init() ->
ets:new(?TABLE_ID, [named_table, public]),
ok.
lookup(Key) ->
case ets:lookup(?TABLE_ID, Key) of
[{Key, Pid}] -> {ok, Pid};
[] -> {error, not_found}
end.
insert(Key, Pid) ->
ets:insert(?TABLE_ID, {Key, Pid}).
delete(Key) ->
ets:match_delete(?TABLE_ID, {'_', Key}).
sc_sup.erl
%% @author Administrator
%% @doc @todo Add description to sc_sup.
-module(sc_sup).
-behaviour(supervisor).
-export([init/1]).
%% ====================================================================
%% API functions
%% ====================================================================
-export([start_link/0, start_child/2]).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
init([]) ->
ElementSupervisor = {sc_element_sup, %% 注意此处是sc_element_sup
{sc_element_sup,start_link,[]},%% 注意此处是sc_element_sup,如果还是sc_element则会导致信息不详的错误提示
permanent, %% 重启策略修改了
2000, %% 关闭时间修改了
supervisor, %% 类型修改了
[sc_element]}, %% 注意此处是sc_element
EventSupervisor = {sc_event,
{sc_event, start_link, []},
permanent,
2000,
worker,
[sc_event]},
Children = [ElementSupervisor, EventSupervisor],
Strategy = {one_for_one, 4, 3600},
{ok,{Strategy, Children}}.
%% ====================================================================
%% Internal functions
%% ====================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
start_child(Value, LeaseTime) ->
supervisor:start_child(?MODULE, [Value, LeaseTime]).
simple_cache.erl
%% @author Administrator
%% @doc @todo Add description to simple_cache.
-module(simple_cache).
%% ====================================================================
%% API functions
%% ====================================================================
-export([lookup/1, insert/2, delete/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
lookup(Key) ->
try
{ok, Pid} = sc_store:lookup(Key),
{ok, Value} = sc_element:fetch(Pid),
sc_event:lookup(Key),
{ok, Value}
catch
_Class:_Exception ->
{error, not_found}
end.
insert(Key, Value) ->
case sc_store:lookup(Key) of
{ok, Pid} ->
sc_element:replace(Pid, Value),
sc_event:replace(Key, Value);
{error, _} ->
{ok, Pid} = sc_element:create(Value),
sc_store:insert(Key, Pid),
sc_event:create(Key, Value) %% 投递自定义的创建事件
end.
delete(Key) ->
case sc_store:lookup(Key) of
{ok, Pid} ->
sc_element:delete(Pid),
sc_event:delete(Key);
{error, _Reason} ->
ok
end.