-
Notifications
You must be signed in to change notification settings - Fork 12
Home
stupid是一个基于java语言的框架,设计用来开发高性能的游戏服务器但是不限于游戏服务器。
- 架构简述
设计初衷出发于游戏服务器引擎,所以我从游戏服务器的角度来描述。游戏服务器的承载最后都会摊派到服务器的几个资源:CPU,内存,硬盘等。这其中CPU负责运算,内存用来存储游戏热数据,经常会用到一些时空权衡的思想,硬盘用来最终落地数据,或者叫做数据固化,考量的主要是容量和iops以及带宽。
那么设计良好的引擎模型一定是有以下特点:
- 可以最大限度(或者最优或者最合适)的利用其这些资源,也就是首先要做到可以合理的scale up纵向扩展。
- 接口足够少,设计足够简单。方便程序员学习和快速上手。
- 设计足够灵活。方便功能切入和扩展。
- 代码如湿。
下面来个stupidbird的内部组件鸟瞰:
这基本是所有的切入组件,了解这些你就已经get到了引擎的一切:
-
Node。代表一个服务节点,目前有两个实现:ServerNode和Commander。接口也足够简单和少。
- config。来为节点加载配置。当然不调用的话节点会使用默认的配置。
- setDispatcher。用来设置节点的分发器,用来分发节点收到的消息。
- executor。用来获取节点的执行器。
- registerShutdownHook。用来注册停机钩子。
- setIoHandler。用来设置io处理器,也是业务的切入入口。
-
NodeDispatcher。是分发器,设计用来分发消息。
-
NodeExecutor。是执行器,用来处理网络消息或者系统内部事件。
-
ObjectKeyGenerator。是key生成器,执行器用此来生成绑定对象的key。
-
NodeIoHandler。是io处理器,也是业务的入口,这里用来创建和业务对象绑定的网络会话。
-
NodeBindableSession。这就是绑定业务对象的网络会话。
然而在其中,你只需要切入实现两个部分:NodeIoHandler
和NodeBindableSession
。
public class CommanderServerApp {
public static void main(String[] args) throws Exception {
Commander commander = new Commander();
commander.setIoHandler(new ServerExampleIoHandler(commander));
commander.startup();
}
}
上面这几行码你就可以启动一个Commander服务器,并且使用telnet与之交互。然后再在上头说到的两个部分注入你的业务。
- 整体层次
-
消息层 : protobuf packet
-
网络层 : mina
-
序列化层 : protobuf
-
执行层 : actor
-
缓存层 : local + redis strategy
-
数据层 : strategy - mongo + morphia
-
执行层
-
执行层使用多线程驱动,线程数量可配置,完全解开线程结构和业务结构之前的耦合。
-
Poision模式进行执行器的graceful stop。
-
线程安全使用acotr模式。actor之间通信使用tell and forget。
-
使用对象绑定线程的方式执行,抽象出keyGenerator供使用者切入。
-
缓存层
整体抽象抽了DBAgent这层中间层。实现EntityDBService接口并且聚合了缓存接口KVClient,缓存这里可以实现多个策略,目前有redis和本地缓存。不过目前代理这里设计的不够完善,下个版本去搞。
-
LocalCache
-
Redis
-
数据层
数据层使用异步更新的方式,解耦数据生产者和消费者。
-
抽象出EntityDBService策略。目前有mongodb实现和cassandra实现。Entity代表会引入ORM的概念,关于用不用ORM取决于它能让我有多方便和它有多降低效率。
-
提倡使用mongodb3.2+。原因是3.2+以后引擎由原来的mmapv1换成了wiredtigger。wiredtigger整体表现比较稳定,而且很多特性跟innodb很像。
-
cluster集群的方案后续添加。
-
模板层
目前使用基于@hawk的orm方案。但是后期会重构这里,使用DSL的模式,让这一切都自动化。
- 脚本支持
引擎内置js脚本引擎支持,可以支持把js脚本映射成java类,其中Node的启动配置就是通过这种方式加载的。
js脚本:
// ===== import begin =====//
//===== import end =====//
config.name = "game";
// bind ip
config.bindIp = "0.0.0.0";
// bind port
config.port = 8001;
// telnet ip
config.telnetIp = "0.0.0.0";
// telnet port
config.telnetPort = 7001;
// 消息处理线程个数
config.msgThreadCount = 4;
// 数据处理线程个数
config.dbThreadCount = 4;
// 数据库连接
config.dbHost = "127.0.0.1";
// mongo:27017
// cassandra:9042
config.dbPort = 27017;
config.dbName = "stupidbird";
对应的映射java文件:
/**
* 游戏服务器配置;
*
* @author crazyjohn
*
*/
public class GameServerConfig extends ServerConfig {
private String dbHost;
private int dbPort;
private String dbName;
private int dbThreadCount;
public String getDbHost() {
return dbHost;
}
public void setDbHost(String dbHost) {
this.dbHost = dbHost;
}
public int getDbPort() {
return dbPort;
}
public void setDbPort(int dbPort) {
this.dbPort = dbPort;
}
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public int getDbThreadCount() {
return dbThreadCount;
}
public void setDbThreadCount(int dbThreadCount) {
this.dbThreadCount = dbThreadCount;
}
}