之前数据库读写模型的实现
之前有个数据库读写的设想,然后就实现了一下,实现花的时间比预期的要长,昨天没有弄完,今天又加班弄了一会。但还是有很多很不满意的地方,包括测试也没有做到位。明天回家看孩子,再整就得下周二了,到时候再好好调整下。
重点看game_db_sup后面的,game_db_sup监督了4个behaviour。
eredis_pool_sup是github上现成的第三方库,主要负责redis的读写,主要是有poolboy和erdis的东西一起完成的。
game_db_cache是个gen_server,主要负责配置表从mysql=>redis一些具体需求的映射初始化,之所以写成gen_server主要还是为了方便热更新。
game_db_queue是一个redis=>mysql同步用的消息队列,gen_server。主要开放的接口是
-export([enqueue/1,dequeue/0,get_queue_len/0]).
意思分别是入队,出队,查询队列长度。
game_db_writer是用gen_fsm状态机实现的,主要负责具体的写事务,这个类目前写的效率还没有具体调优。
game_db_cache.erl
%%%------------------------------------------------------------------- %%% @author 李世铭 %%% @copyright (C) Mar,30th 2016, <COMPANY> %%% @doc %%% 进行服务器启动时的缓存处理服务 %%% @end %%% Created : 30. 三月 2016 14:10 %%%------------------------------------------------------------------- -module(game_db_cache). -author("Administrator"). -behaviour(gen_server). %% API -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%自定义API -export([reload/0, reload/1]). -define(SERVER, ?MODULE). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== %%重新计算全部的缓存,mysql=>redis reload()-> reload(all). %%重新计算缓存,根据对应的atom读取计算对应的cache,mysql=>redis reload(TableName) when is_atom(TableName)-> gen_server:call(?MODULE, {reload,TableName}); reload(TableName) when is_list(TableName)-> TableAtom = list_to_existing_atom(TableName), gen_server:call(?MODULE, {reload,TableName}); reload(TableName) when is_binary(TableName)-> TableAtom = list_to_existing_atom(binary_to_list(TableName)), gen_server:call(?MODULE, {reload,TableAtom}). %%根据传入的原子来读取对应的cache load_cache()-> load_cache(all). load_cache(CacheAtom) when is_atom(CacheAtom)-> case CacheAtom of all-> %%读取计算所有的缓存 cache_model:init_sciense_cache(), cache_model:init_user_cache(); science-> cache_model:init_sciense_cache(); user-> cache_model:init_user_cache(); _-> ok end. %%-------------------------------------------------------------------- %% @doc %% Starts the server %% %% @end %%-------------------------------------------------------------------- -spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %%-------------------------------------------------------------------- %% @private %% @doc %% Initializes the server %% %% @spec init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -spec(init(Args :: term()) -> {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | {stop, Reason :: term()} | ignore). init([]) -> load_cache(), {ok, #state{}}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling call messages %% %% @end %%-------------------------------------------------------------------- -spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, State :: #state{}) -> {reply, Reply :: term(), NewState :: #state{}} | {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | {stop, Reason :: term(), NewState :: #state{}}). handle_call({reload,TableName},_From,State)-> load_cache(TableName), {reply, ok, State}; handle_call(_Request, _From, State) -> {reply, ok, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling cast messages %% %% @end %%-------------------------------------------------------------------- -spec(handle_cast(Request :: term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}). handle_cast(_Request, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling all non call/cast messages %% %% @spec handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% @end %%-------------------------------------------------------------------- -spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}). handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_server terminates %% with Reason. The return value is ignored. %% %% @spec terminate(Reason, State) -> void() %% @end %%-------------------------------------------------------------------- -spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), State :: #state{}) -> term()). terminate(_Reason, _State) -> ok. %%-------------------------------------------------------------------- %% @private %% @doc %% Convert process state when code is changed %% %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} %% @end %%-------------------------------------------------------------------- -spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, Extra :: term()) -> {ok, NewState :: #state{}} | {error, Reason :: term()}). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%===================================================================
game_db_queue.erl
%%%------------------------------------------------------------------- %%% @author 李世铭 %%% @copyright (C) Mar,31th 2016, <COMPANY> %%% @doc %%% 维护一个队列,redis=>mysql同步数据用的队列 %%% 包含以下功能: %%% 1、入队 %%% 2、出队 %%% 3、获取队列余量 %%% 使用genserver来保证队列单线程, %%% 使用redis的队列来保证数据不会丢失太多。 %%% @end %%% Created : 31. 三月 2016 17:18 %%%------------------------------------------------------------------- -module(game_db_queue). -author("Administrator"). -behaviour(gen_server). -include("db_config.hrl"). %% API -export([start_link/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([enqueue/1,dequeue/0,get_queue_len/0]). -define(SERVER, ?MODULE). -record(state, {}). %%%=================================================================== %%% API %%%=================================================================== %%入队操作 enqueue(DbMsg) -> gen_server:call(?MODULE, {enqueue,DbMsg}). %%出队操作 dequeue()-> gen_server:call(?MODULE,{dequeue}). %%获取队列长度 get_queue_len()-> gen_server:call(?MODULE,{get_queue_len}). %%-------------------------------------------------------------------- %% @doc %% Starts the server %% %% @end %%-------------------------------------------------------------------- -spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %%-------------------------------------------------------------------- %% @private %% @doc %% Initializes the server %% %% @spec init(Args) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -spec(init(Args :: term()) -> {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | {stop, Reason :: term()} | ignore). init([]) -> {ok, #state{}}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling call messages %% %% @end %%-------------------------------------------------------------------- -spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, State :: #state{}) -> {reply, Reply :: term(), NewState :: #state{}} | {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | {stop, Reason :: term(), NewState :: #state{}}). handle_call({enqueue,DbMsg},_From,State)-> SzDbMsg = db_utility:pack_data(DbMsg), Result = redis:lpush(?MYSQL_WRITE_LIST,SzDbMsg), {reply,Result,State}; handle_call({dequeue},_From,State)-> SzDbMsg = redis:rpop(?MYSQL_WRITE_LIST), Result = db_utility:unpack_data(SzDbMsg), {reply,Result,State}; handle_call({get_queue_len},_From,State)-> Result = redis:llen(?MYSQL_WRITE_LIST), {reply,Result,State}; handle_call(_Request, _From, State) -> {reply, ok, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling cast messages %% %% @end %%-------------------------------------------------------------------- -spec(handle_cast(Request :: term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}). handle_cast(_Request, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handling all non call/cast messages %% %% @spec handle_info(Info, State) -> {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% @end %%-------------------------------------------------------------------- -spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> {noreply, NewState :: #state{}} | {noreply, NewState :: #state{}, timeout() | hibernate} | {stop, Reason :: term(), NewState :: #state{}}). handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_server terminates %% with Reason. The return value is ignored. %% %% @spec terminate(Reason, State) -> void() %% @end %%-------------------------------------------------------------------- -spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), State :: #state{}) -> term()). terminate(_Reason, _State) -> ok. %%-------------------------------------------------------------------- %% @private %% @doc %% Convert process state when code is changed %% %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} %% @end %%-------------------------------------------------------------------- -spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, Extra :: term()) -> {ok, NewState :: #state{}} | {error, Reason :: term()}). code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%===================================================================
game_db_writer的代码想了想还是先不放了,下周修改下再说。
最后顺手拍下我们颇有个性的墙绘。