这些文章可以算是读书笔记了..写在这记录一下
记录:
程序的源代码在 https://github.com/erlware/Erlang-and-OTP-in-Action-Source/tree/master/chapter_06
编译程序在window下出错的(elrc -o ebin src/*.erl 显示src/*.erl:none: I/O error)的 可以用以下命令:
FOR %f in (src\*.erl) DO erlc -W -o ebin "%f"
这个示例程序中包含了几种行为模式:
sc_app 是application行为 包含了最基本的启动和停止。
sc_sup 是supervisor行为 在程序中是根监督者,并负责创建sc_element。
sc_element 是gen_server行为 通用服务器 这边用处是作为一个进程保存内容(State中) 提供超时清除
其他的
simple_cache是用户接口
sc_store相当于数据库访问接口(类似java中的DaoImpl)
sc_app作为程序启动的入口
程序通过application:start(simple_cache)启动 启动后初始化数据库并启动根监督者:
start(StartType, StartArgs) -> sc_store:init(), case sc_sup:start_link() of {ok, Pid} -> {ok, Pid}; Other -> {error, Other} end.
sc_sup负责监督工作者sc_element 同时sc_element由sc_sup代理创建
流程如下: ---> sc_element:create/2 调用-->sc_sup:start_child/2 -->调用 supervisor:start_child/2 -->sc_element:start_link/2 -->gen_server:start_link/3
创建后sc_element就由sc_supervisor管理(如下图):
(使用的模式是simple_one_for_one 工作者只能有一种类型 不会随监督者启动而启动 通过start_child动态启动).
sc_store保存了 key-PID的映射
记录的PID是sc_element的进程 value在sc_element内
这个PID的作用是 让gen_server:cast/2 和 gen_server:call/2 找到对应的进程并执行操作 代码如下:
fetch(Pid) -> gen_server:call(Pid,fetch).
该代码交给模块中具体的handle_call/3来完成
其他的内容都比较简单了
不过发现代码存在一个不是很严重的问题
用户接口处的代码存在竞态条件:
insert(Key, Value) -> case sc_store:lookup(Key) of {ok, Pid} -> sc_element:replace(Pid, Value); {error, _} -> {ok, Pid} = sc_element:create(Value), sc_store:insert(Key, Pid) end.
先检查后执行
不过对于ETS来说插入的key相同只是一个覆盖而已 这边顶多产生不被记录在表里的Pid(该进程也会在默认时间内被清除)