From e981366609d987c763c3ac04d0da87f7e9e2cfd8 Mon Sep 17 00:00:00 2001 From: SisMaker <156736github> Date: Sun, 10 Apr 2022 17:35:27 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ranks.hrl | 11 +++- src/rank/rankMgr.erl | 18 ++++-- src/rank/rankWork.erl | 136 +++++++++++++++++++++++++++++++++++++++++- src/ranks.erl | 21 ++++--- 4 files changed, 169 insertions(+), 17 deletions(-) diff --git a/include/ranks.hrl b/include/ranks.hrl index 54b1f6f..f5de77a 100644 --- a/include/ranks.hrl +++ b/include/ranks.hrl @@ -3,10 +3,14 @@ %% 相关配置模块名 -define(ranksCfg, ranksCfg). +-define(ranksLimit, ranksLimit). %% 工作者数量 -define(workCnt, workCnt). +%% 公共信息字段 +-define(publicInfoPos, #etsRankRecord.publicInfo). + %% 三元表达式 -define(IIF(Cond, Ret1, Ret2), (case Cond of true -> Ret1; _ -> Ret2 end)). @@ -21,6 +25,11 @@ , rankTypeNScore :: term() }). --define(RankRecordFields, record_info(fields, etsRankRecord)). +-record(rankInfo, { + rank :: integer() + , key :: integer() + , publicInfo :: term() + , rankTypeScore :: term() +}). -endif. diff --git a/src/rank/rankMgr.erl b/src/rank/rankMgr.erl index edad5a3..2ee03c6 100644 --- a/src/rank/rankMgr.erl +++ b/src/rank/rankMgr.erl @@ -18,7 +18,6 @@ ]). -define(SERVER, ?MODULE). --record(state, {}). %% ******************************************** API ******************************************************************* start_link() -> @@ -27,11 +26,18 @@ start_link() -> %% ******************************************** 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}; + {ok, #{}}. + +handleCall({mInitRank, RankType, CntLimit, CntMax}, State, _FROM) -> + case State of + #{RankType := _} -> + {reply, {error, used}}; + _ -> + ets:new(RankType, [ordered_set, public, named_table, {write_concurrency, auto}, {read_concurrency, true}]), + NewState = State#{RankType => {CntLimit, CntMax}}, + gtKvsToBeam:load(?ranksLimit, maps:to_list(NewState)), + {reply, ok, NewState} + end; handleCall(_Msg, _State, _FROM) -> {reply, ok}. diff --git a/src/rank/rankWork.erl b/src/rank/rankWork.erl index b6eb6ce..13b445c 100644 --- a/src/rank/rankWork.erl +++ b/src/rank/rankWork.erl @@ -2,8 +2,13 @@ -behavior(gen_srv). +-include("ranks.hrl"). + -export([ start_link/1 + , mUpdateScore/3 + , mUpdateInfo/2 + , mGetRankInfo/5 ]). -export([ @@ -25,8 +30,6 @@ start_link(SrvName) -> init(_Args) -> {ok, #state{}}. -handleCall(_Msg, _State, _FROM) -> - {reply, ok}; handleCall(_Msg, _State, _FROM) -> {reply, ok}. @@ -42,3 +45,132 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ****************************************************** logic ******************************************************** + +mUpdateScore(Key, RankType, Score) -> + {_RankLimit, RankMax} = ?ranksLimit:getV(RankType), + RankPos = ?ranksCfg:getV(RankType), + case ets:lookup(etsRankInfo, Key) of + [RankRecord] -> + OldScore = element(RankPos, RankRecord), + ets:delete(RankType, OldScore), + + RankSize = ets:info(RankType, size), + case RankSize >= RankMax of + true -> + FirstKey = ets:first(RankType), + case Score > FirstKey of + true -> + ets:insert(RankType, Score), + ets:delete(RankType, FirstKey); + _ -> + ignore + end; + _ -> + ets:insert(RankType, Score) + end, + ets:update_element(etsRankInfo, Key, {RankPos, Score}); + _ -> + %% 插入新的数据 + RankSize = ets:info(RankType, size), + case RankSize >= RankMax of + true -> + FirstKey = ets:first(RankType), + case Score > FirstKey of + true -> + ets:insert(RankType, Score), + ets:delete(RankType, FirstKey), + NewRecord = #etsRankRecord{key = Key}, + RankRecord = setelement(RankPos, NewRecord, Score), + ets:insert(etsRankInfo, RankRecord); + _ -> + ignore + end; + _ -> + ets:insert(RankType, Score), + NewRecord = #etsRankRecord{key = Key}, + RankRecord = setelement(RankPos, NewRecord, Score), + ets:insert(etsRankInfo, RankRecord) + end + end, + {mayReply, ok}. + +mUpdateInfo(Key, RecordKvs) -> + ets:update_element(etsRankInfo, Key, RecordKvs), + {mayReply, ok}. + +mGetRankInfo(RankType, MyKey, Cnt, Page, PageInfo) -> + {RankLimit, _RankMax} = ?ranksLimit:getV(RankType), + + %% 请求第一页的时候 附加上自己的排名 + SelfRank = + case Page of + 0 -> + case ets:lookup_element(etsRankInfo, MyKey) of + [RankRecord] -> + RankPos = ?ranksCfg:getV(RankType), + CurScore = element(RankPos, RankRecord), + MyIndex = ets:select_count(RankType, [{{'$1', '$2'}, [{'>=', '$1', CurScore}], [true]}]), + max(RankLimit, MyIndex); + _ -> + -1 + end; + _ -> + 0 + end, + + MS = + case PageInfo == 0 orelse PageInfo of + true -> + [{'$1', [], ['$1']}]; + <<"">> -> + [{'$1', [], ['$1']}]; + _ -> + try binary_to_term(PageInfo) of + KeyTerm when KeyTerm == <<"">>; KeyTerm == "" -> + [{'$1', [], ['$1']}]; + KeyTerm -> + % ets:fun2ms(fun({K, _V} = T) when K < KeyTerm -> T end) + [{{'$1', '$2'}, [{'<', '$1', KeyTerm}], ['$_']}] + catch _C:_R -> + [{'$1', [], ['$1']}] + end + end, + + case ets:select_reverse(RankType, MS, Cnt) of + '$end_of_table' -> + {true, {<<"">>, []}}; + {KeyIds, '$end_of_table'} -> + {true, SelfRank, makeRankData(KeyIds, Cnt * Page + 1)}; + {KeyIds, _NextKey} -> + Length = length(KeyIds), + if + Length < Cnt -> + {true, SelfRank, makeRankData(KeyIds, Cnt * Page + 1)}; + true -> + NewCurCnt = Page * Cnt + Length, + case NewCurCnt >= RankLimit of + true -> + {true, SelfRank, makeRankData(KeyIds, Cnt * Page + 1)}; + _ -> + {false, SelfRank, makeRankData(KeyIds, Cnt * Page + 1)} + end + end + end. + +makeRankData(KeyIds, Idx) -> + makeRankData(KeyIds, Idx, <<"">>, []). + +makeRankData([], _Idx, LastKey, Acc) -> + case LastKey of + <<"">> -> + {LastKey, lists:reverse(Acc)}; + _ -> + {erlang:term_to_binary(LastKey), lists:reverse(Acc)} + end; +makeRankData([{CurKey, Score} | KeyIds], Idx, LastKey, Acc) -> + case ets:lookup(ets_pub_rank_info, CurKey) of + [OneData] -> + makeRankData(KeyIds, Idx + 1, CurKey, [#rankInfo{rank = Idx, key = CurKey, publicInfo = element(?publicInfoPos, OneData), rankTypeScore = Score} | Acc]); + _ -> + makeRankData(KeyIds, Idx, LastKey, Acc) + end. \ No newline at end of file diff --git a/src/ranks.erl b/src/ranks.erl index eed7cc1..704373b 100644 --- a/src/ranks.erl +++ b/src/ranks.erl @@ -6,7 +6,7 @@ start/0 , stop/0 , startWork/1 - , initRank/1 %% 创建新的排行榜 + , initRank/3 %% 创建新的排行榜 , updateScore/3 %% 更新分数 , updateInfo/2 %% 更新其他分数 , getRankInfo/4 %% 获取排行某页的信息 @@ -40,24 +40,29 @@ startWork(Cnt) when Cnt > 0 -> {error, started} end. -initRank(RankType) -> - gen_srv:call(rankMgr, {mInitRank, RankType}). +initRank(RankType, CntLimit, CntMax) -> + gen_srv:call(rankMgr, {mInitRank, RankType, CntLimit, CntMax}). %% 根据排行榜类型更新分数 需要根据key 分配到指定的排行榜工程进程执行 updateScore(RankType, Key, Score) -> WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt)) + 1), - %% 这里就看业务层面是否需要同步更新分数了 需要根据key 分配到指定的排行榜工程进程执行 RankPos = ?ranksCfg:getV(RankType), - gen_srv:cast(WorkName, {mUpdateScore, RankPos, Score}). + %% 这里就看业务层面是否需要同步更新分数了 + %% 同步请求 + %% gen_srv:clfn(WorkName, rank_work, mUpdateScore, [Key, RankPos, Score]), + %% 异步请求 + gen_srv:csfn(WorkName, rank_work, mUpdateScore, [Key, RankPos, Score]). %% 更新非排行榜分数的其他其他字段的信息 需要根据key 分配到指定的排行榜工程进程执行 updateInfo(Key, RecordKvs) -> WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt)) + 1), %% 这里就看业务层面是否需要同步更新信息了 - gen_srv:cast(WorkName, {mUpdateInfo, RecordKvs}). + %% 同步请求 + %% gen_srv:clfn(WorkName, rank_work, mUpdateInfo, [Key, RecordKvs]), + %% 异步请求 + gen_srv:csfn(WorkName, rank_work, mUpdateInfo, [Key, RecordKvs]). %% 根据排行榜类型 获取指定数量 指定页的排行榜的信息 不需要在排行榜工作进程执行 getRankInfo(RankType, Cnt, Page, PageInfo) -> - - ok. + rankWork:mGetRankInfo(RankType, Cnt, Page, PageInfo).