Java游戏服务器-概述

上个项目结束后,转做手游。新接手的项目,需要从头构建服务器。之前一直做C++,手游以Java居多,同时我也想做些不同的东西,于是时隔8年后再次捡起Java。好在服务器开发的领域知识是主要的,语言是其次,但一些惯例技巧还是需要慢慢熟知。这个系列博客,就是记录其中点滴。

新项目是休闲类游戏,服务器需求比较简单,采用分区分服机制。客户端通过短连接或长连接向服务器主动请求,服务器返回响应数据。不用HTTP是为了减少网络包大小,同时有更多可控性。不用UDP是希望尽量利用TCP的特性保证数据包的可靠传输。

鉴于需求较简单,服务器只分了两种,LoginServer和GameServer。数据存储使用了Mysql和阿里云OSS,缓存使用了Redis。

LoginServer负责账户创建和登录等,同时也管理服务器列表。一个大区可以有若干个,采用DNS负载均衡,客户端行为主要有三种,注册、登录和选择服务器,每次登录流程需要连接同一个服务器。

GameServer负责具体的游戏逻辑。主要分三层:网络、逻辑和数据存取。网络使用Netty,开一个Boss线程和CPU数目的Worker线程。逻辑单线程,有消息队列,网络层收到的消息放入该队列,等待处理。逻辑若有阻塞当前线程的数据存取需求,比如读写数据库或存档上传下载等,封装成任务交由数据存取层处理。数据存取层负责处理同步数据请求任务,其使用线程池,包含CPU数目的固定线程。任务执行完成后,逻辑线程进行后续的操作。

数据存储采用了Mysql和阿里云OSS。Mysql主要用于角色ID生成和便于信息查询,游戏数据以OSS记录为准。

为了提高读写效率,增加并发,GameServer采用了缓存策略,有两级缓存:内存和Redis。平时逻辑操作的都是内存数据。玩家进入游戏加载数据时,首先查看内存,没有则查看Redis,没有则查询数据库和OSS,加载后也会将数据缓存到Redis。

由于逻辑操作的都是内存数据,若服务器发生宕机等情形,会导致数据丢失,即所谓回档。为解决这个问题,数据会以30秒间隔定时写入Redis,300秒间隔写入数据库和OSS。同时,对于涉及金钱等的关键操作,每次都会标记数据记录为dirty,逻辑线程一旦检测到dirty,则会立刻发起写入Redis、数据库和OSS的同步任务。这样不能保证数据百分百不会丢失,但已足够实用有效。

本文是对目前项目服务器架构的简要介绍,一些细节问题将在后续文章中具体论述。

公共库仓库:JMetazion

服务器示例仓库:JGameDemo