Redis在游戏服务器的使用,看看战力排行榜的实现
【摘要】 今天主要回顾一下redis 在我们游戏服务器上的应用。1、redis 介绍Redis 是完全开源的,是一个高性能的 key-value 数据库。redis 的优势性能高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作...
今天主要回顾一下redis 在我们游戏服务器上的应用。
1、redis 介绍
Redis 是完全开源的,是一个高性能的 key-value 数据库。
redis 的优势
- 性能高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。。
2、在项目中的应用
在游戏服务器中的应用主要有下面几个场景:
基于redis 的高性能,所以我们当做跨服的数据存储使用
基于redis的发布订阅功能,我们当做服务器之间的消息发布使用,统领所有的服务器
基于redis 的排序功能,我们当做跨服的排行榜使用。
看下我们的服务器架构:
注:
MS:跨服服务器
G:游戏逻辑服务器
这里仅仅画出了和redis的链接,并没有标注出游戏的链接
3、代码展示
在项目中加入下面的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
如果你使用springboot搭建的项目,可以加入下面到pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1、数据存储
数据的存储比较简单,常规的使用就行
@Component
public class RedisUtil {
public static RedisUtil util;
public RedisUtil(@Autowired JedisPool jedisPool) {
this.jedisPool = jedisPool;
RedisUtil.util = this;
}
public static RedisUtil getInstance(){
return util;
}
@Autowired
private JedisPool jedisPool;
/**
* 向Redis中存值,永久有效
*/
public String set(String key, String value) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.set(key, value);
} catch (Exception e) {
return "0";
} finally {
jedis.close();
}
}
/**
* 根据传入Key获取指定Value
*/
public String get(String key) {
Jedis jedis = null;
String value;
try {
jedis = jedisPool.getResource();
value = jedis.get(key);
} catch (Exception e) {
return "0";
} finally {
jedis.close();
}
return value;
}
/**
* 校验Key值是否存在
*/
public Boolean exists(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.exists(key);
} catch (Exception e) {
return false;
} finally {
jedis.close();
}
}
/**
* 删除指定Key-Value
*/
public Long del(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.del(key);
} catch (Exception e) {
return 0L;
} finally {
jedis.close();
}
}
/**
* 分布式锁
* @param key
* @param value
* @param time 锁的超时时间,单位:秒
*
* @return 获取锁成功返回"OK",失败返回null
*/
public String getDistributedLock(String key,String value,int time){
Jedis jedis = null;
String ret = "";
try {
jedis = jedisPool.getResource();
ret = jedis.set(key, value, new SetParams().nx().ex(time));
return ret;
} catch (Exception e) {
return null;
} finally {
jedis.close();
}
}
public void pub(){
jedisPool.getResource().publish("test","msg");
}
}
2、排行榜排序功能
排行榜的功能,主要是使用sortset的数据结构,但是这里有一个比较要注意的情况就是时间的问题。
比如相同战力,先上榜的玩家肯定排名靠前,这是毫无疑问的,但是总有些同学会忘记这件事情,
同时,在更新的时候,要把原来值的小数去掉,再加上当前的时间运算之后的值
把玩家id 当做key,把玩家战力当做score,把时间当做小数进行累加
/**
* 计算score,通过一个基准时间,可以是2100或2050年,减去lastOrderTime再除以基准时间,可以获得一个小于1的小数,
* 在获取真正score的时候,只要舍去小数位即可
* @param orderNum
* @param lastOrderTime
* @return
*/
private double getOrderNum(int orderNum, long lastOrderTime) {
return orderNum + (BASE_TIME - lastOrderTime) * 1.0 / BASE_TIME;
}
/**
* 向redis中存入数据
* 保存的是经过处理之后的数据
* @param key
* @param value
*/
public long put(String key, int value) {
long time = System.currentTimeMillis();
double dValue = value + 1 - time / Math.pow(10, (int) Math.log10(time) + 1d);
dbData.put(key, dValue);
return time;
}
3、发布订阅
@Component
public class JedisPubListener extends JedisPubSub implements ApplicationRunner {
@Override
public void onMessage(String channel, String message) {
System.out.println(String.format("receive redis published message, channel %s, message %s", channel, message));
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println(String.format("subscribe redis channel success, channel %s, subscribedChannels %d",
channel, subscribedChannels));
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
System.out.println(String.format("unsubscribe redis channel, channel %s, subscribedChannels %d",
channel, subscribedChannels));
}
@Override
public void run(ApplicationArguments args) throws Exception {
}
}
因为Jedis的订阅是阻塞的,所以单独开一个线程
@Component
public class ListenerThread extends Thread{
@Autowired
public JedisPubListener listener;
@Autowired
private JedisPool jedisPool;
@PostConstruct
public void init(){
this.start();
System.err.println("thread start");
}
@Override
public void run() {
Jedis resource = jedisPool.getResource();
resource.subscribe(listener,"test");
}
}
4、总结
redis的高性能贴合了游戏行业的的要求,所以现在很多游戏服务器都开始使用redis。
redis 可以作为数据存储,可以快速实现排行榜,可以作为缓存使用,是真的好用。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)