数据结构
封装了socket的基本使用
class NetHandler {
public:
int connect(const entity_addr_t &addr, const entity_addr_t& bind_addr);//连接
int reconnect(const entity_addr_t &addr, int sd);//重连
int set_socket_options(int sd, bool nodelay, int size);//设置属性
};
封装了NetHandler的接口,同时一个worker实例会绑定一个处理线程id
class PosixWorker : public Worker {
ceph::NetHandler net;//NetHandler实例
public:
PosixWorker(CephContext *c, unsigned i)//输入必须要有线程对应数据组的id
: Worker(c, i), net(c) {}
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;//封装了客户端连接的一系列接口
}
单例模式,维护统一的worker线程池,以及线程池中线程的处理函数
class NetworkStack : public CephContext::ForkWatcher {
std::string type;//posix,rdma,dpdk
unsigned num_workers = 0;//线程数
Spinlock pool_spin;
bool started = false;//是否已启动
std::function<void ()> add_thread(unsigned i);//具体线程的处理函数
public:
static Worker* create_worker(
CephContext *c, const string &t, unsigned i);//创建worker实例
void start();//启动线程池
void stop();//停止线程池
virtual Worker *get_worker();
Worker *get_worker(unsigned i) {
return workers[i];
}
EventDriver是一个抽象的接口,定义了添加事件监听,删除事件监听,获取触发的事件的接口,我们主要使用EpollDriver
class EventDriver {
public:
virtual ~EventDriver() {} // we want a virtual destructor!!!
virtual int init(EventCenter *center, int nevent) = 0;//对应初始化epoll_create
virtual int add_event(int fd, int cur_mask, int mask) = 0;// epoll_ctl
virtual int del_event(int fd, int cur_mask, int del_mask) = 0;// epoll_ctl
virtual int event_wait(vector<FiredFileEvent> &fired_events, struct timeval *tp) = 0;// epoll_wait
virtual int resize_events(int newsize) = 0;
virtual bool need_wakeup() { return true; }
}
保存所有事件以及相关事件的处理
class EventCenter {
public:
static const int MAX_EVENTCENTER = 24;//线程的最大上线
private:
struct FileEvent {//socket对应的事件
int mask;//读写等标识
EventCallbackRef read_cb;
EventCallbackRef write_cb;
FileEvent(): mask(0), read_cb(NULL), write_cb(NULL) {}
};
struct TimeEvent {//暂时还没弄清楚干嘛的,主流程不太需要
uint64_t id;
EventCallbackRef time_cb;
TimeEvent(): id(0), time_cb(NULL) {}
};
private:
CephContext *cct;
std::string type; //dpdk,rdma,posix
int nevent; //初始化的事件数量
// Used only to external event
pthread_t owner; //线程实例
std::mutex external_lock;
std::atomic_ulong external_num_events;
deque<EventCallbackRef> external_events;
vector<FileEvent> file_events;
EventDriver *driver;
std::multimap<clock_type::time_point, TimeEvent> time_events;
// Keeps track of all of the pollers currently defined. We don't
// use an intrusive list here because it isn't reentrant: we need
// to add/remove elements while the center is traversing the list.
std::vector<Poller*> pollers;
std::map<uint64_t, std::multimap<clock_type::time_point, TimeEvent>::iterator> event_map;
uint64_t time_event_next_id;
int notify_receive_fd; //用于监听事件的fd
int notify_send_fd; //用于wakeup的fd
ceph::NetHandler net;
EventCallbackRef notify_handler;
unsigned idx;
AssociatedCenters *global_centers = nullptr; //单例,用于保存所有的eventcenter
};
class AsyncMessenger : public SimplePolicyMessenger {
private:
static const uint64_t ReapDeadConnectionThreshold = 5;//deleted_conns达到的水位则真正删除
NetworkStack *stack;//单例
std::vector<Processor*> processors;//posix场景只有一个
DispatchQueue dispatch_queue;//请求分发队列
Worker *local_worker;// worker线程中的一个,目前只用于asyncmessenger来回收conns
uint64_t nonce;//进程识别码
ceph::unordered_map<entity_addr_t, AsyncConnectionRef> conns;//正在连接的conns
set<AsyncConnectionRef> accepting_conns;//刚刚accept的conns
set<AsyncConnectionRef> deleted_conns;//待删除的conns
};
class AsyncConnection : public Connection {
private:
ConnectedSocket cs;//对应的socket
map<int, list<pair<bufferlist, Message*> > > out_q;//发送消息队列
Worker *worker;//工作线程
};
概要分析
主要实例之间的关系
以osd举例
基于事件投递的线程模型
每个EventCenter中会包含notify_receive_fd和notify_send_fd2个字段。
EventCenter::init中会调用pipe来生成这2个fd
上图为整个线程处理的框架,其中在set_owner会向epoll注册notify_receive_fd的读监听事件以及相关的回调处理。
当需要worker线程干活的时候,只需要调用EventCenter::dispatch_event_external函数,worker线程就能够执行传入的回调
下图为worker线程EventCenter::process_events的部分处理流程
主要流程
客户端和服务端正常交互的流程
Client发送请求的异步流程:
AsyncConnection::process负责整个connect的建立以及状态机变化,handle_write负责发送message数据。
整个连接的建立流程
Client和server在建立连接的过程都会最终调到AsyncConnection::process函数,只不过client进入process函数的时候state是STATE_CONNECTING,而server进入process函数的时候状态是STATE_ACCEPTING
Client调用到process的流程上文已经说明,server调用到process的流程如下:
由上图可以看到messenger在start的过程中会注册listen的fd到epoll的监听事件中回调为C_processor_accept。后续流程为
整个连接过程的client和server的状态机变化,以及各个状态的处理逻辑如下图: