优化 排行榜使用redis缓存
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
161
game-server/app/services/redisService.ts
Normal file
161
game-server/app/services/redisService.ts
Normal 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]);
|
||||
});
|
||||
}
|
||||
@@ -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" // 天梯排行榜
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user