diff --git a/include/ranks.hrl b/include/ranks.hrl index 3710f32..54b1f6f 100644 --- a/include/ranks.hrl +++ b/include/ranks.hrl @@ -1,13 +1,26 @@ -ifndef(RANKS_H_). -define(RANKS_H_, true). --record(rank_info, { +%% 相关配置模块名 +-define(ranksCfg, ranksCfg). + +%% 工作者数量 +-define(workCnt, workCnt). + +%% 三元表达式 +-define(IIF(Cond, Ret1, Ret2), (case Cond of true -> Ret1; _ -> Ret2 end)). + +-define(etsRankInfo, etsRankInfo). + +%% 排行榜类型 需要与该定义中的 rankTypeXScore 名字一样 +-record(etsRankRecord, { key :: integer() - , otherInfo :: term() - , score1 :: term() - , score2 :: term() - , scoreN :: term() + , publicInfo :: term() + , rankType1Score :: term() + , rankType2Score :: term() + , rankTypeNScore :: term() }). +-define(RankRecordFields, record_info(fields, etsRankRecord)). -endif. diff --git a/src/rank/rankMgr.erl b/src/rank/rankMgr.erl index 0b8037e..edad5a3 100644 --- a/src/rank/rankMgr.erl +++ b/src/rank/rankMgr.erl @@ -2,4 +2,49 @@ -behavior(gen_srv). --export([]). +-include("ranks.hrl"). + +-export([ + start_link/0 +]). + +-export([ + init/1 + , handleCall/3 + , handleCast/2 + , handleInfo/2 + , terminate/2 + , code_change/3 +]). + +-define(SERVER, ?MODULE). +-record(state, {}). + +%% ******************************************** API ******************************************************************* +start_link() -> + gen_srv:start_link({local, ?SERVER}, ?MODULE, [], []). + +%% ******************************************** callback ************************************************************** +init(_Args) -> + ets:new(?etsRankInfo, [set, public, named_table, {keypos, #etsRankRecord.key}, {write_concurrency, auto}, {read_concurrency, true}]), + {ok, #state{}}. + +handleCall({mInitRank, RankType}, _State, _FROM) -> + ets:new(RankType, [ordered_set, public, named_table, {write_concurrency, auto}, {read_concurrency, true}]), + {reply, ok}; +handleCall(_Msg, _State, _FROM) -> + {reply, ok}. + +%% 默认匹配 +handleCast(_Msg, _State) -> + kpS. + +handleInfo(_Msg, _State) -> + kpS. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +%% ****************************************************** logic ******************************************************** diff --git a/src/rank/rankWork.erl b/src/rank/rankWork.erl index 1f05884..b6eb6ce 100644 --- a/src/rank/rankWork.erl +++ b/src/rank/rankWork.erl @@ -2,4 +2,43 @@ -behavior(gen_srv). --export([]). +-export([ + start_link/1 +]). + +-export([ + init/1 + , handleCall/3 + , handleCast/2 + , handleInfo/2 + , terminate/2 + , code_change/3 +]). + +-record(state, {}). + +%% ******************************************** API ******************************************************************* +start_link(SrvName) -> + gen_srv:start_link({local, SrvName}, ?MODULE, [], []). + +%% ******************************************** callback ************************************************************** +init(_Args) -> + {ok, #state{}}. + +handleCall(_Msg, _State, _FROM) -> + {reply, ok}; +handleCall(_Msg, _State, _FROM) -> + {reply, ok}. + +handleCast(_Msg, _State) -> + kpS. + +handleInfo(_Msg, _State) -> + kpS. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +%% ****************************************************** logic ******************************************************** diff --git a/src/rank/rankWork_sup.erl b/src/rank/rankWork_sup.erl new file mode 100644 index 0000000..5b4f7c5 --- /dev/null +++ b/src/rank/rankWork_sup.erl @@ -0,0 +1,26 @@ +-module(rankWork_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +init([]) -> + SupFlags = #{strategy => simple_one_for_one, intensity => 100, period => 3600}, + ChildSpecs = [ + #{ + id => rankWork, + start => {rankWork, start_link, []}, + restart => permanent, + shutdown => 3000, + type => worker, + modules => [rankWork] + } + ], + {ok, {SupFlags, ChildSpecs}}. diff --git a/src/rank/rsKvsToBeam.erl b/src/rank/rsKvsToBeam.erl new file mode 100644 index 0000000..597a7c1 --- /dev/null +++ b/src/rank/rsKvsToBeam.erl @@ -0,0 +1,42 @@ +-module(rsKvsToBeam). + +-export([ + load/2 +]). + +%% 注意 map类型的数据不能当做key +-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple(). +-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map(). + +-spec load(term(), [{key(), value()}]) -> ok. +load(Module, KVs) -> + Forms = forms(Module, KVs), + {ok, Module, Bin} = compile:forms(Forms), + code:soft_purge(Module), + {module, Module} = code:load_binary(Module, atom_to_list(Module), Bin), + ok. + +forms(Module, KVs) -> + %% -module(Module). + Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]), + %% -export([getV/0]). + ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getV), erl_syntax:integer(1))], + Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]), + %% getV(K) -> V + Function = erl_syntax:function(erl_syntax:atom(getV), lookup_clauses(KVs, [])), + [erl_syntax:revert(X) || X <- [Mod, Export, Function]]. + +lookup_clause(Key, Value) -> + Var = erl_syntax:abstract(Key), + Body = erl_syntax:abstract(Value), + erl_syntax:clause([Var], [], [Body]). + +lookup_clause_anon() -> + Var = erl_syntax:variable("_"), + Body = erl_syntax:atom(undefined), + erl_syntax:clause([Var], [], [Body]). + +lookup_clauses([], Acc) -> + lists:reverse(lists:flatten([lookup_clause_anon() | Acc])); +lookup_clauses([{Key, Value} | T], Acc) -> + lookup_clauses(T, [lookup_clause(Key, Value) | Acc]). \ No newline at end of file diff --git a/src/ranks.erl b/src/ranks.erl index 2e6b17a..eed7cc1 100644 --- a/src/ranks.erl +++ b/src/ranks.erl @@ -1,5 +1,7 @@ -module(ranks). +-include("ranks.hrl"). + -export([ start/0 , stop/0 @@ -7,7 +9,7 @@ , initRank/1 %% 创建新的排行榜 , updateScore/3 %% 更新分数 , updateInfo/2 %% 更新其他分数 - , getRankInfo/3 %% 获取排行某页的信息 + , getRankInfo/4 %% 获取排行某页的信息 ]). start() -> @@ -16,18 +18,46 @@ start() -> stop() -> application:stop(ranks). -startWork(Cnt) -> - ok. +workName(Idx) -> + binary_to_atom(<<"$rankWork_", (integer_to_binary(Idx))/binary>>). + +fieldIdx([], _Idx, Acc) -> + Acc; +fieldIdx([Field | Fields], Idx, Acc) -> + fieldIdx(Fields, Idx + 1, [{Field, Idx} | Acc]). + +-spec startWork(Cnt :: non_neg_integer()) -> ok | {error, term()}. +startWork(Cnt) when Cnt > 0 -> + case ?ranksCfg:getV(?workCnt) of + 0 -> + NameList = [{Idx, workName(Idx)} || Idx <- lists:seq(1, Cnt)], + [supervisor:start_child(rankWork_sup, [WorkName]) || {_Idx, WorkName} <- NameList], + CfgList = [{?workCnt, Cnt} | NameList], + Fields = record_info(fields, etsRankRecord), + gtKvsToBeam:load(?ranksCfg, fieldIdx(Fields, 1, CfgList)), + ok; + _Cnt -> + {error, started} + end. initRank(RankType) -> - ok. + gen_srv:call(rankMgr, {mInitRank, RankType}). +%% 根据排行榜类型更新分数 需要根据key 分配到指定的排行榜工程进程执行 updateScore(RankType, Key, Score) -> - ok. + WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt)) + 1), + %% 这里就看业务层面是否需要同步更新分数了 需要根据key 分配到指定的排行榜工程进程执行 + RankPos = ?ranksCfg:getV(RankType), + gen_srv:cast(WorkName, {mUpdateScore, RankPos, Score}). -updateInfo(Key, Infos) -> - ok. +%% 更新非排行榜分数的其他其他字段的信息 需要根据key 分配到指定的排行榜工程进程执行 +updateInfo(Key, RecordKvs) -> + WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt)) + 1), + %% 这里就看业务层面是否需要同步更新信息了 + gen_srv:cast(WorkName, {mUpdateInfo, RecordKvs}). + +%% 根据排行榜类型 获取指定数量 指定页的排行榜的信息 不需要在排行榜工作进程执行 +getRankInfo(RankType, Cnt, Page, PageInfo) -> -getRankInfo(RankType, Cnt, PageInfo) -> ok. diff --git a/src/ranks_app.erl b/src/ranks_app.erl index 86908ea..1167e6a 100644 --- a/src/ranks_app.erl +++ b/src/ranks_app.erl @@ -2,9 +2,12 @@ -behaviour(application). +-include("ranks.hrl"). + -export([start/2, stop/1]). start(_StartType, _StartArgs) -> + gtKvsToBeam:load(?ranksCfg, [{?workCnt, 0}]), ranks_sup:start_link(). stop(_State) -> diff --git a/src/ranks_sup.erl b/src/ranks_sup.erl index 27315c4..01ebac8 100644 --- a/src/ranks_sup.erl +++ b/src/ranks_sup.erl @@ -11,20 +11,24 @@ start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). -%% sup_flags() = #{strategy => strategy(), % optional -%% intensity => non_neg_integer(), % optional -%% period => pos_integer()} % optional -%% child_spec() = #{id => child_id(), % mandatory -%% start => mfargs(), % mandatory -%% restart => restart(), % optional -%% shutdown => shutdown(), % optional -%% type => worker(), % optional -%% modules => modules()} % optional init([]) -> - SupFlags = #{strategy => one_for_all, - intensity => 0, - period => 1}, - ChildSpecs = [], + SupFlags = #{strategy => one_for_one, intensity => 0, period => 1}, + ChildSpecs = [ + #{ + id => rankMgr, + start => {rankMgr, start_link, []}, + restart => permanent, + shutdown => 3000, + type => worker, + modules => [rankMgr] + }, + #{ + id => rankWork_sup, + start => {rankWork_sup, start_link, []}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [rankWork_sup] + } + ], {ok, {SupFlags, ChildSpecs}}. - -%% internal functions