erlang otp并发编程实战

第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中的删除


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值