上个项目结束后,转做手游。新接手的项目,需要从头构建服务器。之前一直做C++,手游以Java居多,同时我也想做些不同的东西,于是时隔8年后再次捡起Java。好在服务器开发的领域知识是主要的,语言是其次,但一些技巧惯例还是需要慢慢熟知。这个系列博客,就是记录以当前项目需求为准则,从头构建一个游戏服务器DEMO的点滴。
新项目是休闲类游戏,服务器需求类似刀塔传奇,是典型的请求响应式。采取分区分服机制,但实际设计以全区全服为目标。客户端通过短连接向服务器主动请求,服务器返回响应数据。不用HTTP是为了减少网络包大小,同时有更多可控性。不用UDP是希望尽量利用TCP的特性保证数据包的可靠传输。
鉴于需求较简单,服务器只分了三种,Login Server、Game Server和Journal Server,服务器间通过socket通信,支持自动重连。数据存储使用Mysql和阿里云OSS,缓存使用Redis。项目是典型的IO密集型应用。
Login Server负责账户创建和登录等,同时也管理服务器列表。一个大区可以有若干个,采用DNS负载均衡,客户端行为主要有三种,注册、登录和选择服务器,每次登录流程需要连接同一个服务器。
Game Server负责具体的游戏逻辑。主要分三层:网络、逻辑和数据存取。网络使用Netty,开一个boss线程和CPU数目的worker线程。逻辑单线程,有消息队列,网络层收到的消息放入该队列,等待处理。逻辑若有阻塞当前线程的数据存取需求,比如读写数据库或存档上传下载等,封装成任务交由数据存取层处理。数据存取层负责处理同步数据请求任务,使用线程池,包含两倍CPU数目的固定线程。任务执行完成后,逻辑线程进行后续的操作。
Journal Server负责记录游戏运营日志。
数据存储采用了Mysql和阿里云OSS。Mysql存储主要游戏数据,便于数据查询分析。玩家数据以存档形式存储在OSS。
为了提高读写效率,增加并发,GameServer采用了缓存策略,有两级缓存:内存和Redis。平时逻辑操作的都是内存数据。玩家进入游戏加载数据时,首先查看内存,没有则查看Redis,没有则查询数据库和OSS,加载后也会将数据缓存到Redis。
由于逻辑操作的都是内存数据,若服务器发生宕机等情形,会导致数据丢失,即所谓回档。为解决这个问题,数据会以30秒间隔定时写入Redis,300秒间隔写入数据库和OSS。同时,对于涉及金钱等的关键操作,每次都会标记数据记录为emergent,逻辑线程一旦检测到emergent标志,则会立刻发起写入Redis、数据库和OSS的同步任务。这样不能保证数据百分百不会丢失,但已足够实用有效。
本文是对目前项目服务器架构的简要介绍,一些细节问题将在后续文章中具体论述。
公共库仓库: JMetazion
服务器示例仓库: JGameDemo
新建QQ交流群:330459037