之前数据库读写模型的实现

Posted 李世铭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了之前数据库读写模型的实现相关的知识,希望对你有一定的参考价值。

之前有个数据库读写的设想,然后就实现了一下,实现花的时间比预期的要长,昨天没有弄完,今天又加班弄了一会。但还是有很多很不满意的地方,包括测试也没有做到位。明天回家看孩子,再整就得下周二了,到时候再好好调整下。

技术分享

重点看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的代码想了想还是先不放了,下周修改下再说。 
最后顺手拍下我们颇有个性的墙绘。
技术分享

以上是关于之前数据库读写模型的实现的主要内容,如果未能解决你的问题,请参考以下文章

Cg入门20:Fragment shader - 片段级模型动态变色(实现汽车动态换漆)

ReentrantReadWriteLock场景应用

在导航抽屉关闭之前加载片段

NIO实现Reactor单线程模型

通过spring抽象路由数据源+MyBatis拦截器实现数据库自动读写分离

AJAX相关JS代码片段和部分浏览器模型