一套新游戏服务器解决方案Bulls提案

Bulls Proposal

一套新游戏服务器解决方案Bulls提案_第1张图片
red-bulls.jpg

一套新游戏服务器解决方案

2020-05-14 landon30

关键词

  • 单线程、异步非阻塞、协程
  • stateful、分布式
  • actor model like
  • vert.x

关注的两个主要点

1. 单进程并发

2. 分布式支持

提高服务器并发能力

高效充分的利用CPU

  • high throughput and low-latency
  • 降低成本

C10K

  • epoll、reactor
  • netty
    • 支持多种reactor线程模型
    • 长连接和短连接区别(对于长连接项目的话,accept线程池可以不用)

线程阻塞是根本问题

  • io密集型、cpu密集型

    • 如果所有所有业务都是cpu密集型的,那没必要异步
    • 游戏服务器是一个io密集型的应用
  • 伪异步

    • 治标不治本
  • 全异步

  • jdbc-Blocking IO

    • Asynchronous Database Access API (ADBA)

    • Oracle stops work on ADBA

    • HiKariCP

      You want a small pool, saturated with threads waiting for connections

减少上下文切换

  • 多线程不是银弹
    • 大量的不同种类的线程池
  • 真正的开销来源于线程阻塞唤醒调度

游戏服务器线程模型

  • 业务单线程
    • 分场景、分房间/副本
    • 取模映射
      • 当线程数是2的n次幂时,用位运算效率更好
      • id & executeors.length -1id % executors.length
    • 业务对象消息队列
      • 如玩家持有一个消息队列,和玩家的消息都分发这个队列,保证顺序
      • 运行期线程可不固定
    • 早期的游戏服务器进程纯单线程
      • while(true) {收包,分发,处理,发包}
  • 多人交互玩法
    • 多线程
    • 单独线程跑玩法服务
    • 消息投递
  • 不同种类的线程池

建议

  • 线程数目
    • 和CPU核数匹配,要多于核心数
  • 尽量全异步
    • mongodb-driver-async
    • HttpAsyncClient
    • Lettuce - Advanced Java Redis client
  • 对于类似jdbc这种bio
    • 只能用异步线程池封装

异步

  • callback
    • 无返回值
      • onSuccess、onFailure
      • onResult
    • 回调地狱
  • Future、Promise
    • CompletableFuture
    • thenCompose、thenAccept、thenCombine...链式调用
    • 接口必须返回Future
  • Reactive
    • RxJava、Reactor、Spring WebFlux
    • 编程思维转变(反应式编程)
  • Coroutine
    • 代码优雅同时还是之前的编程思维(命令式编程)
    • async/await
    • stackless、stackful

Java的协程

  • quasar
    • 字节码注入,有一定的性能损失
  • 阿里wisp/wisp2
  • 华为
    • 用在自己的RPC系统中,将异步的RPC调用转换成了同步的写法
  • loom
    • Codes Like Sync,Works Like Async
    • 正在进行中
  • kotlin
  • 协程遇到阻塞io会导致线程挂起吗?
    • 为了支持协程,阻塞的api需要以某种形式告诉调用者在当前任务执行前切走
    • 支持协程的语言必须想办法处理所有可能会发生阻塞的情况
    • 协程和异步结合起来用才是最大的用途
    • Java有沉重的历史包袱(不像node.js和go),loom正在进行中,所以kotlin如果调用Java的阻塞api会导致挂起线程的

总结

  1. 少量的线程驱动所有的服务
  2. 服务不能阻塞线程,从而压榨CPU
  3. 协程 + 异步是绝佳的解决方案

分布式支持

已有的问题

  • 进程和section绑定,无法扩容
    • 运营想支持单服5w人
  • 运营中后期,dau下降,无法缩容
    • 资源严重浪费

stateful vs statelss

  • 无状态服务设计原则在创建水平可伸缩服务的技术行业中已经很普遍
    • 直接访问数据库-Latency Issues
    • 缓存加速-Concurrency Issues
  • stateful
    • session sticky
    • 长连接本身就是一种状态
    • 有状态服务路由
      • 一致性hash
      • DHT
    • 不同node节点的数据一致性问题(负载均衡)
  • 游戏服务器需要什么
    • 单线程
    • stateful

actor-like架构

  • Responsibility-Oriented
    • 服务拆分
    • 服务发现
    • 服务解耦
    • 服务间通讯
  • actor model
    • 对于game object这个抽象非常适合,更加面向对象
    • actor可以保存状态
    • 无并发问题,无锁
    • 异步(actor之间通过消息通讯)
    • 天生分布式
      • 无论Actor地址是在本地还是在远程机器上对于代码来说都是一样的
  • actor like
    • 游戏服务器的实现也通常是消息驱动 + 消息分发
      • Client、Internal Server、Server-Interal-Thread
      • 引入rpc

参考选型

skynet

https://github.com/cloudwu/skynet

基于actor模式的开源并发框架


一套新游戏服务器解决方案Bulls提案_第2张图片
skynet-1.png

一套新游戏服务器解决方案Bulls提案_第3张图片
skynet-2.png

主要特点

  • 在 skynet 中,用服务 (service) 这个概念来表达某项具体业务,它包括了处理业务的逻辑以及关联的数据状态
  • Skynet 只负责把一个数据包从一个服务内发送出去,让同一进程内的另一个服务收到,调用对应的 callback 函数处理
    • actor like
    • 每个服务实体有一个私有的消息队列,队列中是一个个发送给它的消息
    • Skynet 维护了一个全局消息队列,里面放的是诸个不为空的次级消息队列
    • 在 Skynet 启动时,建立了若干工作线程(数量可配置),它们不断的从主消息列队中取出一个次级消息队列来,再从次级队列中取去一条消息,调用对应的服务的 callback 函数进行出来
  • 每一个lua服务都有一个独立lua state
    • 配合lua coroutine,'同步写异步'
  • 支持集群

案例

  • 目前主要是阿里系(简悦)在用,主要代表作品用最近很火的三国志·战略版

Pebble

https://cloud.tencent.com/developer/article/1006377

主要特点

  1. 整个编程理念往服务的方式做思考,任何系统,比如帮派、家族都是服务
  2. 异步框架和协程
    • Pebble本身是一个异步单进程框架,能提供非常高的处理吞吐量,同时也能大大简化复杂业务逻辑中的并发数据共用问题
    • Pebble引入了协程的能力:用户的每个RPC处理,都会自动建立一个协程
  3. 数据缓存和持久化
    • Pebble提供了一个类似std::map的接口,让用户定义的数据结构,可以用put/get的方式放入一个对象池中缓冲。这个对象池是在整个集群中共享的,也就是说任何一个进程put放入一个对象,其他的进程都能get获得此对象
    • 为了提高性能,Pebble的对象缓存池是具备“本地缓存”能力的,也就是说,如果你持续在一个进程来put/get某个对象,这个对象实际上是在本地机器的内存中缓存的
  4. 集群特性
    • 单服(standalone)
    • 集群(cluster)
    • 云(cloud)
  5. 状态无关的服务
    • Pebble对于服务状态数据的解决方案是“让服务进程尽量的状态无关”。Pebble为集群服务进程,提供了一个标准的状态存储接口,让用户把状态数据托管给Pebble,这样用户就无需自己去维持复杂的状态有效性和伸缩性了

案例

  • 现在作为腾讯游戏服务gcloud的一个产品

orleans

一套新游戏服务器解决方案Bulls提案_第4张图片
orleans-2.png

https://dotnet.github.io/orleans/

主要特点

  1. 无状态的N层模型在存储层解决分区问题。它经常需要添加缓存层来得到可接受的性能,增加了复杂性并且引入了缓存一致性问题
  2. Orleans允许你在内存中保存状态-低延迟
  3. Orleans允许你写简单的单线程C#代码来使用actor间的异步消息传递处理并发-简化并发
  4. 虚拟Actor
    • Actor的物理实例完全被抽象出来,并由Virtual Actor所在的运行时自动管理
    • Actor的位置是透明的
  5. 集群

案例

  • Xbox Halo4 and Halo5

vert.x

通过前面的分析我们得出一些特点

  • 单线程
  • 异步
  • actor like
  • stateful
  • 分布式能力
  • 协程
  • 服务化
  • 充分利用cpu

但是之前的框架要么是C/Lua,要么是C++,要么是C#

而我们需要的是Java

主要特点

  • Scale
    • Eclipse Vert.x is event driven and non blocking. This means your app can handle a lot of concurrency using a small number of kernel threads. Vert.x lets your app scale with minimal hardware.
  • vert.x core
    • Don’t call us, we’ll call you、Multi-Reactor
      • netty eventloop
    • The Golden Rule - Don’t Block the Event Loop
    • JVM生态系统中有很多同步API
      • 在单独的worker pool执行
    • Verticle
      • actor like
    • Event Bus
      • The event bus allows different parts of your application to communicate with each other
      • 消息通讯
    • Shared data - local maps and clustered distributed maps
    • 集群管理
  • 提供了如Web Client和Data Access等异步组件
  • 其他
    • 在同一JVM内部进行通信时,对象将作为Verticle之间的内存引用传递,没有开销
  • vertx-lang-kotlin-couroutines
    • 引入kotlin的协程能力

观点

  1. vert.x可以作为Bulls架构原型实现的框架
    • 基于netty实现,开源,可控
  2. 整体还是actor like
    • 具体如何抽象actor看项目本身
    • 组件和机制都有

RoadMap

  1. 用vert.x重写某上线手游典型业务
    • 主要做技术验证
  2. 整合bulls框架最佳实践
    • demos、docs
    • 压测、发布
  3. 整合游戏服务器开发常用的通用组件
    • 如id-generator
  4. 持续关注loom
  5. Akka作为备选技术方案或考虑自实现

附一些有用的参考链接

  1. Go语言出现后,Java还是最佳选择吗?
    • 建议好好阅读一下,很多思路和我想的一致
    • Question
      • Vert.x自己实现的mongodb client用的线程池是自己的线程池还是vert.x的线程池?
  2. 并发之痛 Thread,Goroutine,Actor
    • 对并发这块梳理的很好
  3. Pattern: Responsibility-Oriented Game Server
    • 如何设计一个千万人在线的MMO游戏?
    • 中文翻译标题有点取巧
  4. Making The Case For Building Scalable Stateful Services In The Modern Era
    • 可扩展的有状态服务
    • 介绍了stateful
  5. vert.x
    • 知乎关注圆胖肿
    • Vert.x中国用户组

QA

  1. 应该选择哪个协程方案,是否一定要用kotlin协程?是否要等loom
    • 如果loom出来是否可切换
    • 如果发现协程有坑,是否可以换其他异步方案
  2. 分布式架构
    • 进程只是逻辑单元,和区服无关
    • 玩家散落在不同的进程
    • 那么对公共玩法的进程是否有单点问题
    • 另外同一个区服的不同进程玩家如何交互?数据共享问题
      • 进程间相互通讯去修改数据
    • 同一个玩家如果再次登录路由时发现原有节点负载已经很高时,路由到其他节点,如何保证数据一致性问题
    • 增加节点或者减少节点
      • 路由算法等问题
      • 缩容的进程如何处理
  3. vert.x自实现的三方异步client的thread应该也是用vert.x自己的线程池
    • 这样的话性能更好
    • 但是如果用dubbo的client,那么dubbo是自己的netty eventloop,还是要异步投递的,性能不够好
  4. 未来会考虑云
  5. vert.x eventbus是否有问题?有哪些问题?
    • 进程间通讯也有grpc

你可能感兴趣的:(一套新游戏服务器解决方案Bulls提案)