- WIN10
- VS2013 UPDATE5
- 500机器人 + 500SOCKET调度工具
- 类比 简易黑大厅 + 500个客户端管理器
- 输入 大厅,游戏服务器状态,配置数据
- 输出 机器人的精细配桌调度行为
- 为前置工具,
强依赖
大厅服务器, 游戏服务器 - 若大厅, 游戏没启动,工具第一次无法启动,启动成功后会自动重连,不用重启
- 机器人工具网络库会启动大量线程,和网络连接成正比
- 线程切换过多占用大量cpu,造成同机游戏服务消息堆积,应该独立部署,避免影响别的服务
- 机器人越多,网络接口会越慢,如果业务设计
10s
一个机器人,因考虑此效应参数设计成7-8s
- 若网络库sessionid解析成负数。检查游戏服务器是否发了一个不存在的response给机器人工具
- 如果游戏服务模拟发了需要应答的request的消息, 最后需要
过滤reponse
, 不用发给机器人工具 - 发送不存在的response会造成机器人网络库触发onError,应用层收到【UR_SOCKET_ERROR】消息
- 机器人除了进房间调度消息以外,应该只接受通知
- 对集中消息发送,需要做
限流
处理,典型的如大量机器人启动时,断线重连,集中心跳 - 使用 #define CURRENT_DELAY // 限流延时标签 默认为50ms间隔
- 2个小时的400机器人的暴力测试,机器人服务总连接收到20次 Socket Error
- 因是机器人网络自己产生了Socket Error,目前使用这个已知bug来
兜底重新同步
游戏服务器所有数据
- 业务配置表 [robot.setting]
- 运维配置表 [RobotTool.ini]
最大最小房银和桌银应自洽
,如桌银max<房银max,让补银的机器人可以进入房间- 对应的游戏服ini需要填入机器人工具ip白名单 [RobotSvr] Host1=xxxIP Host2=xxxIP
- 检查robot.setting中所有配置房间在在黑大厅配置的ip , game port和实际游戏服务器监听的port应一致
机器人连接的游戏ip由RobotTool.ini配置,端口由黑大厅对应的房间配置决定
- 配置文件3份debug, rc, release
- game_id // 游戏id
- main_interval // 业务主线程定时器间隔 单位ms
- deposit_interval // 补银单线程定时器间隔 单位ms
- deposit_active_id // 后台配置补银还银活动ID
- deposit_gain_url // 后台补银服务地址
- deposit_back_url // 后台还银服务地址
- 房间配置 roomid // 需要机器人的房间ID
- 房间配置 wait_time // 机器人等待时间 注意每个游戏有前置业务条件
- 房间配置 count_per_table // 每桌最多上多少机器人
- 机器人 userid // 机器人平台用户id
- 机器人 password // 机器人平台用户密码
- 由后台同事负责,机器人工具不用配置
- 大厅服务ip port
- [hall_server]
- ip=192.168.103.108
- port=30626
- 连接游戏ip, 不支持域名, 网络库不支持ipv6解析
- [local_ip]
- ip=192.168.44.41
- 正式部署的服务名,显示名
- [service]
- service_name=robottoolrumm
- display_name=robottoolrumm
- 在main函数 TEST CASE 下添加单元测试用例,请注意测试用例非线程安全
- 开启Application Veritfy 检查堆错误
- resharper pvs-studio 静态分析
- DEBUG_NEW 检测内存泄露
- Terminal 中输入'P' 打印进程使用的资源信息
- Terminal 中输入'S' 打印业务类数据状态
- Terminal 中输入'M' 打印发送消息数
- TRACE_STACK 打印调用栈
- TRACE_ASSERT 开启严格调试模式, 错误时打印调用栈TRACE_STACK, 并assert断言
- CHECK_XXX 参数合法性检查
- ERR_STR(x) 详细错误码信息
- REQ_STR(x) 协议定义
- [只支持业务线程mainthread]对象快照API : SnapShotObjectStatus
- 如果机器人和游戏服部署必须在
同一时区
- 上传业务配置robot.setting。填写对应外网房间,补银服务
- 上传运维配置RobotTool.ini。填写对应游戏服务ip地址
- 对应的游戏服务器需要加上
机器人白名单
[RobotSvr] - 黑大厅填写房间ip和game port应于监听实际
端口一致
房银桌银自洽
房间银上下限区间 >= 桌银上下限区间- 桌银上下限区间[左闭右开)
- 注意机器人工具是随机绑定
单核
运行 - 机器人工具在线重启后
- 1 所有机器人登陆大厅
- 2 原先在游戏的机器人EnterGame 触发游戏服务器中的断线续玩流程
- 后端游戏服务器在线重启后
- 1 重新建立游戏服务器连接
- 2 重新建立所有在线的机器人游戏连接
- 如大厅网络IO,游戏网络IO,后台,本地文件
- IO输入输出产生程序的内存数据,加锁封装成mutex对象,如文件IO
- 从
IO角度划分数据对象
, 对应数据锁, 控制数据生命周期
- 每次编译前先
更新到模板最新的pb定义
game_base.pb.h game_base.pb.cc game_base.proto - 在业务层单线程的定时器触发 [MainProcess] 中开发业务逻辑
- 不用考虑[线程安全], [数据时效性],[网络异常],[无业务锁]和[业务单线程]客户端类似
只使用
提供的基础线程安全管理类开发业务,保证线程安全- 不直接使用应该被锁保护的引用对象
- 数据管理类都是实时保持后端同步
- 网络管理类内部心跳保活重新,连接层的异常处理自恢复
- [MainProcess]业务开发过程不关心网络消息接口
启动线程
1条:可以获得任意锁,但只启动和退出时,(服务模式会单独启动一条线程)业务线程
1条:可以获得任意锁,在启动完毕之后内部线程
n条:只能获取对象内部锁和(最后获得)银子锁,不允许获得别的数据锁
- 数据层(配置,网络)-> 资源管理类(线程安全,网络异常)->具体业务(自定义调度)
- 扩展方案:横向扩展,用多个机器人工具, 几个房间一组,机器人只进入指定房间组
- 而不是扩展业务线程+业务锁, 把单线程的业务开发转换成多线程问题
- 注意指定单核绑定,不要让多个机器人工具只在一个单核上运行,目前是随机绑定单核
- 业务配置文件robot.setting
- 运维配置文件RobotTool.ini
- 数据层data为游戏服务器模板层user, room,table, chair 只读映射
- 银子数据
- 大厅登陆状态
- 补银还银队列
- 原始数据 setting.setting, robottool.ini
- 衍生数据
- 大厅连接只有1个,所有机器人共用
- 游戏连接只有1个,同步游戏服务器数据层
- 机器人连接n个,执行调度行为
- 补银还银http,通过后台活动实现
- RobotTool.ini 运维配置文件,不含任何具体游戏业务
- setting/setting_manager 机器人工具配置文件robot.setting
- data/user_manager 管理游戏服务器所有用户数据(包括机器人)
- data/room_mamager 管理游戏服务器所有房间数据(桌椅等)
- data/deposit_data_manager 管理所有用户银子
- data/robot_net (1*n条) 单个机器人连接管理
- net/hall_net_manager (1条) 所有机器人的大厅连接和对应数据
- net/game_net_manager (1条) 游戏服务器连接和接收数据层UserMgr,RoomMgr()
- net/robot_net_manager(0条) 所有机器人连接管理
- net/deposit_http_manager 后台补银还银http连接管理
- app_delegate 业务主线程管理类
- main 进程启动类,包括服务模式
- 四点: 1 线程安全, 2 数据时效性,3网络异常自恢复, 4 数据调用层次封装
- 一个对象一把锁
- 有多把锁需求时说明对象数据聚合太多,需要拆分多个对象
- 抽象接口不含具体业务字段
- 实例化接口处理具体消息
- 请勿在抽象接口中调用实例化接口,避免死循环
- 从对象方法可见性分两种, 只对内部成员对象线程可见,对内部外部线程都可见
- 用
IsAllowedThreadID
限定方法可见线程,非可见线程操作返回 - 无WithLock后缀函数:如果方法有多线程可见数据 一般都需要加锁,除非业务层次允许脏读
- 组合lock + WithLock函数组合而成, 保证不会获得多次mutex,std::mutex 为非递归锁
- 保活
KeepConnection
重连保持连接 - 通过
ThreadNotify
实时同步后端服务器数据状态 - 独立机器人服通知接收线程,维护只读数据层
- 独立心跳线程,避免阻塞过程会让心跳超时
- 消息发送错误后,自动重置对象数据,并重新初始化
- 管理类封装只读数据对象,保证了从逻辑线程调用时的强制顺序
- 逻辑线程-> 管理类(mutex)-> 只读数据类(无锁)
- 封装保证执行流的顺序,强制保证了各个锁的获取顺序,避免死锁
- 银子只用大厅和游戏
- 银子数据锁只内部线程使用时,保证调用顺序,是最后一把锁
- 银子的生命周期常驻对应平台数据层,不随大厅和游戏的内存变化而变化
- 桌银区间 左闭右开 [20000, 20001)
- 补银大小限制 (1,000,000,000后台确认)
- 补银过程: 业务主线程-》http补银-》大厅拉去最新值-》设置data层
- 银子请只使用deposit_data_manager
- 向游戏服务器发送GR_VALID_ROBOTSVR消息注册ip为白名单ip
- 网络库每个连接生成一条线程,导致50*0的线程频繁切换,绑定cpu单核,避免过度抢占所有cpu
- 集中消息发送需要做限流处理,避免冲击后端服务器
- 网络库API接口非线程安全,多线程请注意加锁
- 从游戏服务获得所有房间状态配置,而不是大厅
- ini文件读写非线程安全
- ini文件为启动参数文件,不应加入任何业务配置
- 游戏服务器提供明确数据状态,不自己推导衍生状态
- game_net_manager类不应该出现具体业务字段,抽象管理类
- 业务定义字段应出现在room table chair user 具体数据类
- 具体数据类负责如room table chair user跟踪游戏服务器数据状态的变化
- 游戏模板默认游戏开始后有倒计时5秒
- 注意游戏开始后,会有桌子上的玩家下局准备玩的玩家,状态是waitting
- TableUserInfo 包括旁观,没有椅子信息
- 机器人离开游戏时,模板层主动断开连接,降低游戏连接数
- 内部线程里请无调用其他有锁对象(银子除外),保证业务独立,避免死锁
- 不支持n个机器人发心跳,在内网不通过代理
- 不支持robot.setting的热更新
- 不支持robot.setting中配置的多个房间ip port不一致,必须一样
- 不支持多个游戏服务器
- 不支持多个游戏
- 不支持ipv6
- 不支持定时获得财富信息,后台数据查询压力过大
- 不支持昵称,头像
- 只读,脏读, 可写数据分离
- 状态越少越好
- 状态可见线程越少越好
- 控制对象和线程可见性
- 构建相应线程安全级别对象构建业务
- 业务逻辑线程中不使用非线程安全的对象
- 尽量不返回被mutex保护的数据引用
- 不直接操作raw memory如new和delete
- 注意智能指针内存泄露
- 多线程析构对象应用智能指针管理
- Macro宏只用在参数检查,打印日志,不做业务逻辑
- const的使用 参数,方法,返回值三处都应添加
- const的方法对多线程“读”安全,可重入
- 检查所有用户user类型为kRobot,防止误操作真实用户
- 记录本机HardID方便大厅排查问题
- 数据对象,管理器,业务层次封装分明,强制保证执行流顺序,避免死锁
- release网络库自身随机抛出Socket Error
- 机器人进入游戏失败统计
- 接入钉钉报警如cpu过高,机器人消耗完等
- thread local 重构错误码
- 抽象mixin, 如timer, connect, config复用代码