做服务器的开发工作也有一年了,得益于最近的业务工作没有那么的繁忙,让我有了些许的空闲时间来好好思考这一年时间的点点滴滴
虽然项目千奇百怪各不相同,但其实大部分的时候我们都是在做重复的事情,比如:网络,数据库。
刚好最近在研究 ARK 开源项目,从中的学习到了一些优秀的设计,今天在此记录一下 ARK 中的网络设计以及我自己的一些思考,以免我这榆木脑袋过几天就忘记了
核心问题?
- 网络协议多而风格迥异,如何做到 高性能 + 业务层无感知(只需要配置使用那种协议?
- 有关高性能的话题就涉及到一些底层的
epoll
,multi-reactor
之类的话题,我自己也还在持续学习中,大家可以关注我的一些关于io
的博客,在这儿就不赘述了,本文更多的侧重于如何设计好网络这部分,让各种网络协议可以通用的切换。
ARK 中的 Net 插件
- 首先是
ARK
中的三大基石,项目中的一些设计都是基于他们的
net message
- 所有的网络消息都会被解析成为如下的格式
- 对于流式的消息,先取包头进行解析,再解析body
- udp,ws 是 package 形式的消息,收到一个包就直接进行解析即可,解析成为如下的格式
1
2
3
4
5
6
7
8
9
10
11struct AFNetMsg
{
// head
uint16 protocolID
uint32 length
uint64 actor_id_{0}; // Actor id
uint32 src_bus_{0}; // Source bus id
uint32 dst_bus_{0}; // Destination bus id
// body
char* data // 具体的业务数据
}
net event
- 描述网络事件,新连接建立 & 连接断开
- 这个目前正在考虑需不需要和 net message 进行合并
session
- 一个新连接建立会对应一个
session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class AFSession
{
uint32_t head_len_{0}; // 定义消息头的长度,收到的消息按照这个长度进行解析
conv_id_t session_id_{0}; // 每一个连接会对应一个 sessionID,连接进来就创建一个 id
AFBuffer buffer_; // 对应流式数据(tcp),收到消息之后直接存入这个可自动伸缩的 buffer 中
// TODO: merge msg_queue and event_queue together?
// cuz recv_msg is a event too.
AFLockFreeQueue<AFNetMsg*> msg_queue_;
AFLockFreeQueue<AFNetEvent*> event_queue_;
const SessionPTR session_; // 具体的连接,源码中这个是一个范型
// 描述状态
volatile bool connected_{false};
volatile bool need_remove_{false};
}
如何适配各种协议,来进行统一的管理呢?
- 服务器这件事情其实说起来也很简单,就是与连接(笼统的说法,udp是没有连接的概念)进行发包 & 收包
- 这儿就可以进行一次抽象,分成两层:
- 上层:每一个连接对应一个
session
,我维护好所有的session
即可,定期的遍历session
,处理每个session中的 msg & event- 这儿我有一个想法就是不遍历所有的session,有消息的才进行遍历(类似于epoll的做法)
- 下层:负责收包并且解析成上面所说的那种格式(
AFNetMsg
,并且可以进行发送数据
- 上层:每一个连接对应一个
- 每个session中对应了一个
const SessionPTR session_
,把这个部分抽象成一个连接接口
即可, 类似于这样
1 | class ConnInterface |
这样我可以各种协议类型的 server 只需要:
- 建立socket + bind port + listen
- accept 一个 ConnInterface + 创建session,抛到上层即可
这样我各种业务的处理函数可以在上层注册(协议号+处理函数)
这样就实现了
业务
和网络
的分离,并且将各种协议糅合在一起,很棒