import { GOOD_QUALITY } from './../consts/consts'; import { RoleModel } from "../db/Role"; import * as Redis from 'redis'; import {REDIS_KEY} from '../consts/consts' import { GameModel } from "../db/Game"; import { promisifyAll } from 'bluebird'; // 映射 redis 接口 declare module 'redis' { export interface RedisClient extends NodeJS.EventEmitter { hdelAsync(...args: any[]): Promise; spopAsync(...args: any[]): Promise; sremAsync(...args: any[]): Promise; sismemberAsync(...args: any[]): Promise; srandmemberAsync(...args: any[]): Promise; delAsync(...args: any[]): Promise; } export interface Multi extends Commands { execAsync(...args: any[]): Promise; } } let redisArr = 'r-8vb4i2kgl91886fkxd.redis.zhangbei.rds.aliyuncs.com'; if(process.env.NODE_ENV == 'local') { redisArr = '127.0.0.1'; } // 创建 redis 连接 const oldRedisClient = Redis.createClient(6379, redisArr, {detect_buffers: true}); oldRedisClient.auth('zyz_2020', (err, reply) => { if (err) { console.log('redis err', err); } else { console.log('redis suc'); } }) // 转 promise const client = promisifyAll(oldRedisClient) as Redis.RedisClient; client.set('hello', 'redis', Redis.print); /** * 在服务重新启动时,将信息存入redis */ export async function initAllRank() { const serverList = await GameModel.getAllServerList(); await redisDel(REDIS_KEY.USER_INFO); for(let {id} of serverList) { await redisDel(getKeyName(REDIS_KEY.TOWER_RANK, id)); await initRank(id); } } export async function initRank(serverId: number) { // console.log('*****', 'initRank') await redisExpire(getKeyName(REDIS_KEY.TOWER_RANK, serverId), 30 * 24 * 60 * 60); await redisExpire(REDIS_KEY.USER_INFO, 30 * 24 * 60 * 60); let ranks = await RoleModel.getRank('tower', serverId, ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']); for(let {towerLv, roleId, roleName, lv, vLv, towerUpTime} of ranks) { // console.log(roleId); await redisZadd(getKeyName(REDIS_KEY.TOWER_RANK, serverId), encodeScoreWithTime(towerLv, towerUpTime?towerUpTime.getTime():0), roleId); await redisUserInfoAdd(roleId, {roleName, lv, vLv, guildName:"", head: "zhaoyun"}); } } // 排行榜是否存在 export async function existsRank(key: string, serverId: number) { const result = await redisExists(getKeyName(key, serverId)); return result; } export function getKeyName(key: string, serverId: number) { return `${key}:${serverId}`; } // 更新玩家信息 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, serverId: number, roleId: string, score: number, timestamp: number, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) { // 更新分数 const _score = encodeScoreWithTime(score, timestamp); await redisZadd(getKeyName(key, serverId), _score, roleId); // 移除100名以外 await redisZremRangeByRank(getKeyName(key, serverId), 100, 10000); // 如果没有信息,更新玩家信息 const hasCurUser = await redisHexists(REDIS_KEY.USER_INFO, roleId); if(!hasCurUser) { await redisUserInfoAdd(roleId, params); } return true; } // 获取排行榜 export async function getRank(key: string, serverId: number, roleId: string) { let ranks = [], myRank = null; const rankFromDb = await redisZrevrangeByScore(getKeyName(key, serverId), '+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: Math.floor(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); } /**************** 寻宝相关 start */ // 把寻宝的玩家信息存入 redis export async function setTeamSearchReq(roleId: string, sid: string, qualityArr: Array) { let cmds = []; qualityArr.forEach(quality => { if (quality) { cmds.push(['sadd', `${REDIS_KEY.COM_TEAM_SEARCH_PRE}${quality}`, `${roleId}:${sid}`]); } }); const multiClient = client.multi(cmds) as Redis.Multi; const newMulti = promisifyAll(multiClient) as Redis.Multi; const res = await newMulti.execAsync(); console.log('setTeamSearchReq: ', res); return res; } // 取出 0 - 2 个某品质的匹配中玩家,并在其它品质中删除此玩家信息 export async function rmRoleFromQueue(roleId: string, sid: string, qualityArr: Array) { let cmds = []; for (let q of qualityArr) { cmds.push(['srem', `${REDIS_KEY.COM_TEAM_SEARCH_PRE}${q}`, `${roleId}:${sid}`]); }; const multiClient = client.multi(cmds) as Redis.Multi; const newMulti = promisifyAll(multiClient) as Redis.Multi; await newMulti.execAsync(); } export async function getTeamSearchByQuality(quality: number) { // TODO: 操作不具有原子性 const userInfos = await client.srandmemberAsync(`${REDIS_KEY.COM_TEAM_SEARCH_PRE}${quality}`, 2); console.log('getTeamSearchByQuality: ' + userInfos); if (!userInfos || !userInfos.length) return null; let cmds = []; let res = []; for (let userInfo of userInfos) { const decodeData = `${userInfo}`.split(':'); if (decodeData.length !== 2) return null; res.push({roleId: decodeData[0], sid: decodeData[1]}); GOOD_QUALITY.forEach((q) => { cmds.push(['srem', `${REDIS_KEY.COM_TEAM_SEARCH_PRE}${q}`, userInfo]); }) const multiClient = client.multi(cmds) as Redis.Multi; const newMulti = promisifyAll(multiClient) as Redis.Multi; await newMulti.execAsync(); } console.log('getTeamSearchByQuality res: ', res); return res; } export async function checkRoleInQueue(roleId: string, sid: string, qualityArr: Array) { for (let quality of qualityArr) { let res = await client.sismemberAsync(`${REDIS_KEY.COM_TEAM_SEARCH_PRE}${quality}`, `${roleId}:${sid}`); if (res) { return true; } }; return false; } export async function clearComBtlQueue() { for (let q of GOOD_QUALITY) { client.delAsync(`${REDIS_KEY.COM_TEAM_SEARCH_PRE}${q}`); } } /**************** 寻宝相关 end */ ////// redis方法 //// key // key 是否存在。 export async function redisExists(key: string) { return await createPromise('exists', [key]); } // 在 key 存在时删除 key。 export async function redisDel(key: string) { return await createPromise('del', [key]); } // 设置过期时间 export async function redisExpire(key: string, seconds: number) { return await createPromise('expire', [key, seconds]); } //// 有序集合 // 返回有序集中指定分数区间内的成员,分数从高到低排序 export async function redisZrevrangeByScore(key: string, max: (number|string), min: (number|string), withscores: boolean, limit?:number, offset=0): Promise> { 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):Promise { 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]); }); }