在Ceph的网络通信模块里,早期一直使用 Simple 这个网络通信模块。由于其实现简单,最早被ceph采用并用于生产环境。其最大的缺陷是:针对每个Connection创建两个线程,一个用于接收消息,一个用于发送消息。在大规模的集群环境下,随着连接数的增多会产生大量的用于通信的线程,极大的影响性能。
ceph在L版本中把Async网络通信模型做为默认的通信方式。Async实现了IO的多路复用,使用共享的线程池实现异步发送和接收任务。
本文主要介绍Async的的Epol + 线程池的 实现模型,主要介绍基本框架和关键实现。本文的思路是首先概要介绍相关的类,在介绍类时主要关注其数据结构和相关的操作。 其次介绍网络通信的核心流程:server端sock的监听和接受连接,客户端如何主动发起连接。消息的发送和接收主要流程。
类NetHandler 封装了Socket的基本的功能。
class NetHandler {
int generic_connect(const entity_addr_t& addr,
const entity_addr_t& bind_addr,
bool nonblock);
CephContext *cct;
public:
explicit NetHandler(CephContext *c): cct(c) {}
//创建socket
int create_socket(int domain, bool reuse_addr=false);
//设置socket 为非阻塞
int set_nonblock(int sd);
//当用exec起子进程时:设置socket关闭
void set_close_on_exec(int sd);
//设置socket的选项:nodelay,buffer size
int set_socket_options(int sd, bool nodelay, int size);
//connect
int connect(const entity_addr_t &addr, const entity_addr_t& bind_addr);
//重连
int reconnect(const entity_addr_t &addr, int sd);
//非阻塞connect
int nonblock_connect(const entity_addr_t &addr, const entity_addr_t& bind_addr);
//设置优先级
void set_priority(int sd, int priority);
}
Worker类是工作线程的抽象接口,同时添加了listen和connect接口用于服务端和客户端的网络处理。其内部创建一个EventCenter类,该类保存相关处理的事件。
class Worker {
std::atomic_uint references;
EventCenter center; //事件处理中心, 处理该center的所有的事件
// server 端
virtual int listen(entity_addr_t &addr,
const SocketOptions &opts, ServerSocket *) = 0;
// client主动连接
virtual int connect(const entity_addr_t &addr,
const SocketOptions &opts, ConnectedSocket *socket) = 0;
}
类PosixWorker实现了 Worker接口。
class PosixWorker : public Worker {
NetHandler net;
int listen(entity_addr_t &sa,
const SocketOptions
&opt,ServerSocket *socks) override;
int connect(const entity_addr_t &addr,
const SocketOptions &opts,
ConnectedSocket *socket) override;
}
int PosixWorker::listen(entity_addr_t &sa, const SocketOptions &opt,ServerSocket *sock)
函数PosixWorker::listen 实现了Server端的sock的功能:底层调用了NetHandler的功能,实现了socket 的 bind ,listen等操作,最后返回ServerSocket对象。
int PosixWorker::connect(const entity_addr_t &addr, const SocketOptions &opts, ConnectedSocket *socket)
函数PosixWorker::connect 实现了主动连接请求。返回ConnectedSocket对象。
class NetworkStack : public CephContext::ForkWatcher {
std::string type; //NetworkStack的类型
ceph::spinlock pool_spin;
bool started = false;
//Worker 工作队列
unsigned num_workers = 0;
vector workers;
}
类NetworkStack是 网络协议栈的接口。PosixNetworkStack实现了linux的 tcp/ip 协议接口。DPDKStack实现了DPDK的接口。RDMAStack实现了IB的接口。
class PosixNetworkStack : public NetworkStack {
vector<int> coreids;
vector<std::thread> threads; //线程池
}
Worker可以理解为工作者线程,其一一对应一个thread线程。为了兼容其它协议的设计,对应线程定义在了PosixNetworkStack类里。
通过上述分析可知,一个Worker对应一个线程,同时对应一个 事件处理中心EventCenter类。
EventDriver是一个抽象的接口,定义了添加事件监听,删除事件监听,获取触发的事件的接口。
class EventDriver {
public:
virtual ~EventDriver() {} // we want a virtual destructor!!!
virtual int init(EventCenter *center, int nevent) = 0;
virtual int add_event(int fd, int cur_mask, int mask) = 0;
virtual int del_event(int fd, int cur_mask, int del_mask) = 0;
virtual int event_wait(vector &fired_events, struct timeval *tp) = 0;
virtual int resize_events(int newsize) = 0;
virtual bool need_wakeup() { return true; }
};
针对不同的IO多路复用机制,实现了不同的类。SelectDriver实现了select的方式。EpollDriver实现了epoll的网络事件处理方式。KqueueDriver是FreeBSD实现kqueue事件处理模型。
类EventCenter是保存所有事件,并提供了处理事件的相关函数。
FileEvent
FileEvent事件,也就是socket对应的事件。
struct FileEvent {
int mask; //标志
EventCallbackRef read_cb; //处理读操作的回调函数
EventCallbackRef write_cb; //处理写操作的回调函数
FileEvent(): mask(0), read_cb(NULL), write_cb(NULL) {}
};
TimeEvent
struct TimeEvent {
uint64_t id; //时间事件的ID号
EventCallbackRef time_cb; //事件处理的回调函数
TimeEvent(): id(0), time_cb(NULL) {}
};
类Poller 用于轮询事件,主要用于DPDK 模式。在PosixStack模式里没有用。
EventCenter
Class EventCenter {
//外部事件
std::mutex external_lock;
std::atomic_ulong external_num_events;
deque external_events;
//socket事件, 其下标是socket对应的fd
vector file_events;
//时间事件 [expire time point, TimeEvent]
std::multimap time_events;
//时间事件的map [id, iterator of [expire time point,time_event]]
std::mapstd ::multimap ::iterator> event_map;
//触发执行外部事件的fd
int notify_receive_fd;
int notify_send_fd;
EventCallbackRef notify_handler;
//底层事件监控机制
EventDriver *driver;
NetHandler net;
// Used by internal thread
//创建file event
int create_file_event(int fd, int mask, EventCallbackRef ctxt);
//创建time event
uint64_t create_time_event(uint64_t milliseconds, EventCallbackRef ctxt);
//删除file event
void delete_file_event(int fd, int mask);
//删除 time event
void delete_time_event(uint64_t id);
//处理事件
int process_events(int timeout_microseconds);
//唤醒处理线程
void wakeup();
// Used by external thread
void dispatch_event_external(EventCallbackRef e);
}
类EventCenter,主要保存事件(包括fileevent,和timeevent,以及外部事件)和 处理事件的相关的函数。
处理事件
int EventCenter::process_events(int timeout_microseconds, ceph::timespan *working_dur)
函数process_event处理相关的事件,其处理流程如下:
在这里,内部事件指的是通过 epoll_wait 获取的事件。外部事件(external event)是其它投送的事件,例如处理主动连接,新的发送消息触发事件。
在类EventCenter里定义了两种方式向EventCenter里投递外部事件:
//直接投递EventCallback类型的事件处理函数
void EventCenter::dispatch_event_external(EventCallbackRef e)
//处理func类型的事件处理函数
void submit_to(int i, func &&f, bool nowait = false)
类AsyncMessenger 主要完成AsyncConnection的管理。其内部保存了所有Connection相关的信息。
class AsyncMessenger : public SimplePolicyMessenger {
//现在的Connection
ceph::unordered_map conns;
//正在accept的Connection
set accepting_conns;
//准备删除的 Connection
set deleted_conns;
}
Processor类
class Processor {
NetHandler net;
Worker *worker;
ServerSocket listen_socket; //监听的socket
EventCallbackRef listen_handler; //accept的处理函数
对应 C_processor_accept,其对应Processor::accept()函数
}
class AsyncConnection : public Connection {
//对应的socket
ConnectedSocket cs;
//发送消息队列
map<int, list > > out_q;
//对应的工作者线程
Worker *worker;
//对应的事件中心,也就是本Connection的所有的实际都有center处理
EventCenter *center;
}