优化 排行榜使用redis缓存
This commit is contained in:
+2
-12
@@ -8,7 +8,7 @@ import {
|
|||||||
RouteRecord
|
RouteRecord
|
||||||
} from 'pinus';
|
} from 'pinus';
|
||||||
import * as mongoose from 'mongoose';
|
import * as mongoose from 'mongoose';
|
||||||
import * as redis from 'redis';
|
import * as redisService from './app/services/redisService';
|
||||||
import './app/servers/user.rpc.define'
|
import './app/servers/user.rpc.define'
|
||||||
import * as routeUtil from './app/util/routeUtil';
|
import * as routeUtil from './app/util/routeUtil';
|
||||||
import { preload } from './preload';
|
import { preload } from './preload';
|
||||||
@@ -55,17 +55,7 @@ mongoose.connect(mongoAddr, { useNewUrlParser: true, useUnifiedTopology: true },
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建 redis 连接
|
redisService.initRank();
|
||||||
const client = redis.createClient(6379, redisArr, {detect_buffers: true});
|
|
||||||
client.auth('zyz_2020', (err, reply) => {
|
|
||||||
if (err) {
|
|
||||||
console.log('redis err', err);
|
|
||||||
} else {
|
|
||||||
console.log('redis suc');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client.set('hello', 'redis', redis.print);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init app for client.
|
* Init app for client.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { STATUS } from './../../../consts/statusCode';
|
import { STATUS } from './../../../consts/statusCode';
|
||||||
import { HANG_UP_CONSTS, GOLD_COST_RATIO, TOWER_TASK_CONST } from './../../../consts/consts';
|
import { HANG_UP_CONSTS, GOLD_COST_RATIO, TOWER_TASK_CONST, REDIS_KEY } from './../../../consts/consts';
|
||||||
import { TowerTaskRecModel } from './../../../db/TowerTaskRec';
|
import { TowerTaskRecModel } from './../../../db/TowerTaskRec';
|
||||||
import { HangUpSpdUpRecModel } from './../../../db/HangUpSpdUpRec';
|
import { HangUpSpdUpRecModel } from './../../../db/HangUpSpdUpRec';
|
||||||
import { HangUpRecordModel } from './../../../db/HangUpRecord';
|
import { HangUpRecordModel } from './../../../db/HangUpRecord';
|
||||||
@@ -11,6 +11,7 @@ import { decodeArrayStr, resResult, shouldRefresh, calculateNum, getRefTime, gen
|
|||||||
import { calcuHangUpReward, checkTaskConditions, checkHangUpSpdUpCnt, createCurTasks, treatTask, getRemainTime, getTasksReward, getTaskStatus, getDoingOrWaitingTasks } from '../../../services/battleService';
|
import { calcuHangUpReward, checkTaskConditions, checkHangUpSpdUpCnt, createCurTasks, treatTask, getRemainTime, getTasksReward, getTaskStatus, getDoingOrWaitingTasks } from '../../../services/battleService';
|
||||||
import { handleFixedReward, handleReward } from '../../../services/rewardService';
|
import { handleFixedReward, handleReward } from '../../../services/rewardService';
|
||||||
import { checkBattleHeroes } from '../../../services/normalBattleService';
|
import { checkBattleHeroes } from '../../../services/normalBattleService';
|
||||||
|
import { getRank, setRank } from '../../../services/redisService';
|
||||||
|
|
||||||
export default function(app: Application) {
|
export default function(app: Application) {
|
||||||
return new TowerBattleHandler(app);
|
return new TowerBattleHandler(app);
|
||||||
@@ -29,7 +30,11 @@ export class TowerBattleHandler {
|
|||||||
let { towerLv } = await RoleModel.findByRoleId(roleId);
|
let { towerLv } = await RoleModel.findByRoleId(roleId);
|
||||||
if (!towerLv) {
|
if (!towerLv) {
|
||||||
towerLv = 1;
|
towerLv = 1;
|
||||||
await RoleModel.towerLvUp(roleId);
|
let role = await RoleModel.towerLvUp(roleId);
|
||||||
|
// 更新redis
|
||||||
|
let { roleName, towerUpTime, lv, vLv } = role;
|
||||||
|
await setRank(REDIS_KEY.TOWER_RANK, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
|
||||||
|
|
||||||
}
|
}
|
||||||
let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv);
|
let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv);
|
||||||
if (!towerRec) {
|
if (!towerRec) {
|
||||||
@@ -303,17 +308,7 @@ export class TowerBattleHandler {
|
|||||||
let roleId = session.get('roleId');
|
let roleId = session.get('roleId');
|
||||||
let roleName = session.get('roleName');
|
let roleName = session.get('roleName');
|
||||||
|
|
||||||
let ranks = await RoleModel.getRank('tower', ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']);
|
let {ranks, myRank} = await getRank(REDIS_KEY.TOWER_RANK, roleId);
|
||||||
let resultRanks = ranks.map((cur, index) => {
|
|
||||||
let { roleId, roleName, towerLv, lv, vLv } = cur;
|
|
||||||
return {rank: index+1, roleId, roleName, lv, vLv, guildName: "", head: "zhaoyun", num: towerLv, str: ''}
|
|
||||||
});
|
|
||||||
let myRank = null;
|
|
||||||
for(let r of resultRanks) {
|
|
||||||
if(r.roleId == roleId) {
|
|
||||||
myRank = r; break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!myRank) {
|
if(!myRank) {
|
||||||
let role = await RoleModel.findByRoleId(roleId);
|
let role = await RoleModel.findByRoleId(roleId);
|
||||||
let {towerLv, lv, vLv} = role;
|
let {towerLv, lv, vLv} = role;
|
||||||
@@ -322,6 +317,6 @@ export class TowerBattleHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resResult(STATUS.SUCCESS, { ranks: resultRanks, myRank });
|
return resResult(STATUS.SUCCESS, { ranks, myRank });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { HeroModel } from './../db/Hero';
|
import { HeroModel } from './../db/Hero';
|
||||||
import { HangUpRecordModel } from './../db/HangUpRecord';
|
import { HangUpRecordModel } from './../db/HangUpRecord';
|
||||||
import { ChannelService } from 'pinus';
|
import { ChannelService } from 'pinus';
|
||||||
import { HANG_UP_CONSTS, TOWER_TASK_CONST } from './../consts/consts';
|
import { HANG_UP_CONSTS, TOWER_TASK_CONST, REDIS_KEY } from './../consts/consts';
|
||||||
import { BattleRecordModel } from './../db/BattleRecord';
|
import { BattleRecordModel } from './../db/BattleRecord';
|
||||||
import { TowerRecordModel } from './../db/TowerRecord';
|
import { TowerRecordModel } from './../db/TowerRecord';
|
||||||
import { RoleModel } from './../db/Role';
|
import { RoleModel } from './../db/Role';
|
||||||
@@ -11,6 +11,7 @@ import { handleFixedReward } from './rewardService';
|
|||||||
import { STATUS } from '../consts/statusCode';
|
import { STATUS } from '../consts/statusCode';
|
||||||
import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec';
|
import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec';
|
||||||
import { TowerTaskRecModel } from '../db/TowerTaskRec';
|
import { TowerTaskRecModel } from '../db/TowerTaskRec';
|
||||||
|
import { setRank } from './redisService';
|
||||||
|
|
||||||
export async function checkTowerWar(roleId: string, battleId: number, heroes: Array<number>) {
|
export async function checkTowerWar(roleId: string, battleId: number, heroes: Array<number>) {
|
||||||
const battleIdStr = `${battleId}`;
|
const battleIdStr = `${battleId}`;
|
||||||
@@ -69,7 +70,11 @@ export async function towerBattleEnd(channelService: ChannelService, sid: string
|
|||||||
let newRec = await TowerRecordModel.updateRecord(roleId, towerLv, battleCode, battleId, heroes, inc);
|
let newRec = await TowerRecordModel.updateRecord(roleId, towerLv, battleCode, battleId, heroes, inc);
|
||||||
let towerReward = null;
|
let towerReward = null;
|
||||||
if (inc === 1) {
|
if (inc === 1) {
|
||||||
await RoleModel.towerLvUp(roleId);
|
let role = await RoleModel.towerLvUp(roleId);
|
||||||
|
// 更新redis
|
||||||
|
let { roleName, towerLv, towerUpTime, lv, vLv } = role;
|
||||||
|
await setRank(REDIS_KEY.TOWER_RANK, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
|
||||||
|
|
||||||
const nextTowerInfo = getTowerDataByLv(towerLv + 1);
|
const nextTowerInfo = getTowerDataByLv(towerLv + 1);
|
||||||
if (nextTowerInfo) {
|
if (nextTowerInfo) {
|
||||||
const { warArray } = nextTowerInfo;
|
const { warArray } = nextTowerInfo;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { HeroModel } from '../db/Hero';
|
import { HeroModel } from '../db/Hero';
|
||||||
import Role, { RoleModel } from '../db/Role'
|
import Role, { RoleModel } from '../db/Role'
|
||||||
import { getLvByExp, getExpByLv } from '../pubUtils/gamedata';
|
import { getLvByExp, getExpByLv } from '../pubUtils/gamedata';
|
||||||
|
import { redisUserInfoUpdate } from './redisService';
|
||||||
|
|
||||||
export async function roleLevelup(roleId: string, kingExp: number) {
|
export async function roleLevelup(roleId: string, kingExp: number) {
|
||||||
let role = await RoleModel.findByRoleId(roleId);
|
let role = await RoleModel.findByRoleId(roleId);
|
||||||
@@ -9,6 +10,9 @@ export async function roleLevelup(roleId: string, kingExp: number) {
|
|||||||
let newExp = exp + kingExp;
|
let newExp = exp + kingExp;
|
||||||
let newLv = getLvByExp(newExp);
|
let newLv = getLvByExp(newExp);
|
||||||
await RoleModel.levelup(roleId, newLv, newExp);
|
await RoleModel.levelup(roleId, newLv, newExp);
|
||||||
|
if(newLv > lv) { // 升级
|
||||||
|
await redisUserInfoUpdate(roleId, [{field: 'lv', value: newLv}])
|
||||||
|
}
|
||||||
let actordata = [];
|
let actordata = [];
|
||||||
for(let i = lv; i <= newLv; i++) {
|
for(let i = lv; i <= newLv; i++) {
|
||||||
let {sum, cur} = getExpByLv(i);
|
let {sum, cur} = getExpByLv(i);
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { RoleModel } from "../db/Role";
|
||||||
|
import * as redis from 'redis';
|
||||||
|
import {REDIS_KEY} from '../consts/consts'
|
||||||
|
|
||||||
|
let redisArr = 'r-8vb4i2kgl91886fkxd.redis.zhangbei.rds.aliyuncs.com';
|
||||||
|
if(process.env.NODE_ENV == 'local') {
|
||||||
|
redisArr = '127.0.0.1';
|
||||||
|
}
|
||||||
|
// 创建 redis 连接
|
||||||
|
const client = redis.createClient(6379, redisArr, {detect_buffers: true});
|
||||||
|
client.auth('zyz_2020', (err, reply) => {
|
||||||
|
if (err) {
|
||||||
|
console.log('redis err', err);
|
||||||
|
} else {
|
||||||
|
console.log('redis suc');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
client.set('hello', 'redis', redis.print);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在服务重新启动时,将信息存入redis
|
||||||
|
*/
|
||||||
|
export async function initRank() {
|
||||||
|
await redisDel(REDIS_KEY.TOWER_RANK); // 清空天梯排行榜(ZSet)
|
||||||
|
await redisDel(REDIS_KEY.USER_INFO); // 清空用户缓存信息
|
||||||
|
let ranks = await RoleModel.getRank('tower', ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']);
|
||||||
|
for(let {towerLv, roleId, roleName, lv, vLv, towerUpTime} of ranks) {
|
||||||
|
await redisZadd(REDIS_KEY.TOWER_RANK, encodeScoreWithTime(towerLv, towerUpTime?towerUpTime.getTime():0), roleId);
|
||||||
|
await redisUserInfoAdd(roleId, {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新玩家信息
|
||||||
|
export async function redisUserInfoUpdate(roleId: string, arr: Array<{field: string, value:(string|number)}>) {
|
||||||
|
let params = await redisHget(REDIS_KEY.USER_INFO, roleId);
|
||||||
|
if(params) {
|
||||||
|
let obj = JSON.parse(params);
|
||||||
|
for(let {field, value} of arr) {
|
||||||
|
obj[field] = value;
|
||||||
|
}
|
||||||
|
return await redisHset(REDIS_KEY.USER_INFO, roleId, JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加玩家信息缓存
|
||||||
|
export async function redisUserInfoAdd(roleId: string, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) {
|
||||||
|
let value = JSON.stringify(params);
|
||||||
|
return await redisHset(REDIS_KEY.USER_INFO, roleId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新排行榜
|
||||||
|
export async function setRank(key: string, roleId: string, score: number, timestamp: number, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) {
|
||||||
|
// 更新分数
|
||||||
|
const _score = encodeScoreWithTime(score, timestamp);
|
||||||
|
await redisZadd(key, _score, roleId);
|
||||||
|
// 移除100名以外
|
||||||
|
await redisZremRangeByRank(key, 0, 100);
|
||||||
|
// 如果没有信息,更新玩家信息
|
||||||
|
const hasCurUser = await redisHexists(REDIS_KEY.USER_INFO, roleId);
|
||||||
|
if(!hasCurUser) {
|
||||||
|
await redisUserInfoAdd(roleId, params);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取排行榜
|
||||||
|
export async function getRank(key: string, roleId: string) {
|
||||||
|
let ranks = [], myRank = null;
|
||||||
|
const rankFromDb = await redisZrevrangeByScore(key, '+inf', '-inf', true, 100);
|
||||||
|
for(let ii = 0; ii < rankFromDb.length; ii+=2) {
|
||||||
|
const _roleId = rankFromDb[ii];
|
||||||
|
const _score = decodeScoreWithTime(rankFromDb[ii + 1]);
|
||||||
|
const userInfo = await redisHget(REDIS_KEY.USER_INFO, _roleId);
|
||||||
|
const _userInfo = JSON.parse(userInfo);
|
||||||
|
const tmp = {..._userInfo, roleId: _roleId, num: _score, rank: ii%2+1}
|
||||||
|
ranks.push(tmp);
|
||||||
|
if(roleId == _roleId) myRank = tmp;
|
||||||
|
}
|
||||||
|
return {ranks, myRank}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有序排行综合时间和得分排序
|
||||||
|
function encodeScoreWithTime(score: number, timestamp: number): number {
|
||||||
|
// value = score * Math.power(10, 14) + max_time - timestamp
|
||||||
|
let timelen = 13;
|
||||||
|
let pow = Math.pow(10, timelen + 1);
|
||||||
|
return score * pow + pow - 1 - timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeScoreWithTime(num: string): number {
|
||||||
|
let timelen = 13;
|
||||||
|
let pow = Math.pow(10, timelen + 1);
|
||||||
|
let _num = parseInt(num);
|
||||||
|
return Math.floor(_num/pow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////// redis方法
|
||||||
|
|
||||||
|
//// key
|
||||||
|
|
||||||
|
// 在 key 存在时删除 key。
|
||||||
|
export async function redisDel(key: string) {
|
||||||
|
return await createPromise('del', [key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 有序集合
|
||||||
|
|
||||||
|
// 返回有序集中指定分数区间内的成员,分数从高到低排序
|
||||||
|
export async function redisZrevrangeByScore(key: string, max: (number|string), min: (number|string), withscores: boolean, limit?:number, offset=0): Promise<Array<string>> {
|
||||||
|
let param = [key, max, min];
|
||||||
|
if(withscores) param.push('WITHSCORES');
|
||||||
|
if(limit) param.push('LIMIT', offset, limit);
|
||||||
|
return await createPromise('zrevrangebyscore', param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向有序集合添加一个或多个成员,或者更新已存在成员的分数
|
||||||
|
export async function redisZadd(key: string, field: number, value: string) {
|
||||||
|
return await createPromise('zadd', [key, field, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取有序集合的成员数
|
||||||
|
export async function redisZcard(key: string) {
|
||||||
|
return await createPromise('zcard', [key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除有序集合中给定的排名区间的所有成员
|
||||||
|
export async function redisZremRangeByRank(key: string, start: number, stop: number) {
|
||||||
|
return await createPromise('zremrangebyrank', [key, start, stop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 哈希
|
||||||
|
|
||||||
|
// 将哈希表 key 中的字段 field 的值设为 value
|
||||||
|
export async function redisHset(key: string, field: string, value: string) {
|
||||||
|
return await createPromise('hset', [key, field, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取存储在哈希表中指定字段的值。
|
||||||
|
export async function redisHget(key: string, field: string) {
|
||||||
|
return await createPromise('hget', [key, field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取存储在哈希表中指定字段的值。
|
||||||
|
export async function redisHexists(key: string, field: string) {
|
||||||
|
return await createPromise('hexists', [key, field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
///// 封装promise
|
||||||
|
function createPromise(type: string, params: Array<string|number|any>):Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const cb = function(err: Error, result: any) {
|
||||||
|
if(err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
client[type].apply(client, [...params, cb]);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -255,3 +255,8 @@ export const COM_BATTLE_ROBOT_HURT_RAND_RATE = 0.05;
|
|||||||
export const COM_BATTLE_FRDCNT_MAX = 100;
|
export const COM_BATTLE_FRDCNT_MAX = 100;
|
||||||
// 情谊点掉落数量
|
// 情谊点掉落数量
|
||||||
export const COM_BATTLE_FRDCNT_DROP = 10;
|
export const COM_BATTLE_FRDCNT_DROP = 10;
|
||||||
|
|
||||||
|
export const REDIS_KEY = {
|
||||||
|
USER_INFO: "userInfo", // 玩家缓存信息
|
||||||
|
TOWER_RANK: "towerRank" // 天梯排行榜
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user