游戏服服务器——DB服

服务器框架 https://blog.csdn.net/nie2314550441/article/details/105981967

一、DB服介绍

整个服务器(除网站后台)与数据库交互都是通过DB服进行。DB服用于数据库交互和数据缓存,使用sqlserver做为存储,用来保存用户的所有相关数据。

二、服务器启动流程

1、启动准备

  1. 初始化网络库
  2. 监听事件注册

2、开始启动

  1. 创建日志、定时器
  2. 用户代理服务启动和监听(监听网关服和数据交互)
  3. 创建DB代理服务(用于存储过程与数据库交互)
  4. 挂接逻辑事件(用户线程读取网络数据)
  5. 创建守护中心(与守护中心服连接,用于服务器拉起)

说明:DB代理服务器,创建的时候会创建DBEngine服务

3、启动流程图

三、DB服与其他服务器交互

参考前面

四、类图总览

DB服按功能分可以分为两大块,连接对象和db代理。

 

五、探DB代理服务器

db代理服务是DB服最重要的模块,用于处理db请求和数据缓存。DB代理服务功能可以分为两部分,一、db请求分发和数据缓存;二、与db交互。下图为DB代理服务类图。

5.1 db请求分发和数据缓存

存储过程功能包含:玩家连接登陆服进行登陆、玩家进入广场服、玩家私有属性加载、玩家登录游戏服、活动数据等等。DB代理在将请求转发给dbengine前加了一层分类处理,用于缓存和校验。请求分类处理分为五类:直通、用户登录、登录广场、登录房间、用户数据。

  1. 直通:数据直接转发给DBEngine
  2. 用户登录:进行校验,校验通过之后转发给DBEngine。玩家登录包含登出,登录/登出的是登录服务器。
  3. 登录广场:进行校验,登录成功之后会创建用户数据,用于记录缓存数据,玩家登出,会执行所有的缓存请求。
  4. 登录房间:进行校验,交互通过之后转发给DBEngine。
  5. 用户数据:判断数据是否需要缓存,若配置为缓存先进行缓存。缓存的数据会在定时存盘或者限制存盘时执行缓存中的存储过程;若配置为不用缓存,转发给DBEngine。一般数据的存储过程是不用进行缓存,一般是需要定期存盘的数据需要缓存,例如玩家私有数据。

5.2 DBEngine与db进行交互

DB代理服务器将db请求处理完毕之后,转发给DBEngine进行执行。执行完毕之后通过接口IDBRetSink::OnRet()返回给DB代理服务器分类对象,再转发给对应连接对象,连接对象通过tcp将返回信息发生给对应请求服务器。

六、探DBEngine

db请求最终是通过DBEngine来与数据库进行交互。DBEngine收到db请求,根据请求所在的配置,将其投递到对应的异步队列中,一个异步队列为一个线程,该线程与数据库相连,当队列中用请求按顺序执行请求,执行完毕向上层返回执行结果。

6.1 DBEgine创建

DB代理服务在创建过程中会加载并创建DBEgine,DBEgine创建流程:配置解析,异步队列创建。

6.1.1 配置解析

需要解析的配置有三个《DBDefine.scp》、《DBEngine.scp》、《DBRequest.scp》。

DBDefine.scp:数据类型定义

DBEngine.scp:包含多个数据库列表配置信息,一个数据库列表配置包含连接数据库需要的信息(ip,断开,数据库名,账号、密码)和异步队列配置。

DBRequest.scp:服务器请求执行的存储过程配置

6.1.2 异步队列创建

一个数据库队列里面可以配置多个异步队列类型,一个异步队列类型对应多个队列,每个队列会创建一个线程,这个线程会和数据库进行连接并处理投入到该队列中的db请求。

6.1.3 DBEngine执行db请求

DBEngine执行db请求流程:

1. DBEngine主线程收到db请求

2. 根据db请求的requestID,查找该请求配置的异步队列类型。

3. 在该异步队列类型中选中一个线程,将db请求投递给它

4. 异步队列执行完db请求,将结果返回给DBEngine主线程

5. DBEngine主线程将db请求返回发送给上层

 

七、探连接对象

DB服收到客户端(中心服、登陆服、广场服、游戏服)连接之后会创建一个DBConnection,用于管理连接的消息接收和发送,DBConnection会创建一个DBDataProxy用于处理db请求转发(发送给DB代理服务)和接收db请求返回并发送给客户端。

DBConnection有多个,一个客户端对应一个。一个DBConnection有一个DBDataProxy。

 

八、一个存储过程执行流程

以在广场服发起执行存储过程为例,省略了一些中间过程。

九、线程同步问题

9.1 DBEngine主线程和异步队列线程之间同步

6.1.3中涉及到需要同步的地方有四处:

1. 主线程将请求投递给异步队列线程

2. 异步队列线程取出请求,用于执行

3. 异步队列将执行完的结果发送给主线程

4. 主线程将db请求结果发送给上层

这里需要有两处数据需要维持同步,步骤1、2同步的是异步队列中请求对象,步骤 3、4是主线程中返回结果队列。

9.2 同步方法

1.旧版本:在请求对象队列中添加或取出请求对象时会进入临界区域(EnterCriticalSection和LeaveCriticalSection)。

2.新版本:将请求对象队列改成支持多线程添加或取出,向队列中添加或取出数据不是用EnterCriticalSection和LeaveCriticalSection,而是用Interlocked系列函数。

两种方法比较:

当线程试图进入一个关键段,但这个关键段正被另一个线程占用的时候,函数会立即把调用线程切到等待状态。这意味着线程必须从用户模式切到内核模式(大约1000个CPU周期),这个切换的开销非常大。——来源《Windows核心编程》

Interlocked系列函数是锁内存,速度非常快,只需要占用几个CPU周期。

3.进一步优化效率

异步队列中新加两个队列,一个是【主线程调用队列】用于主线程调用,一个是【当前插入队列】用于当前线程添加返回结果。异步队列线程将返回结果都添加到【当前插入队列】,不需要考虑同步问题。当主线程需要获取异步队列返回结果是,将【主线程调用队列】和【当前插入队列】进行翻转,即:【主线程调用队列】变成了【当前插入队列】,【当前插入队列】变成了【主线程调用队列】,并将翻转后的结果发送给主线程,只需要在进行翻转队列的时候进行加锁。

猜你喜欢

转载自blog.csdn.net/nie2314550441/article/details/106556806