c s具体通信流程
服务器间的交互
与数据库交互的服务器
玩法服务器
现有的gameserver/battleserver的负载有多少?
几万,battleserver负载均衡
创建一个房间是怎么分配到哪个服务器,有几个业务服务器,创建房间区分到哪个服务器上
gameserver
battleserver 仅仅支持打牌逻辑之类的操作
服务器类型的划分
客户端仅仅和interface之间建立连接 interface和各个服务器之间进行交互
连接服务器、业务服务器
消息的同步
怎么同步到各个客户端,压力比较大的情况怎么处理
netty同步机制
Server结构简述
1网络通信
2业务逻辑
3数据存储
现有的服务器及其职责
1 manageserver
2 authserver 单点服务器,登录验证
3 gameserver
4 clubserver
5 campaignserver
6 pushserver 推送服务器
7 replayserver
8 battleserver 多点服务器 战斗服
9 interfaceserver 多点服务器 网关
为啥要这么多服务器,是否可以一个服务器承担多个角色?
网络通信
通信数据协议使用protobuf进行定义 message管理协议的定义生成,生成客户端使用的协议文件
协议配置文件
req, res
ThreadPool
业务线程池,对于每一个业务任务,按照任务的key,分配到不同的任务组中,多个任务组异步执行,同一个任务组同步执行
网络处理
server之间的通信和客户端和网关之间的通信,都是基于netty框架建立的tcp通信,处理连接的建立和断开,传入和传出数据处理
connection
1 逻辑连接,内部存储netty真是管道信息和获取连接点信息(ip等)和发送消息
2 作为client-interface连接,处理协议加密逻辑
channelhandler
netty中进行报文的编码解码、报文数据压缩解压、自定义协议的编码和加密。
handler分为输入流处理handler、输出流处理handler以及混合handler
服务器主要添加了一下handler 2 3 是netty自带的
1 haproxydecoder 网关服务器专用,由于网关之前有一层高防机器,客户端的消息被高防转发,报文中的ip信息为是高仿机ip,实现透明代码,获取客户端真是ip
2 lengthfieldbasedframedecoder 处理黏包问题
啥叫黏包
3 lengthfieldprepender 附加数据长度,2字节
4 snappyframedcodec 压缩/解压
5 messageprocessor 协议解析
client <-> interface
Byte | Byte | Byte | 4Byte | 4Byte | |
---|---|---|---|---|---|
消息类型 | 加密类型 | 目标serverId | 协议id | callback | protocl |
interface -> server
Byte | 4Byte | 4Byte | 4Byte | 4Byte | 4Byte | |
---|---|---|---|---|---|---|
消息类型 | 玩家id | connectionId | 玩家ip | 协议id | callback | protocl |
server -> interface
Byte | 4Byte | 4Byte | 4Byte | |
---|---|---|---|---|
消息类型 | connectionId | 协议id | callback | protocl |
server <-> server
Byte | 4Byte | 4Byte | |
---|---|---|---|
消息类型 | 协议id | callback | protocl |
业务逻辑
网关协议加密
协议加密
c s协议通信内容进行加密,加密阶段处于c和interface之间。加密原则是由客户端发起,当客户端发起的协议加密,服务器的响应也进行加密,反之亦然
1 客户端建立与服务器进行连接后,服务器退给客户端一个加密算法类型和加密key
2 当客户端发送的协议未加密或者加密类型与服务器推送给它的类型一样时,进行协议解密,处理业务逻辑
3 心跳包 客户端发送不加密
4 客户端发送上来的协议不加密,那么服务器发送回去的协议也不加密
流程图
协议涉及
syn
type 加密类型
key 加密key
对外接口描述
客户端建立连接后,发送加密信息给客户端
数据存储ZDB
limax官网
http://www.limax-project.org/
死锁概述
潮汕 2017.11
现象:因战绩缓存条数较少,在高峰期大量的查询战绩请求导致缓存一直失效,并且产生了清缓存的线程池拒绝执行的错误
原因:当请求非常密集时,前一次请求触发的LRU清缓存还未执行,下一条请求再次触发清缓存,会导致多个线程同时执行该任务,极端情况下会导致线程拒绝执行新任务
限制:对于数据缓存的条数需要合理设置,避免出现频繁清缓存的情况
贵阳 2017.11
现象:俱乐部公告推送上线后,MySQL查询量从前一天的1000条每秒飙升到2000多条每秒
问题:通过GMT后台给俱乐部加一条默认数据,避免每次查询时都要到MySQL去查询
原因:当select一个MySQL中不存在的key时,会始终执行SQL操作。如果该查询非常频繁,会导致MySQL查询量飙升
限制:查询数据时评估该查询发生的频率,如果会非常频繁,则需要考虑key不存在的情况
17/12/01 潮汕线上战绩丢失情况
表现:同比贵阳,贵阳的战绩丢失数量是20~30,潮汕的战绩丢失量是200+
原因:同比贵阳,贵阳在查询战绩的时候使用select获取读锁,不对过期战绩进行删除操作。
修复:同步贵阳代码(查询读锁,不删除战绩;回放数据拆分为新表)
17/12/19 零点game服务器死锁
锁:
Persist_global 1
Runtime_global 2
allAreas 3
A2: logins (老用户)
1 2 3
D: RoomStateRecordTask(非zdb锁)
3 2
1. 先发生a2 d
2. 然后发生a2
17/12/20 零点俱乐部进程消失
表现:为了解决昨天凌晨的死锁问题,临时决定在零点对白名单进行打开关闭操作,避免死锁发生,结果1分钟左后club服务器的进程消失
原因:
使用读锁进行数据修改,zdb存盘是使用读锁访问storage的数据,生成将要写入数据库的数据时,发生了list数据被修改的异常,造成zdb崩溃。
其他常见情况:
1. 锁顺序不一致性造成服务器死锁。
2. 业务中自定义ReentrantLock锁,在持有锁期间调用zdb锁造成的嵌套死锁。
3. list数据遍历访问zdb锁造成的顺序不一致性。
4. 缓存条数的合理设置,默认10240条记录
5. 当以key为0查询表中的数据时,及时表内不存在该记录,依然能够查询出数据
6. 表的缓存机制使用lru,不要使用Concurrent模式(会导致内存持续增长,服务器崩溃)