目录
ceph的网络通信
原文:https://www.dazhuanlan.com/2019/08/21/5d5c34bb785c1/
https://blog.csdn.net/shuningzhang/article/details/90206293
ceph网络通信模式分类
ceph的网络通信的源码在src/msg下,有Ceph Messager的三个实现:simple,async,xio。
simple模式相对比较简单,一个链接的每一端都有两个线程,分别负责读和写,当集群规模很大的时候,会有大量线程,占用大量资源,限制了网络性能。(SimpleMessenger是网络层最早的实现,当时的网络编程模型还是线程当道,因此simple采用的就是这种简单粗暴的线程模型)
async模式基于Epoll + EventCenter,这也是当前的默认消息机制。(async主要是国内ceph大神麦子迈 王豪迈大神的实现的。随着linux提供epoll这样的系统调用,可以提供基于事件的IO多路服用,越来越多的网络开始使用该系统调用来实现高并发通信。比如libevent。async也是如此。)
xio模式基于开源网络通信库accelio来实现,目前处于试验阶段。(xio使用了开源网络通信模块accelio,需要依赖第三方的库,目前也在实验阶段。)
从Messenger.cc 这个类中可以看出在create Messager实例的时候根据type选择网络框架是simple/async/xio.其中选择xio的话需要的话需要定义HAVE_XIO 这个宏,并且xio支持tcp/ip。infiniband 这两种协议,而前两种支持tcp/ip 协议
Messenger *Messenger::create(CephContext *cct, const string &type,
entity_name_t name, string lname,
uint64_t nonce, uint64_t cflags)
{
if (r == 0 || type == "simple")
return new SimpleMessenger(cct, name, std::move(lname), nonce);
else if (r == 1 || type.find("async") != std::string::npos)
return new AsyncMessenger(cct, name, type, std::move(lname), nonce);
#ifdef HAVE_XIO
else if ((type == "xio") &&
cct->check_experimental_feature_enabled("ms-type-xio"))
return new XioMessenger(cct, name, std::move(lname), nonce, cflags);
#endif
lderr(cct) << "unrecognized ms_type '" << type << "'" << dendl;
return nullptr;
}
Ceph通信框架设计模式
设计模式:订阅发布模式(Subscribe/Publish),又名观察者模式,它意图是“定义对象间的一种一对多的依赖关系,
当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。
Ceph通信框架流程图
![](https://img-blog.csdnimg.cn/img_convert/33fe3365529ffe68035d46b24904de0b.png)
步骤:
- Accepter监听peer的请求, 调用 SimpleMessenger::add_accept_pipe() 创建新的 Pipe 到 SimpleMessenger::pipes 来处理该请求。
- Pipe用于消息的读取和发送。该类主要有两个组件,Pipe::Reader,Pipe::Writer用来处理消息读取和发送。
- Messenger作为消息的发布者, 各个 Dispatcher 子类作为消息的订阅者, Messenger 收到消息之后, 通过 Pipe 读取消息,然后转给 Dispatcher 处理。
- Dispatcher是订阅者的基类,具体的订阅后端继承该类,初始化的时候通过 Messenger::add_dispatcher_tail/head 注册到 Messenger::dispatchers. 收到消息后,通知该类处理。
- DispatchQueue该类用来缓存收到的消息, 然后唤醒 DispatchQueue::dispatch_thread 线程找到后端的 Dispatch 处理消息。
![](https://img-blog.csdnimg.cn/img_convert/f195edd4720eab14b7bf62c80f7afa21.png)
ceph_message_2
Ceph通信框架类图
![](https://img-blog.csdnimg.cn/img_convert/9a37a1e884e539c622f4f6fa060fe8d4.png)
架构上采用 Publish/subscribe(发布/订阅) 的设计模式.
simple框架
src/common/config_opts.h
OPTION(ms_type, OPT_STR, "simple")
SimpleMessenger 是simple框架具体的实现。
从这个构造函数中可以看到SimpleMessenger 会在构造函数中分别建立结束accepter和发送dispatch_queue 两个线程
SimpleMessenger::SimpleMessenger(CephContext *cct, entity_name_t name, |
accepter和发送dispatch_queue 线程 建立代码
//这里以accepter为例看看是如何建立接受线程的
class Accepter : public Thread
{
SimpleMessenger *msgr;
bool done;
int listen_sd;
uint64_t nonce;
int shutdown_rd_fd;
int shutdown_wr_fd;
int create_selfpipe(int *pipe_rd, int *pipe_wr);
public:
Accepter(SimpleMessenger *r, uint64_t n)
: msgr(r), done(false), listen_sd(-1), nonce(n),
shutdown_rd_fd(-1), shutdown_wr_fd(-1)
{}
void *entry() override;
void stop();
int bind(const entity_addr_t &bind_addr, const set<int> &avoid_ports);
int rebind(const set<int> &avoid_port);
int start();
};
//可以看到accepter 是thread的子类
//所以我们先看看其start函数
int Accepter::start()
{
ldout(msgr->cct, 1) << __func__ << dendl;
// start thread
create("ms_accepter");
return 0;
}
//在start中通过create来新建一个接收线程,其name是ms_accepter
//其次我们在看看这个线程只要执行的工作,其在entry中实现
void *Accepter::entry()
{
ldout(msgr->cct, 1) << __func__ << " start" << dendl;
int errors = 0;
int ch;
struct pollfd pfd[2];
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = listen_sd;
pfd[0].events = POLLIN | POLLERR | POLLNVAL | POLLHUP;
pfd[1].fd = shutdown_rd_fd;
pfd[1].events = POLLIN | POLLERR | POLLNVAL | POLLHUP;
#开始polling
while (!done)
{
ldout(msgr->cct, 20) << __func__ << " calling poll for sd:" << listen_sd << dendl;
int r = poll(pfd, 2, -1);
if (r < 0)
{
if (errno == EINTR)
{
continue;
}
ldout(msgr->cct, 1) << __func__ << " poll got error"
<< " errno " << errno << " " << cpp_strerror(errno) << dendl;
break;
}
#检查是否polling返回error
if (pfd[0].revents & (POLLERR | POLLNVAL | POLLHUP))
{
ldout(msgr->cct, 1) << __func__ << " poll got errors in revents "
<< pfd[0].revents << dendl;
break;
}
if (pfd[1].revents & (POLLIN | POLLERR | POLLNVAL | POLLHUP))
{
// We got "signaled" to exit the poll
// clean the selfpipe
#检查是否要退出polling
if (::read(shutdown_rd_fd, &ch, 1) == -1)
{
if (errno != EAGAIN)
ldout(msgr->cct, 1) << __func__ << " Cannot read selfpipe: "
<< " errno " << errno << " " << cpp_strerror(errno) << dendl;
}
break;
}
if (done) break;
// accept
#走到这里polling函数就正常返回了,通过accept函数开始接收
sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int sd = ::accept(listen_sd, (sockaddr *)&ss, &slen);
if (sd >= 0)
{
int r = set_close_on_exec(sd);
if (r)
{
ldout(msgr->cct, 1) << __func__ << " set_close_on_exec() failed "
<< cpp_strerror(r) << dendl;
}
errors = 0;
ldout(msgr->cct, 10) << __func__ << " incoming on sd " << sd << dendl;
#实际从pipe中读入msg
msgr->add_accept_pipe(sd);
}
else
{
ldout(msgr->cct, 0) << __func__ << " no incoming connection? sd = " << sd
<< " errno " << errno << " " << cpp_strerror(errno) << dendl;
if (++e