优化 排行榜使用redis缓存

This commit is contained in:
luying
2020-11-20 19:47:13 +08:00
parent d51a7119af
commit 1145d95889
6 changed files with 188 additions and 28 deletions

View File

@@ -8,7 +8,7 @@ import {
RouteRecord
} from 'pinus';
import * as mongoose from 'mongoose';
import * as redis from 'redis';
import * as redisService from './app/services/redisService';
import './app/servers/user.rpc.define'
import * as routeUtil from './app/util/routeUtil';
import { preload } from './preload';
@@ -55,17 +55,7 @@ mongoose.connect(mongoAddr, { useNewUrlParser: true, useUnifiedTopology: true },
}
});
// 创建 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);
redisService.initRank();
/**
* Init app for client.
*/

View File

@@ -1,5 +1,5 @@
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 { HangUpSpdUpRecModel } from './../../../db/HangUpSpdUpRec';
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 { handleFixedReward, handleReward } from '../../../services/rewardService';
import { checkBattleHeroes } from '../../../services/normalBattleService';
import { getRank, setRank } from '../../../services/redisService';
export default function(app: Application) {
return new TowerBattleHandler(app);
@@ -29,7 +30,11 @@ export class TowerBattleHandler {
let { towerLv } = await RoleModel.findByRoleId(roleId);
if (!towerLv) {
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);
if (!towerRec) {
@@ -303,17 +308,7 @@ export class TowerBattleHandler {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let ranks = await RoleModel.getRank('tower', ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']);
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
}
}
let {ranks, myRank} = await getRank(REDIS_KEY.TOWER_RANK, roleId);
if(!myRank) {
let role = await RoleModel.findByRoleId(roleId);
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 });
}
}

View File

@@ -1,7 +1,7 @@
import { HeroModel } from './../db/Hero';
import { HangUpRecordModel } from './../db/HangUpRecord';
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 { TowerRecordModel } from './../db/TowerRecord';
import { RoleModel } from './../db/Role';
@@ -11,6 +11,7 @@ import { handleFixedReward } from './rewardService';
import { STATUS } from '../consts/statusCode';
import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec';
import { TowerTaskRecModel } from '../db/TowerTaskRec';
import { setRank } from './redisService';
export async function checkTowerWar(roleId: string, battleId: number, heroes: Array<number>) {
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 towerReward = null;
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);
if (nextTowerInfo) {
const { warArray } = nextTowerInfo;

View File

@@ -2,6 +2,7 @@
import { HeroModel } from '../db/Hero';
import Role, { RoleModel } from '../db/Role'
import { getLvByExp, getExpByLv } from '../pubUtils/gamedata';
import { redisUserInfoUpdate } from './redisService';
export async function roleLevelup(roleId: string, kingExp: number) {
let role = await RoleModel.findByRoleId(roleId);
@@ -9,6 +10,9 @@ export async function roleLevelup(roleId: string, kingExp: number) {
let newExp = exp + kingExp;
let newLv = getLvByExp(newExp);
await RoleModel.levelup(roleId, newLv, newExp);
if(newLv > lv) { // 升级
await redisUserInfoUpdate(roleId, [{field: 'lv', value: newLv}])
}
let actordata = [];
for(let i = lv; i <= newLv; i++) {
let {sum, cur} = getExpByLv(i);

View File

@@ -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]);
});
}

View File

@@ -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_DROP = 10;
export const REDIS_KEY = {
USER_INFO: "userInfo", // 玩家缓存信息
TOWER_RANK: "towerRank" // 天梯排行榜
}