ceph AsyncMessenger模块源码分析(上)

本文详细解析了Ceph中AsyncMessenger模块的工作原理,包括其网络框架的初始化流程,多线程模型下的事件处理机制,以及如何通过epoll进行高效的I/O复用。同时,文章还介绍了AsyncMessenger在Ceph集群中的应用,如套接字绑定、事件监听和消息传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ceph早些版本默认的网络框架是SimpleMessenger,但是SimpleMessenger是多线程模型,对于较大的集群,会产生很多线程,对主机的要求也比较高。当前的版本(mimic)默认的messenger框架是AsyncMessenger,AsyncMessenger可以选择多路IO复用的epoll模型来处理连接和消息,这大大的减少了由于连接数过多带来的主机资源消耗。下面以OSD的ms_public来介绍AsyncMessenger网络模块。

创建ms_public,并初始化内部的一些模块

(1)创建AsyncMessenger

Messenger *ms_public = Messenger::create(g_ceph_context, public_msg_type,
entity_name_t::OSD(whoami), “client”,
getpid(),
Messenger::HAS_HEAVY_TRAFFIC |
Messenger::HAS_MANY_CONNECTIONS);
在create函数中会根据public_msg_type来选择创建不同类型的Messenger,Mimic默认为"async+posix",因此会选择AsyncMessenger,返回如下:
return new AsyncMessenger(cct, name, type, std::move(lname), nonce);

(2)初始化内部的一些模块

stack = NetworkStack::create(cct, type);  //stack为PosixNetworkStack
    return std::make_shared<PosixNetworkStack>(c, t);//t = "posix"  PosixNetworkStack的构造函数, 其继承了NetworkStack类
        PosixNetworkStack::PosixNetworkStack(CephContext *c, const string &t)
            : NetworkStack(c, t)
        {
          vector<string> corestrs;
          get_str_vec(cct->_conf->ms_async_affinity_cores, corestrs);  //为空,所以下面的for循环不进入
          for (auto & corestr : corestrs) {
            string err;
            int coreid = strict_strtol(corestr.c_str(), 10, &err);
            if (err == "")
              coreids.push_back(coreid);
            else
              lderr(cct) << __func__ << " failed to parse " << corestr << " in " << cct->_conf->ms_async_affinity_cores << dendl;
          }
        }
        //运行PosixNetworkStack构造函数之前,要运行NetworkStack构造函数
        NetworkStack::NetworkStack(CephContext *c, const string &t): type(t), started(false), cct(c)
            const uint64_t InitEventNumber = 5000;
            num_workers = cct->_conf->ms_async_op_threads;  //3
            for (unsigned i = 0; i < num_workers; ++i) { //3
                Worker *w = create_worker(cct, type, i);  //type为posix
                    return new PosixWorker(c, i);
                    /*
                      PosixWorker(CephContext *c, unsigned i) : Worker(c, i), net(c) {}
                    */        
                w->center.init(InitEventNumber, i, type);  //EventCenter center;
                    type = t;
                    idx = i;
                    driver = new EpollDriver(cct);  //explicit EpollDriver(CephContext *c): epfd(-1), events(NULL), cct(c), size(0) {}
                    driver->init(this, n);  //n=5000
                        events = (struct epoll_event*)malloc(sizeof(struct epoll_event)*nevent);  //nevent就是InitEventNumber 5000
                        memset(events, 0, sizeof(struct epoll_event)*nevent);
                        epfd = epoll_create(1024);
                        size = nevent;
                    file_events.resize(n);
                    nevent = n;  //n就是InitEventNumber
                    int fds[2];
                    pipe(fds);
                    notify_receive_fd = fds[0];
                    notify_send_fd = fds[1];
                    net.set_nonblock(notify_receive_fd);
                        fcntl(sd, F_GETFL)
                        fcntl(sd, F_SETFL, flags | O_NONBLOCK)  //固定用法,在原来的设置的基础上加一个O_NONBLOCK
                    net.set_nonblock(notify_send_fd);  //实现同上
                workers.push_back(w);  //vector<Worker*> workers;  定义在NetworkStack上
            }  

1)AsyncMessenger中含有一个PosixNetworkStack变量,其继承NetworkStack类,在初始化PosixNetworkStack变量的时候会执行NetworkStack的构造函数
2)在NetworkStack构造函数中会创建PosixWorker,个数由ms_async_op_threads参数取控制,默认为3。每个PosixWorker包含一个center(EventerCenter),center中实现了底层epoll网络模型。
3)每个epoll模型最多可包含5000个描述符,每次最多可同时监听1024个描述符。同时每个center还包含一对pipe变量(notify_receive_fd和notify_send_fd)和对应的回调函数。epoll最开始会监听notify_receive_fd描述符,监听notify_receive_fd描述符是为了处理extern事件的(下面会说到),注意这里只是做了一些初始化操作,仍然没有开始监听事件。

启动PosixNetworkStack中处理线程process_events

stack->start(); //启动处理线程process_events
    for (unsigned i = 0; i < num_workers; ++i) {   //3
        if (workers[i]->is_init())
            continue;
        std::function<void ()> thread = add_thread(i);
        spawn_worker(i, std::move(thread));
            threads.resize(i+1);
            threads[i] = std::thread(func);  //创建线程,并调用func,即开辟线程运行上面的sambda表达式
    }
    started = true;
    lk.unlock();

    for (unsigned i = 0; i < num_workers; ++i)
      workers[i]->wait_for_init();
        std::unique_lock<std::mutex> l(init_lock);  //condition_variable在notify时,会解锁锁定的unique_lock,这里加一个while(!init)是为了防止wait_for_init提前运行完导致上面还没运行的线程卡死
        while (!init)
          init_cond.wait(l);

对于每一个PosixWorker,会做如下操作
1)利用add_thread返回真正的处理函数,spawn_worker负责创建线程并执行add_thread返回的处理函数。workers[i]->wait_for_init();是等待线程必要部分初始化完毕。
2)add_thread函数的调用栈如下

std::function<void ()> thread = add_thread(i);
    Worker *w = workers[i];
    return [this, w]() {
        char tp_name[16];
        sprintf(tp_name, "msgr-worker-%u", w->id);
        ceph_pthread_setname(pthread_self(), tp_name);
        const unsigned EventMaxWaitUs = 30000000;
        w->center.set_owner();  //前面在center
            owner = pthread_self();  //获得thread的ID,和pthread_create返回的一样
            global_centers = &cct->lookup_or_create_singleton_object<EventCenter::AssociatedCenters>("AsyncMessenger::EventCenter::global_center::" + type, true);
            global_centers->centers[idx] = this;   
            if (driver->need_wakeup())
                notify_handler = new C_handle_notify(this, cct);  //仅仅从notify_receive_fd读出数据,没有其他作用
                create_file_event(notify_receive_fd, EVENT_READABLE, notify_handler);  //notify_receive_fd是管道中的接收端,给该描述符增加EVENT_READABLE事件
                    EventCenter::FileEvent *event = _get_file_event(fd);
                        return &file_events[fd];
                    driver->add_event(fd, event->mask, mask);
                        op = cur_mask == EVENT_NONE ? EPOLL_CTL_ADD: EPOLL_CTL_MOD;
                        ee.events = EPOLLET;
                        add_mask |= cur_mask; /* Merge old events */
                        if (add_mask & EVENT_READABLE)
                            ee.events |= EPOLLIN;
                        if (add_mask & EVENT_WRITABLE)
                            ee.events |= EPOLLOUT;
                        ee.data.u64 = 0;
                        ee.data.fd = fd;
                        epoll_ctl(epfd, op, fd, &ee)
                    event->mask |= mask;
                    if (mask & EVENT_READABLE)   
                        event->read_cb = ctxt;  //设置回调函数,前面传进来的notify_handler
                    if (mask & EVENT_WRITABLE)
                        event->write_cb = ctxt;
        w->initialize();
            //void PosixWorker::initialize(){}  为空函数
        w->init_done();
            init_lock.lock();
            init = true;  
            init_cond.notify_all();   //这里是通知下面的wait_for_init退出
            init_lock.unlock();
        while (!w->done) {   //主要处理函数
            ceph::timespan dur;
            int r = w->center.process_events(EventMaxWaitUs, &dur);
                numevents = driver->event_wait(fired_events, &tv);
                    retval = epoll_wait(epfd, events, size, tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
                    numevents = retval;
                    fired_events.resize(numevents);
                    for (j = 0; j < numevents; j++) {
                      int mask = 0;
                      struct epoll_event *e = events + j;
                      if (e->events & EPOLLIN) mask |= EVENT_READABLE;
                      if (e->events & EPOLLOUT) mask |= EVENT_WRITABLE;
                      if (e->events & EPOLLERR) mask |= EVENT_READABLE|EVENT_WRITABLE;
                      if (e->events & EPOLLHUP) mask |= EVENT_READABLE|EVENT_WRITABLE;
                      fired_events[j].fd = e->data.fd;
                      fired_events[j].mask = mask;   
                    return numevents;
                for (int j = 0; j < numevents; j++)
                    event = _get_file_event(fired_events[j].fd);
                        return &file_events[fd];
                    if (event->mask & fired_events[j].mask & EVENT_READABLE)
                        rfired = 1;
                        cb = event->read_cb;
                        cb->do_request(fired_events[j].fd);
                    if (event->mask & fired_events[j].mask & EVENT_WRITABLE)
                        if (!rfired || event->read_cb != event->write_cb)
                            cb = event->write_cb;
                            cb->do_request(fired_events[j].fd);
                    if (external_num_events.load()) {
                      external_lock.lock();
                      deque<EventCallbackRef> cur_process;
                      cur_process.swap(external_events); //拷贝外部事件
                      external_num_events.store(0);  //外部事件技术清零
                      external_lock.unlock();
                      numevents += cur_process.size();
                      while (!cur_process.empty()) {
                        EventCallbackRef e = cur_process.front();
                        ldout(cct, 30) << __func__ << " do " << e << dendl;
                        e->do_request(0);   //对于asyncmessage套接字绑定,则会调用C_submit_event::do_request
                            void do_request(uint64_t id) override {
                                f(); //对于asyncmessage套接字绑定,则会调用[this, &listen_addr, &opts, &r]() { r = worker->listen(listen_addr, opts, &listen_socket);} lambda表达式
                                    //进而调用int PosixWorker::listen(entity_addr_t &sa, const SocketOptions &opt, ServerSocket *sock)
                                    int listen_sd = net.create_socket(sa.get_family(), true);
                                        s = ::socket(domain, SOCK_STREAM, 0)
                                        ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)
                                    net.set_nonblock(listen_sd)
                                        (flags = fcntl(sd, F_GETFL));
                                        fcntl(sd, F_SETFL, flags | O_NONBLOCK)
                                    net.set_close_on_exec(listen_sd);
                                    net.set_socket_options(listen_sd, opt.nodelay, opt.rcbuf_size);
                                    ::bind(listen_sd, sa.get_sockaddr(), sa.get_sockaddr_len());
                                    ::listen(listen_sd, cct->_conf->ms_tcp_listen_backlog);
                                    *sock = ServerSocket(std::unique_ptr<PosixServerSocketImpl>(new PosixServerSocketImpl(net, listen_sd)));  //赋值 listen_socket
                                lock.lock();
                                cond.notify_all();  //通知等待的线程完成
                                done = true;
                                bool del = nonwait;
                                lock.unlock();
                                if (del)
                                    delete this;
                            }
                        cur_process.pop_front();
                      }
                    }
            w->perf_logger->tinc(l_msgr_running_total_time, dur);
        }
        w->reset();
        w->destroy();

可以看到线程处理函数做了如下工作
1)设置notify_receive_fd描述符监听事件以及回调函数:程序中利用create_file_event函数来给notify_receive_fd(pipe中的接收端)设置EVENT_READABLE事件,同时设置对应的回调函数为notify_handler(C_handle_notify),这个回调函数仅仅是将pipe中的数据读出来,并无其他作用,因此它的功能仅仅是唤醒epoll_wait所在的等待线程(同时去处理extern事件)。这里有一个关键性的变量file_events(FileEvent),其保存了描述符对应的事件mask和读写回调函数。
2)调用init_done通知wait_for_init退出等待,因为wait_for_init会等待线程处理函数必要部分初始化结束,因此init_done会调用init_cond.notify_all();通知wait_for_init退出。
3)process_events负责执行epoll_wait,epoll_wait负责监听描述符到来的事件,如果所监听的描述符有事件发生,就从file_events中获取对应的回调函数。然后如果external_num_events不为0,就说明有external事件发生,然后运行external_events的回调函数,套接字的绑定就是用external事件来完成的。
4)注意此时默认有三个PosixWorker,因此有三个线程处理线程调用epoll_wait监听事件,但只有一个会监听listenfd。

processor初始化

unsigned processor_num = 1;
if (stack->support_local_listen_table())  //false
  processor_num = stack->get_num_worker();
for (unsigned i = 0; i < processor_num; ++i)  //1
  processors.push_back(new Processor(this, stack->get_worker(i), cct));  //运行构造函数
        Processor::Processor(AsyncMessenger *r, Worker *w, CephContext *c)
          : msgr(r), net(c), worker(w),
            listen_handler(new C_processor_accept(this)) {}

1)PosixNetworkStack利用epoll,因为posix在kernel有一个全局的监听tabel,如果一个线程bind一个端口,则其他的线程同时也会察觉到这个事件,因此不需要本地监听表。每个processors对应一个posixwork和listen_handler。

ms_public 监听描述符绑定

(1)

ms_public->bind(paddr)          //entity_addr_t paddr = g_conf->get_val<entity_addr_t>("public_addr");  “-”
    for (auto &&p : processors)
        p->bind(bind_addr, avoid_ports, &bound_addr);
            /*默认情况下是没有指定地址和端口的*/
            for (int port = msgr->cct->_conf->ms_bind_port_min; port <= msgr->cct->_conf->ms_bind_port_max; port++) //6800-7300
                listen_addr.set_port(port);
                worker->center.submit_to(worker->center.get_id(), [this, &listen_addr, &opts, &r](){r = worker->listen(listen_addr, opts, &listen_socket);}, false);
                    EventCenter *c = global_centers->centers[i]; //默认有三个PosixWorker,第一个有Process,这里只用了第一个
                    C_submit_event<func> event(std::move(f), false);
                    c->dispatch_event_external(&event);
                        external_events.push_back(e);  //将event插入到external_events中,让process_events去处理
                        bool wake = !external_num_events.load();
                        uint64_t num = ++external_num_events;
                        if (!in_thread() && wake)
                            wakeup();
                                char buf = 'c';
                                int n = write(notify_send_fd, &buf, sizeof(buf));
                    event.wait(); //等待上面的事件完成
                        while (!done)
                            cond.wait(l);
        *bound_addr = listen_addr;       //已经绑定完成的地址     
    _finish_bind(bind_addr, bound_addr);    //bind为期望绑定的地址,默认为空
        set_myaddr(bind_addr);
            my_inst.addr = a;
        if (bind_addr != entity_addr_t())
            learned_addr(bind_addr);
                entity_addr_t t = peer_addr_for_me;
                t.set_port(my_inst.addr.get_port());
                t.set_nonce(my_inst.addr.get_nonce());
                my_inst.addr = t
        if (get_myaddr().get_port() == 0)
            set_myaddr(listen_addr);
        init_local_connection();
            _init_local_connection();
                local_connection->peer_addr = my_inst.addr;
                local_connection->peer_type = my_inst.name.type();  //my_inst.name = w;
                local_connection->set_features(CEPH_FEATURES_ALL);
                ms_deliver_handle_fast_connect(local_connection.get());
                for (list<Dispatcher*>::iterator p = fast_dispatchers.begin(); p != fast_dispatchers.end(); ++p)
                    (*p)->ms_handle_fast_connect(con);//会调用OSD::ms_handle_fast_connect(Connection *con)  没看懂是干啥的
                        Session *s = static_cast<Session*>(con->get_priv());
                        
        did_bind = true;

1)对于PosixNetworkStack,只有一个processor。默认情况下,集群没有配置地址和端口,因此processor会尝试从ms_bind_port_min(6800)到ms_bind_port_max(7300)端口号依次尝试绑定,绑定过程如2)。
2)执行worker->center.submit_to将lambda表达式插入到external_events中。然后发生数据给notify_send_fd(这里只发生了一个字符’c’)通知epoll_wait所在线程去处理这个external事件。lambda表达式会执行worker->listen,如下

//对于asyncmessage套接字绑定,则会调用[this, &listen_addr, &opts, &r]() { r = worker->listen(listen_addr, opts, &listen_socket);} lambda表达式
//进而调用int PosixWorker::listen(entity_addr_t &sa, const SocketOptions &opt, ServerSocket *sock)
int listen_sd = net.create_socket(sa.get_family(), true);
    s = ::socket(domain, SOCK_STREAM, 0)
    ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)
net.set_nonblock(listen_sd)
    (flags = fcntl(sd, F_GETFL));
    fcntl(sd, F_SETFL, flags | O_NONBLOCK)
net.set_close_on_exec(listen_sd);
net.set_socket_options(listen_sd, opt.nodelay, opt.rcbuf_size);
::bind(listen_sd, sa.get_sockaddr(), sa.get_sockaddr_len());
::listen(listen_sd, cct->_conf->ms_tcp_listen_backlog);
*sock = ServerSocket(std::unique_ptr<PosixServerSocketImpl>(new PosixServerSocketImpl(net, listen_sd)));  //赋值 listen_socket

对于asyncmessage套接字绑定,就是调用linux系统提供的接口去绑定套接字

(2)给ms_messenger添加dispathcer

client_messenger->add_dispatcher_head(this);  //this就是osd
client_messenger->add_dispatcher_head(&mgrc);

add_dispatcher_head调用栈如下

bool first = dispatchers.empty();
dispatchers.push_front(d);
if (d->ms_can_fast_dispatch_any())  //dispather返回为false,若为实现则为false  OSD为True
    fast_dispatchers.push_front(d);
if (first)  //第一次加入dispatcher
    ready();
        stack->ready(); //PosixNetworkStack没有实现,空函数
        for (auto &&p : processors) //貌似只有一个
            p->start();
              if (listen_socket) {  //在上面套接字绑定的时候赋值了
                worker->center.submit_to(worker->center.get_id(), [this]() {
                  worker->center.create_file_event(listen_socket.fd(), EVENT_READABLE, listen_handler); }, false);                          
        dispatch_queue.start();
            dispatch_thread.create("ms_dispatch");//启动线程
                dq->entry();  //dq指向dispatch_queue  负责处理 mqueue 的信息        
            local_delivery_thread.create("ms_local");  //启动线程
                dq->run_local_delivery();  //dq指向dispatch_queue 负责处理 local_messages 的信息

1)首先将该dispatcher加入到dispatchers中,然后将dispatcher加入到fast_dispatchers中(若不能处理fast_dispatch,则不用,osd可以处理)。
2)如果是第一次调用add_dispatcher_head,则需要调用ready(),ready对每个processor会将监听套接字加入到epoll,具体是执行

worker->center.create_file_event(listen_socket.fd(), EVENT_READABLE, listen_handler); }, false);

其中listen_socket就是上面绑定完成的要结字,listen_handler是processor创建的时候赋值的C_processor_accept。
3)当有新的连接时,epoll监听到,然后就会调用C_processor_accept,其会调用Processor::accept(),其调用栈如下

pro->accept();  //Processor::accept()
    while (true)
        Worker *w = worker;  //一个worker对应一个processor
        w = msgr->get_stack()->get_worker();  //取引用最少的posix worker
        listen_socket.accept(&cli_socket, opts, &addr, w);  //listen_socket的类型为PosixServerSocketImpl(net, listen_sd)
            int sd = ::accept(_fd, (sockaddr*)&ss, &slen);
            handler.set_close_on_exec(sd);
            handler.set_nonblock(sd);
            handler.set_socket_options(sd, opt.nodelay, opt.rcbuf_size);
            out->set_sockaddr((sockaddr*)&ss);  //赋值给 addr
            handler.set_priority(sd, opt.priority, out->get_family());
            std::unique_ptr<PosixConnectedSocketImpl> csi(new PosixConnectedSocketImpl(handler, *out, sd, true));
            *sock = ConnectedSocket(std::move(csi)); //赋值给 cli_socket
        msgr->add_accept(w, std::move(cli_socket), addr);
            AsyncConnectionRef conn = new AsyncConnection(cct, this, &dispatch_queue, w);
                read_handler = new C_handle_read(this);  //下面会用到
                write_handler = new C_handle_write(this);
                wakeup_handler = new C_time_wakeup(this);
                tick_handler = new C_tick_wakeup(this);              ///////还有很多状态!!!!!!!!
            
            conn->accept(std::move(cli_socket), addr);
                cs = std::move(socket);
                socket_addr = addr;
                state = STATE_ACCEPTING;
                center->dispatch_event_external(read_handler); //同上,将外部事件加入到external_events,并给notify_recv_fd发生数据,唤醒处理线程
                //然后线程调用read_handler 回调函数,结合STATE_ACCEPTING状态,开始将已连接的描述符加入centers,如上
            accepting_conns.insert(conn);

1)寻找引用次数最少的PosixWorker,以此来实现三个worker的负载均衡
2)调用linux系统提供的accept来接受新的连接请求,并设置描述符选项
3)调用msgr->add_accept来创建一个AsyncConnection,其用来表示一个连接,AsyncConnection包含了dispatch_queue线程、PosixWorker,和对应的center。在AsyncConnection的构造函数中赋值一些回调函数。
4)调用conn->accept(std::move(cli_socket), addr);将描述符对应的要监听的事件、连接状态加入到external_events,并给notify_recv_fd发生数据,唤醒处理线程,执行read_handler回调函数,这个回调函数就负责数据的收发了。

开启dispatch_queue

dispatch_queue.start();
dispatch_thread.create("ms_dispatch");//启动线程
    dq->entry();  //dq指向dispatch_queue  负责处理 mqueue 的信息
local_delivery_thread.create("ms_local");  //启动线程
    dq->run_local_delivery();  //dq指向dispatch_queue 负责处理 local_messages 的信息

由之前套接字连接的处理可以知道,一个AsyncConnection会关联一个dispatch_queue,因此这里的dispatch_thread和local_delivery_thread就负责给ms_public转发消息。

总结

AsyncMessenger实例在创建的时候会首先初始化PosixNetworkStack实例,PosixNetworkStack继承NetworkStack,因此也会调用NetworkStack的构造函数,在其构造函数中会创造ms_async_op_threads(默认为3)个PosixWorker实例worker,每个worker包含一个EventCenter实例center和NetHandler实例net,在NetworkStack构造函数中会调用center.init来设置epoll,并用net来设置管道(pipe)的接收端的配置项,并利用create_file_event函数将管道的接收端notify_receive_fd加入到epoll所监听的描述符中,除此之外还会把notify_receive_fd和其对应的要监听的事件mask以及回调函数加入到file_events中。
对每一个work,AsyncMessenger构造函数还会运行它的线程函数,worker的线程函数是add_thread函数返回的lambda表达式,并在通过调用spawn_worker函数运行。work的线程函数会把自己的center注册到全局的global_centers中。如果线程函数中的epoll_wait返回,则首先会检测numevents是不是大于0,如果大于0就说明监听的描述符有事件到来,就利用活跃描述符从file_events中取出对应的回调函数,然后会继续处理external_events中的事件,因为不是所有的事件都能直接通过描述符来通知线程函数的,这里要注意一下前面注册notify_receive_fd就是让线程函数去处理external_events的,因此notify_receive_fd回调函数仅仅是读取pipe中的数据,所以发送数据给notify_receive_fd就是唤醒线程函数(退出epoll_wait),让其处理external_events的事件。
AsyncMessenger构造函数的最后,还会实例化一个Processor,并加入到processors中,Processor会关联一个worker,Processor内有listenfd的回调函数listen_handler(new C_processor_accept(this))

在上面一些工作做完之后,程序会绑定套接字,其是通过AsyncMessenger的bind函数实现的,bind函数会利用processor的bind函数来去做具体的绑定工作,如果配置文件中没有指定地址,则processor的bind函数会尝试从ms_bind_port_min(6800)到ms_bind_port_max(7300)的端口号,直到成功。对一个端口号,会调用

worker->center.submit_to(worker->center.get_id(), [this, &listen_addr, &opts, &r](){r = worker->listen(listen_addr, opts, &listen_socket);}, false);

来将上面的lambda表达式插入到external_events中,并给processor关联的work的notify_send_fd发送唤醒数据,让关联的work的线程函数去处理bind任务。

最后就是给messenger添加dispatcher,dispatcher负责转发messenger的消息的。添加dispatcher是通过add_dispatcher_head来实现的。其将dispatcher加入到dispatchers和fast_dispatchers中。如果是第一次调用add_dispatcher_head,还会processor的start函数,start函数负责将前面以及绑定结束listen_socket对应的描述符加入到epoll的监听中,其是通过下面代码实现的

worker->center.submit_to(worker->center.get_id(), [this]() {worker->center.create_file_event(listen_socket.fd(), EVENT_READABLE, listen_handler); }, false);

其回调函数为listen_handler,当有新连接请求到达时,第一个worker中的线程函数就会去处理,然后利用下面的语句来获得引用最少的worker

w = msgr->get_stack()->get_worker(); 

这里是为了实现三个worker的负载平衡。 用引用最少的worker去处理新的已连接描述符,并负责这个描述符的数据收发。

最后在add_dispatcher_head中启动dispatch_thread和local_delivery_thread两个线程。

Ceph中,stripe是一种将数据分片存储的概念。当进行文件读取操作时,需要通过一系列的计算来确定数据所在的具体位置。本文以CephFS的文件读取流程为例进行分析。 首先,在文件读取过程中,Ceph会将文件划分为若干个条带(stripe),每个条带由多个对象分片(stripe unit)组成。条带可以看作是逻辑上连续的一维地址空间。 接下来,通过file_to_extent函数将一维坐标转化为三维坐标(objectset,stripeno,stripepos),来确定具体的位置。其中,objectset表示所在的对象集,stripeno表示条带号,stripepos表示条带内的偏移位置。 具体的计算过程如下:假设需要读取的数据的偏移量为offset,每个对象分片的大小为su(stripe unit),每个条带中包含的对象分片数为stripe_count。 首先,计算块号blockno = offset / su,表示数据所在的分片号。 然后,计算条带号stripeno = blockno / stripe_count,表示数据所在的条带号。 接着,计算条带内偏移stripepos = blockno % stripe_count,表示数据在条带内的偏移位置。 接下来,计算对象集号objectsetno = stripeno / stripes_per_object,表示数据所在的对象集号。 最后,计算对象号objectno = objectsetno * stripe_count + stripepos,表示数据所在的对象号。 通过以上计算,可以确定数据在Ceph中的具体位置,从而完成文件读取操作。 需要注意的是,以上分析是基于Ceph版本10.2.2(jewel)进行的,尽管版本跨度较大,但是该部分代码在12.2.10(luminous)版本中仍然比较稳定,基本的框架没有发生变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值