netty服务端设计,适用于app、游戏等场景
一、系统目标
使用netty搭建tcp服务器,支持长连接,短连接模式。
使用protobuf作为主要的数据编码手段
将数据传输隐藏在框架内,使得业务开发人员可直接关注业务实现,无需关注通信,甚至无需关心多线程问题。
可扩展的架构,方便接入其他中间件。
将编码任务简单化。
二、系统分层
Class Diagram.png2.1 通信层
绝大部分的功能,netty已经支持了。
2.1.1 通信协议
服务器在收到数据之后,需要进行如下的过滤判断:
a、数据是否合法
b、数据版本是否支持
c、如何解析客户端数据,客户端数据类型是什么
d、客户端期望服务端提供什么服务
数据包具体协议设计如下:
长度(4字节)| 时间戳(4字节) | 版本 (2字节) | 服务id (2字节)| 数据(不定长)| 校验码(2字节)
2.1.2 Mapping
由上述协议传输的数据中的服务id,具体对应到哪个类的哪个方法执行
上传数据如何解析由方法参数决定
下发数据如何解析由方法返回值决定
如返回值为void,且方法中带异步回调参数,则一般为异步执行
如返回值不为void,则为同步执行。
服务id计算,(65536上限,一般应用足够了):
所有Controller的method列表,使用字典顺序排序的序号,并持久化序号和method的对应关系,新版本新的Controller的排序基数使用上个版本排序最大值。
2.1.3 通信用POJO
在网络层为protobuf生成,.proto文件,可以根据method方法参数生成。
具体规则为:
int Calc.add(int a,int b);
则生成
syntax = "proto3";
package com.project.net.pojo;
message CalcAdd{
int32 a = 1;
int32 b=2;
}
void Articals.add(Artical a);
syntax = "proto3";
package com.project.net.pojo;
message ArticalsAdd{
//Artical字段
}
不支持Map或List<Map>作为参数
返回值规则:
返回值为简单型,则可使用通用的.proto文件
message IntResult{
int32 result =1 ;
}
message StringResult{
string result = 1;
}
客户端解析规则:
在程序内内置服务id和.proto的对应关系,并可在线更新或整体更新。
2.2 线程管理
可配置使用额外的线程管理,或是直接使用netty自己的工作线程。
额外的线程管理,设计如下:
2.2.1 逻辑线程
有必要将逻辑线程和工作线程分开
a、逻辑线程为单线程,整个服务器单元只有一个逻辑线程
b、原则上,逻辑线程只处理逻辑,不做重度任务处理
c、如涉及长时间任务,将任务投递到工作线程上。
优点:无线程安全问题,可放心大胆使用hashmap、hashset等数据结构。
2.2.2 任务线程
a、处理重任务,如数据库操作等
b、非线程安全,原则上不存放线程共享数据
c、框架应提供齐全的工具类以规避线程安全问题,保证在开发人员水平参差不齐的情况下仍然不影响业务逻辑的开发。
d、将缩小异常范围,以减少对整个系统的损害。
e、提供线程退出机制。
2.2.3 任务队列
上述任务线程的共享的队列。
2.2.4 异步回调机制
在工作线程中做完任务之后,可以将结果投递逻辑线程
也可以直接与客户端通信,并主动关闭连接。
2.3. Controller层
设计思路可以参考spring mvc。或直接整合spring、jfinal等框架(待研究)。
2.3.1 java class
Controller可设计单例模式
2.3.2 method
可支持aop技术
2.3.3 参数解析、结果解析
参见2.1.3 节