引言
前面提到,可以用Redis解决游戏的跨服排行榜的痛点,使用Redis可以让排行榜数据更及时,且更新数据时也不会和其他玩家冲突,可以说是完美的跨服排行榜技术方案。那么,Redis在游戏内还有其他什么妙用?
Redis并不只具备存储数据和读取数据的能力,翻看它的API文档,我惊讶地发现,它有一个发布订阅的功能。
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。Redis 客户端可以订阅任意数量的频道。
![1690117255250145.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690117436876820.jpg)
上图展示了多个服务器订阅同一个频道,并且任意服务器可以向频道推送消息。下图则展示了订阅该频道的服务器接受来自频道的消息。这看起来就像是一种电台技术,大家都绑定在同一个频段内,就可以多方互相进行消息通信。
![1690117349666625.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690117436802931.jpg)
有了这个技术,也就是说所有的游戏服(为了更好地进行负载,一般游戏都会分区分服,降低单服压力)可以进行简单的通信。比如,所有服都绑定了频道【cross-channel】,此时1服推送一条消息到Redis,Redis则会转发给所有绑定了该频道的游戏服,通过解析数据,可以指定任意服务器进行数据处理。
游戏内实战——解决跨服聊天。
第一步,新建一个跨服频道中心,初始化的时候启动消息接收器。
/**
* 跨服数据中心
* 聊天发送和订阅
*/
object CrossChannelHall {
/**
* Jedis中心
*/
val jedis = CrossDataCenter.getJedis()
/**
* 初始化
*/
fun init() {
// 启动消息接收器
Thread(MessageReceiver()).start()
}
/**
* 推送消息
*/
fun pushMessage(type: Int, data: Any) {
val json = Gson().toJson(data)
jedis.publish(GameContext.MESSAGE_CHANNEL_KEY, Gson().toJson(CrossMessageData(type, json)))
}
}
自定义消息接收器,就是消息监听器,进行循环监听检查消息,当有消息的时候就进行处理。
/**
* 消息接收器
*/
class MessageReceiver : Runnable {
/**
* Jedis中心
*/
val jedis = CrossDataCenter.getJedis()
/**
* 我的处理器
*/
private val msgServerRegister = MsgServerRegister()
/**
* 绑定处理器
*/
private fun checkMessage() {
jedis.subscribe(msgServerRegister, GameContext.MESSAGE_CHANNEL_KEY)
}
/**
* 运行
*/
override fun run() {
while (true) {
checkMessage()
}
}
}
同时,还需要一个消息注册器JedisPubSub,用于接受消息,并进行对应的处理。
下面是我自定义的一个消息注册器,根据消息类型进行分类处理,这样后期拓展的时候,只需要添加新的Handler处理器。
/**
* 消息注册器
*/
class MsgServerRegister : JedisPubSub() {
/**
* 处理库
*/
private val handlerMap = mutableMapOf<Int, IMsgHandler>()
init {
handlerMap[MsgCmd.MSG_CHAT] = MsgChatHandler()
}
/**
* 处理函数
*/
override fun onMessage(channel: String, message: String) {
try {
Gson().fromJson(message, CrossMessageData::class.java)?.let { data ->
handlerMap[data.cmd]?.execute(data.json)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
下面是自定义的一个消息处理器接口。
interface IMsgHandler {
fun execute(json: String)
}
具体的跨服聊天消息处理器。收到消息,并推送给当前游戏的所有玩家。
class MsgChatHandler : IMsgHandler
{
override fun execute(json: String)
{
Gson().fromJson(json, ChatData::class.java)?.let {
ChatLogger.addChat(it)
SessionUtil.gameBroadcast(PushCodes.CHAT, it)
}
}
}
综上,一个简单的跨服聊天功能就开发完成了。使用Redis开发跨服功能不需要额外的第三方服务器,之前我是自己写了一个跨服服务器进行消息中转,每次有新的内容拓展都需要重新发布更新跨服服务器,极大地增加了维护成本,同时也容易引起游戏版本和跨服版本不一致。现在使用Redis后,就有效规避一致性的问题。
当然,我对这个Redis发布订阅的性能目前并不太了解,能够支撑每秒多少次多少数据量等等,所以使用这个功能的场景还比较少,不敢贸然增加。如果有知道这方面性能的大佬,烦请指点一二。