brpc网络模型

服务端对网络请求的处理大致可以分为三个阶段:

dispatcher:接收到pollin事件

ProcessEvent:处理事件,主要是读取请求数据,做内置协议的解析,为后续调用协议的处理逻辑做准备

ProcessInputMessage:处理请求,调用usercode,CallMethod

EventDispatcher——事件分发器

brpc支持多个EventDispatcher,具体由event_dispatcher_num参数决定,默认数量是1.每个EventDispatcher负责一部分fd(accept_fd)的监听处理。在客户端连接不多的情况下,设置多个EventDispatcher没有效果

class EventDispatcher {
friend class Socket;
public:
    EventDispatcher();  
    virtual ~EventDispatcher();
    //在bthread中启动EventDispatcher,使用consumer_thread_attr作为bthread的属性
    virtual int Start(const bthread_attr_t* consumer_thread_attr);

    bool Running() const;//判断是否在bthread中运行
    // Stop bthread of this dispatcher.
    void Stop();
    // Suspend calling thread until bthread of this dispatcher stops.
    void Join();//等待bthread停止

    //添加消费者,当fd发生事件时,调用Socket的on_edge_triggered_events,转交Socket的所有权
    int AddConsumer(SocketId socket_id, int fd);

    //添加EPOLLOUT事件到epoll中,当事件发生时,调用Socket的HandleEpollOut
    //如果pollin为true,则同时添加EPOLLIN事件,使用EPOLL_CTL_MOD代替EPOLL_CTL_ADD
    int AddEpollOut(SocketId socket_id, int fd, bool pollin);
    //移除EPOLLOUT事件,如果pollin为true,则同时保留EPOLLIN事件,使用EPOLL_CTL_MOD代替EPOLL_CTL_DEL
    int RemoveEpollOut(SocketId socket_id, int fd, bool pollin);
private:
    DISALLOW_COPY_AND_ASSIGN(EventDispatcher);//禁止拷贝构造和赋值
    static void* RunThis(void* arg);//bthread的入口函数
    void Run();//bthread的入口函数
    int RemoveConsumer(int fd);//移除fd
    int _epfd;//epoll_fd
    volatile bool _stop;
    bthread_t _tid;
    bthread_attr_t _consumer_thread_attr;
    int _wakeup_fds[2];//管道,用于唤醒EventDispatcher
};
EventDispatcher& GetGlobalEventDispatcher(int fd) {
    // 初始化全局事件分发器
    pthread_once(&g_edisp_once, InitializeGlobalDispatchers);
    // 如果事件分发器数量为1
    if (FLAGS_event_dispatcher_num == 1) {
        // 直接返回第一个事件分发器
        return g_edisp[0];
    }
    // 计算索引值
    int index = butil::fmix32(fd) % FLAGS_event_dispatcher_num;
    // 返回对应索引的事件分发器
    return g_edisp[index];
}
void EventDispatcher::Run() {
    while (!_stop) {
        epoll_event e[32];
#ifdef BRPC_ADDITIONAL_EPOLL
        // Performance downgrades in examples.
        int n = epoll_wait(_epfd, e, ARRAY_SIZE(e), 0);
        if (n == 0) {
            n = epoll_wait(_epfd, e, ARRAY_SIZE(e), -1);
        }
#else
        const int n = epoll_wait(_epfd, e, ARRAY_SIZE(e), -1);
#endif
        if (_stop) {
            // epoll_ctl/epoll_wait should have some sort of memory fencing
            // guaranteeing that we(after epoll_wait) see _stop set before
            // epoll_ctl.
            break;
        }
        if (n < 0) {
            if (EINTR == errno) {
                // We've checked _stop, no wake-up will be missed.
                continue;
            }
            PLOG(FATAL) << "Fail to epoll_wait epfd=" << _epfd;
            break;
        }
        for (int i = 0; i < n; ++i) {
            if (e[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)
#ifdef BRPC_SOCKET_HAS_EOF
                || (e[i].events & has_epollrdhup)
#endif
                ) {
                // We don't care about the return value.
                Socket::StartInputEvent(e[i].data.u64, e[i].events,
                                        _consumer_thread_attr);
            }
        }
        for (int i = 0; i < n; ++i) {
            if (e[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) {
                // We don't care about the return value.
                Socket::HandleEpollOut(e[i].data.u64);
            }
        }
    }
}

定义一个epoll_event数组e来存储事件。

使用epoll_wait来等待事件。这里有两种模式:

BRPC_ADDITIONAL_EPOLL定义时,首先使用非阻塞的epoll_wait(超时时间为0),如果返回0(表示没有事件),则再次使用阻塞的epoll_wait(超时时间为-1)。

没有定义BRPC_ADDITIONAL_EPOLL时,直接使用阻塞的epoll_wait。

检查_stop变量,如果在epoll_wait后它被设置为true,则跳出循环。

处理返回的事件数n,如果n小于0,表示出现错误。如果错误是EINTR(表示被中断),则继续循环;否则,记录一个致命错误并跳出循环。

遍历所有事件,对于每个事件,检查是否满足特定的条件(如EPOLLIN, EPOLLERR,EPOLLHUP等),并调用Socket::StartInputEvent方法。

再次遍历所有事件,但这次检查是否满足输出事件的条件(如EPOLLOUT),并调用Socket::HandleEpollOut方法。

开始处理input事件

int Socket::StartInputEvent(SocketId id, uint32_t events,
                            const bthread_attr_t& thread_attr) {
    SocketUniquePtr s;
    // 根据SocketId获取Socket对象
    if (Address(id, &s) < 0) {
        return -1;
    }

    // 如果没有设置回调函数,则返回0
    if (NULL == s->_on_edge_triggered_events) {
        // 当接收错误的epoll事件时,回调函数可以为NULL
        // (在`WaitConnected'时加入到epoll中)
        // Callback can be NULL when receiving error epoll events
        // (Added into epoll by `WaitConnected')
        return 0;
    }

    // 检查Socket的文件描述符是否有效
    if (s->fd() < 0) {
        // 在Linux系统中,检查是否设置了EPOLLIN事件
        CHECK(!(events & EPOLLIN)) << "epoll_events=" << events;
        return -1;
    }

    // 以下部分被注释掉了,可能是在某种特定情况下才需要处理
    // if (events & has_epollrdhup) {
    //     s->_eof = 1;
    // }

    // 由于直接传递e[i].events会导致复杂的可见性问题,并且需要更强的内存屏障
    // 由于读取fd也可能返回错误,因此我们不传递events
    // 如果当前Socket对象的事件计数为0,则增加计数并启动事件处理线程
    if (s->_nevent.fetch_add(1, butil::memory_order_acq_rel) == 0) {
        // 根据统计,上述fetch_add操作非常有效。在每秒处理100万个请求的服务器上,
        // 这个计数器的增长速率仅为1500~1700/s
        s_vars->neventthread << 1;

        bthread_t tid;
        // 转移所有权,之后不再使用s
        // transfer ownership as well, don't use s anymore!
        Socket* const p = s.release();

        bthread_attr_t attr = thread_attr;
        attr.keytable_pool = p->_keytable_pool;
        // 启动bthread处理线程,如果失败则直接在当前线程处理
        if (bthread_start_urgent(&tid, &attr, ProcessEvent, p) != 0) {
            LOG(FATAL) << "Fail to start ProcessEvent";
            ProcessEvent(p);
        }
    }
    return 0;
}
void* Socket::ProcessEvent(void* arg) {
    // 封装的Socket对象在函数内部是有效的,并且可以自由访问
    SocketUniquePtr s(static_cast<Socket*>(arg));

    // 调用Socket对象的_on_edge_triggered_events方法,并传入Socket对象的指针
    // Input事件有两类:
    // 1. server的acceptor上注册的是:Acceptor::OnNewConnections
    // 2. 建立好连接的socket上注册的是:InputMessenger::OnNewMessages
    s->_on_edge_triggered_events(s.get());
    return NULL;
}

Acceptor::OnNewConnections —— 建立连接

建立连接的逻辑比较简单,主要就是在Acceptor::OnNewConnections

void Acceptor::OnNewConnections(Socket* acception) {
    // 初始化进度
    int progress = Socket::PROGRESS_INIT;
    do {
        // 调用函数处理新的连接,直到遇到EAGAIN错误
        OnNewConnectionsUntilEAGAIN(acception);

        // 如果连接失败,则直接返回
        if (acception->Failed()) {
            return;
        }
    } while (// 调用函数检查是否还有更多的读事件,并更新进度
             acception->MoreReadEvents(&progress));
}

OnNewConnectionsUntilEAGAIN会持续地接受新的连接,直到没有更多的连接可以处理(即遇到EAGAIN错误)或Acceptor停止运行

void Acceptor::OnNewConnectionsUntilEAGAIN(Socket* acception) {
    while (1) {
        // 定义一个sockaddr结构体,用于存储客户端的地址信息
        struct sockaddr in_addr;
        // 设置in_addr的大小为sockaddr结构体的大小
        socklen_t in_len = sizeof(in_addr);
        // 使用butil::fd_guard包装accept函数的返回值,确保在作用域结束时正确关闭文件描述符
        butil::fd_guard in_fd(accept(acception->fd(), &in_addr, &in_len));

        // 如果accept函数返回的文件描述符小于0,表示出错
        if (in_fd < 0) {
            // 因为监听的文件描述符是非阻塞的,所以不会出现EINTR错误
            // no EINTR because listened fd is non-blocking.
            if (errno == EAGAIN) {
                // 当出现EAGAIN错误时,表示当前没有更多的连接可以处理,直接返回
                return;
            }
            // 当accept失败时,不要直接返回-1,否则可能会导致_listened_fd被关闭
            // 如果accept持续失败,可能会频繁打印日志,这里限制日志的频率
            PLOG_EVERY_SECOND(ERROR)
                << "Fail to accept from listened_fd=" << acception->fd();
            continue;
        }

        // 尝试将acception->user()动态转换为Acceptor*类型
        Acceptor* am = dynamic_cast<Acceptor*>(acception->user());
        if (NULL == am) {
            // 如果转换失败,打印致命错误并设置acception为失败状态
            LOG(FATAL) << "Impossible! acception->user() MUST be Acceptor";
            acception->SetFailed(EINVAL, "Impossible! acception->user() MUST be Acceptor");
            return;
        }
        
        // 声明SocketId和SocketOptions变量
        SocketId socket_id;
        SocketOptions options;

        // 设置SocketOptions的属性
        options.keytable_pool = am->_keytable_pool;
        options.fd = in_fd;
        options.remote_side = butil::EndPoint(*(sockaddr_in*)&in_addr);
        options.user = acception->user();
        options.on_edge_triggered_events = InputMessenger::OnNewMessages;
        options.initial_ssl_ctx = am->_ssl_ctx;

        // 尝试根据options创建Socket,并将socket_id的地址作为参数传入
        if (Socket::Create(options, &socket_id) != 0) {
            // 如果创建失败,打印错误日志并继续循环
            LOG(ERROR) << "Fail to create Socket";
            continue;
        }

        // 将in_fd的所有权转移给socket_id
        in_fd.release(); // transfer ownership to socket_id

        // 注意:这里存在一个有趣的竞态条件。在Socket::Create之后,来自socket的消息可能已经开始被处理,
        // 并且RPC可能已经完成,但在下面的代码将socket添加到_socket_map之前。
        // 尝试获取新创建的Socket的指针
        SocketUniquePtr sock;
        if (Socket::AddressFailedAsWell(socket_id, &sock) >= 0) {
            bool is_running = true;
            {
                // 锁定_map_mutex,确保线程安全
                BAIDU_SCOPED_LOCK(am->_map_mutex);
                // 如果Acceptor已经停止运行,则丢弃新创建的Socket,并打印警告日志
                if (!is_running) {
            LOG(WARNING) << "Acceptor on fd=" << acception->fd()
                        << " has been stopped, discard newly created " << *sock;
            sock->SetFailed(ELOGOFF, "Acceptor on fd=%d has been stopped, "
                                      "discard newly created %s", acception->fd(),
                                      sock->description().c_str());
            return;
        }
        // 如果Socket::AddressFailedAsWell返回小于0的值(即Socket已经失败或已销毁),
        // 则不会执行此段代码,也不会将socket_id添加到_socket_map中
    } // else: The socket has already been destroyed, Don't add its id
      // into _socket_map

    // 注意:上面的else注释是假设性的,实际上在这个上下文中并没有一个显式的else语句。
// 如果Socket::AddressFailedAsWell返回小于0,那么sock将不会被初始化,
            //因此下面的代码也不会使用sock。

    // 接下来的代码可能会处理新创建的Socket(如果它没有被销毁或标记为失败)
    // ... (处理新Socket的逻辑可能在这里)

} // 无限循环结束,直到遇到EAGAIN错误或发生其他导致退出的条件

accept接受连接后,主要流程就是Socket::Create和Socket::AddressFailedAsWell创建并管理Socket对象。

int Socket::Create(const SocketOptions& options, SocketId* id) {
    // 1. 从资源池中获取一个Socket的槽位
    butil::ResourceId<Socket> slot;
    Socket* const m = butil::get_resource(&slot, Forbidden());

    // 2. 如果获取失败,记录致命错误并返回-1
    if (m == NULL) {
        LOG(FATAL) << "Fail to get_resource<Socket>";
        return -1;
    }

    // 3. 增加全局的socket计数
    s_vars->nsocket << 1;

    // 4. 检查新获取的Socket实例的共享部分是否为空(应该是空的)
    CHECK(NULL == m->_shared_part.load(butil::memory_order_relaxed));

    // 5. 初始化Socket实例的某些原子变量
    m->_nevent.store(0, butil::memory_order_relaxed);

    // 6. 从传入的SocketOptions中设置Socket实例的属性
    m->_keytable_pool = options.keytable_pool;
    m->_tos = 0;
    m->_remote_side = options.remote_side;
    m->_on_edge_triggered_events = options.on_edge_triggered_events;
    m->_user = options.user;
    m->_conn = options.conn;
    m->_app_connect = options.app_connect;

    // 7. 设置Socket的唯一ID,这个ID是基于Socket的版本化引用计数和其在资源池中的位置
    m->_this_id = MakeSocketId(
            VersionOfVRef(m->_versioned_ref.fetch_add(
                    1, butil::memory_order_release)), slot);

    // 8. 初始化Socket的其他属性
    m->_preferred_index = -1;
    m->_hc_count = 0;
    CHECK(m->_read_buf.empty());

    // 9. 设置Socket的上次读取时间为当前时间
    const int64_t cpuwide_now = butil::cpuwide_time_us();
    m->_last_readtime_us.store(cpuwide_now, butil::memory_order_relaxed);

    // 10. 重置解析上下文(可能用于某种协议解析)
    m->reset_parsing_context(options.initial_parsing_context);

    // 11. 初始化Socket的其他属性
    m->_correlation_id = 0;
    m->_health_check_interval_s = options.health_check_interval_s;
    m->_ninprocess.store(1, butil::memory_order_relaxed);
    m->_auth_flag_error.store(0, butil::memory_order_relaxed);

    // 12. 创建一个新的认证ID(可能用于安全或认证目的)
    const int rc2 = bthread_id_create(&m->_auth_id, NULL, NULL);
    if (rc2) {
        // 如果创建失败,记录错误并设置Socket为失败状态
        LOG(ERROR) << "Fail to create auth_id: " << berror(rc2);
        m->SetFailed(rc2, "Fail to create auth_id: %s", berror(rc2));
        return -1;
    }

    // 13. 根据是否提供了SSL上下文来设置SSL状态
    m->_ssl_state = (options.initial_ssl_ctx == NULL ? SSL_OFF : SSL_UNKNOWN);
    m->_ssl_session = NULL;
    m->_ssl_ctx = options.initial_ssl_ctx;

    // 14. 初始化Socket的其他属性
    m->_connection_type_for_progressive_read = CONNECTION_TYPE_UNKNOWN;
    m->_controller_released_socket.store(false, butil::memory_order_relaxed);
    m->_overcrowded = false;
    m->_fail_me_at_server_stop = false;
    m->_logoff_flag.store(false, butil::memory_order_relaxed);
    m->_recycle_flag.store(false, butil::memory_order_relaxed);
    m->_error_code = 0;
    m->_error_text.clear();
    // 15. 初始化Socket的`_agent_socket_id`属性为无效Socket ID
    m->_agent_socket_id.store(INVALID_SOCKET_ID, butil::memory_order_relaxed);
    
    // 16. 初始化Socket的`_id_wait_list`,这是一个用于等待特定Socket ID的列表
    // 注意这里的注释提到后两个参数在bthread的某个版本后是无用的
    const int rc = bthread_id_list_init(&m->_id_wait_list, 512, 512);
    if (rc) {
        // 如果初始化失败,记录错误并设置Socket为失败状态
        LOG(ERROR) << "Fail to init _id_wait_list: " << berror(rc);
        m->SetFailed(rc, "Fail to init _id_wait_list: %s", berror(rc));
        return -1;
    }
    
    // 17. 设置Socket的上次写入时间为当前时间,并初始化未写入的字节数为0
    m->_last_writetime_us.store(cpuwide_now, butil::memory_order_relaxed);
    m->_unwritten_bytes.store(0, butil::memory_order_relaxed);
    
    // 18. 检查Socket的写入链表头是否为空(应该是空的)
    CHECK(NULL == m->_write_head.load(butil::memory_order_relaxed));
    
    // 19. 调用ResetFileDescriptor方法来设置Socket的文件描述符(fd)
    // 注意这里的注释提到,在调用ResetFileDescriptor之后,Socket的内部字段可能会被访问
    // 所以这一步必须放在最后
    if (m->ResetFileDescriptor(options.fd) != 0) {
        const int saved_errno = errno;
        PLOG(ERROR) << "Fail to ResetFileDescriptor";
        m->SetFailed(saved_errno, "Fail to ResetFileDescriptor: %s", 
                     berror(saved_errno));
        return -1;
    }
    // 20. 将新创建的Socket的ID赋值给传入的指针id
    *id = m->_this_id;
    return 0;
    }

InputMessenger::OnNewMessages —— 读取rpc请求

接收读事件主要逻辑:

从socket中读取数据直到EOF

对数据请求切分,这里涉及到baidu-rpc协议(12个字节:“PRPC” + body_size(uint32_t) + meta_size(uint32_t))

启动bthread处理请求

// source code: src/brpc/input_messenger.cpp
void InputMessenger::OnNewMessages(Socket* m) {
    // 如果socket中只有一个请求,那么这个请求的解析和处理都是在当前bthread,主要是为了减少上下文切换
    // 如果socket中有多个请求,所有的请求都会在这里解析,除了最后一个请求,其他请求都会生成一个bthread
    InputMessenger* messenger = static_cast<InputMessenger*>(m->user());
    const InputMessageHandler* handlers = messenger->_handlers;
    int progress = Socket::PROGRESS_INIT;

    // 这个是自定义了资源释放函数的智能指针
    // 在智能指针析构时,会调用自定义的释放资源函数
    // 这里主要用于原地处理最后一个请求:RunLastMessage
    std::unique_ptr<InputMessageBase, RunLastMessage> last_msg;
    bool read_eof = false;
    while (!read_eof) {
        // 获取当前时间,主要是为了避免socket因为idle_timeout_s被关闭
        const int64_t received_us = butil::cpuwide_time_us();

        // 计算本次从socket中读取的数据长度
        size_t once_read = m->_avg_msg_size * 16;
        if (once_read < MIN_ONCE_READ) {
            once_read = MIN_ONCE_READ;  // min: 4KB
        } else if (once_read > MAX_ONCE_READ) {
            once_read = MAX_ONCE_READ;  // max: 512KB
        }

        // 将数据从socket中读取到socket中IOBuf
        const ssize_t nr = m->DoRead(once_read);
        if (nr <= 0) {
            if (0 == nr) {
                // 读完了
                LOG_IF(WARNING, FLAGS_log_connection_close) << *m << " was closed by remote side";
                read_eof = true;                
            } else if (errno != EAGAIN) {
                if (errno == EINTR) {
                    continue;  // just retry
                }
                // 错误处理
                const int saved_errno = errno;
                PLOG(WARNING) << "Fail to read from " << *m;
                m->SetFailed(saved_errno, "Fail to read from %s: %s",
                             m->description().c_str(), berror(saved_errno));

                // 注意:此处代码虽然是直接返回,但是在返回之前会处理last_msg
                return;
            } else if (!m->MoreReadEvents(&progress)) {
                // 该socket上没有新入的读事件,就返回
                // 注意:此处代码虽然是直接返回,但是在返回之前会处理last_msg
                return;
            } else {
                // 有新事件到达,继续处理
                continue;
            }
        }
        
        // 指标统计
        m->AddInputBytes(nr);

        // 避免socket因为idle_timeout_s被关闭
        m->_last_readtime_us.store(received_us, butil::memory_order_relaxed);
        
        size_t last_size = m->_read_buf.length();
        int num_bthread_created = 0;
        while (1) {
            size_t index = 8888;
            // 做请求切分
            ParseResult pr = messenger->CutInputMessage(m, &index, read_eof);
            if (!pr.is_ok()) {
                if (pr.error() == PARSE_ERROR_NOT_ENOUGH_DATA) {
                    // 读取的请求msg不完整,需要从socket中再次读取
                    m->_last_msg_size += (last_size - m->_read_buf.length());
                    break;
                } else if (pr.error() == PARSE_ERROR_TRY_OTHERS) {
                    // 错误处理
                    LOG(WARNING)
                        << "Close " << *m << " due to unknown message: "
                        << butil::ToPrintable(m->_read_buf);
                    m->SetFailed(EINVAL, "Close %s due to unknown message",
                                 m->description().c_str());
                    return;
                } else {
                    // 错误处理
                    LOG(WARNING) << "Close " << *m << ": " << pr.error_str();
                    m->SetFailed(EINVAL, "Close %s: %s",
                                 m->description().c_str(), pr.error_str());
                    return;
                }
            }
            // 指标统计
            m->AddInputMessages(1);

            // 计算请求msg的平均大小
            const size_t cur_size = m->_read_buf.length();
            if (cur_size == 0) {
                // 如果socket中的有效数据size为空
                // 那么就将该buf中cache的block归还给TLS(ThreadLocalStorage)
                m->_read_buf.return_cached_blocks();
            }
            m->_last_msg_size += (last_size - cur_size);
            last_size = cur_size;
            const size_t old_avg = m->_avg_msg_size;
            if (old_avg != 0) {
                m->_avg_msg_size = (old_avg * (MSG_SIZE_WINDOW - 1) + m->_last_msg_size)
                / MSG_SIZE_WINDOW;
            } else {
                m->_avg_msg_size = m->_last_msg_size;
            }
            m->_last_msg_size = 0;
            
            if (pr.message() == NULL) { // the Process() step can be skipped.
                continue;
            }
            pr.message()->_received_us = received_us;
            pr.message()->_base_real_us = base_realtime;
                        
            // 启动一个bthread处理上一个请求:msg->_process(msg)
            // 在第一次循环时由于last_msg为空,所以不会创建bthread
            // 在第二次之后的每个循环都会启动一个bthread处理上一个请求
            DestroyingPtr<InputMessageBase> msg(pr.message());
            QueueMessage(last_msg.release(), &num_bthread_created,
                             m->_keytable_pool);
            if (handlers[index].process == NULL) {
                LOG(ERROR) << "process of index=" << index << " is NULL";
                continue;
            }
            m->ReAddress(&msg->_socket);
            m->PostponeEOF();
            msg->_process = handlers[index].process;
            msg->_arg = handlers[index].arg;
            
            if (!m->is_read_progressive()) {
                // Transfer ownership to last_msg
                last_msg.reset(msg.release());
            } else {
                // brpc走的是上一个分支
                // 在http rpc短连接中应该是走这个分支
                QueueMessage(msg.release(), &num_bthread_created,
                                 m->_keytable_pool);
                bthread_flush();
                num_bthread_created = 0;
            }
        }

        // 一个活跃的TaskGroup会立即处理,无需通知
        // 在比较差的情况下,TaskGroup线程正在休眠,那么就需要通知线程处理bthread
        if (num_bthread_created) {
            bthread_flush();
        }
    }

    if (read_eof) {
        m->SetEOF();
    }
}

// 为last_msg启动bthread处理请求
static void QueueMessage(InputMessageBase* to_run_msg,
                         int* num_bthread_created,
                         bthread_keytable_pool_t* keytable_pool) {
    if (!to_run_msg) {
        return;
    }

    bthread_t th;
    bthread_attr_t tmp = (FLAGS_usercode_in_pthread ?
                          BTHREAD_ATTR_PTHREAD :
                          BTHREAD_ATTR_NORMAL) | BTHREAD_NOSIGNAL;
    tmp.keytable_pool = keytable_pool;
    if (bthread_start_background(
            &th, &tmp, ProcessInputMessage, to_run_msg) == 0) {
        ++*num_bthread_created;
    } else {
        ProcessInputMessage(to_run_msg);
    }
}

void* ProcessInputMessage(void* void_arg) {
    InputMessageBase* msg = static_cast<InputMessageBase*>(void_arg);
    // 如果是baidu_rpc协议的话,msg->_process为:
    // baidu_rpc_protocal.h: ProcessRpcRequest
    msg->_process(msg);
    return NULL;
}

ProcessInputMessage —— 处理rpc请求

读取请求数据后就确认了使用的什么协议,baidu-rpc消息格式后面再看

处理rpc请求的主要流程:

解析出RpcMeta,主要字段包括:service_name、method_name、compress_type、attachment_size等(baidu_rpc_meta.proto)

根据service_name、method_name找到对应的service和method

解析业务定义的pb

做一些初始化的操作,比如构建Closure

CallMethod就是我们实现的rpc接口,比如rpc Write(WriteRequest) returns (WriteResponse)

// source code: src/brpc/policy/baidu_rpc_policy.cpp
void ProcessRpcRequest(InputMessageBase* msg_base) {
    DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));
    SocketUniquePtr socket_guard(msg->ReleaseSocket());
    Socket* socket = socket_guard.get();
    const Server* server = static_cast<const Server*>(msg_base->arg());
    ScopedNonServiceError non_service_error(server);

    // 解析RpcMeta
    // 这个是baidu-rpc协议定义的格式:baidu_rpc_meta.proto
    RpcMeta meta;
    if (!ParsePbFromIOBuf(&meta, msg->meta)) {
        LOG(WARNING) << "Fail to parse RpcMeta from " << *socket;
        socket->SetFailed(EREQUEST, "Fail to parse RpcMeta from %s",
                          socket->description().c_str());
        return;
    }
    const RpcRequestMeta &request_meta = meta.request();

    // 做一些请求初始化工作
    std::unique_ptr<Controller> cntl(new (std::nothrow) Controller);
    std::unique_ptr<google::protobuf::Message> req;
    std::unique_ptr<google::protobuf::Message> res;

    ServerPrivateAccessor server_accessor(server);
    ControllerPrivateAccessor accessor(cntl.get());
    const bool security_mode = server->options().security_mode() &&
                               socket->user() == server_accessor.acceptor();
    if (request_meta.has_log_id()) {
        cntl->set_log_id(request_meta.log_id());
    }
    cntl->set_request_compress_type((CompressType)meta.compress_type());
    accessor.set_server(server)
        .set_security_mode(security_mode)
        .set_peer_id(socket->id())
        .set_remote_side(socket->remote_side())
        .set_local_side(socket->local_side())
        .set_auth_context(socket->auth_context())
        .set_request_protocol(PROTOCOL_BAIDU_STD)
        .set_begin_time_us(msg->received_us())
        .move_in_server_receiving_sock(socket_guard);

    MethodStatus* method_status = NULL;
    do {
        // 检查server状态
        if (!server->IsRunning()) {
            cntl->SetFailed(ELOGOFF, "Server is stopping");
            break;
        }

        // 检查网络拥塞、检查并发
        if (socket->is_overcrowded()) {
            cntl->SetFailed(EOVERCROWDED, "Connection to %s is overcrowded",
                            butil::endpoint2str(socket->remote_side()).c_str());
            break;
        }
        if (!server_accessor.AddConcurrency(cntl.get())) {
            cntl->SetFailed(
                ELIMIT, "Reached server's max_concurrency=%d",
                server->options().max_concurrency);
            break;
        }
        if (FLAGS_usercode_in_pthread && TooManyUserCode()) {
            cntl->SetFailed(ELIMIT, "Too many user code to run when"
                            " -usercode_in_pthread is on");
            break;
        }

        // 根据RpcMeta找到具体的service和method
        // 并初始化request和response
        butil::StringPiece svc_name(request_meta.service_name());
        if (svc_name.find('.') == butil::StringPiece::npos) {
            const Server::ServiceProperty* sp =
                server_accessor.FindServicePropertyByName(svc_name);
            if (NULL == sp) {
                cntl->SetFailed(ENOSERVICE, "Fail to find service=%s",
                                request_meta.service_name().c_str());
                break;
            }
            svc_name = sp->service->GetDescriptor()->full_name();
        }
        const Server::MethodProperty* mp =
            server_accessor.FindMethodPropertyByFullName(
                svc_name, request_meta.method_name());
        if (NULL == mp) {
            cntl->SetFailed(ENOMETHOD, "Fail to find method=%s/%s",
                            request_meta.service_name().c_str(),
                            request_meta.method_name().c_str());
            break;
        } else if (mp->service->GetDescriptor()
                   == BadMethodService::descriptor()) {
            BadMethodRequest breq;
            BadMethodResponse bres;
            breq.set_service_name(request_meta.service_name());
            mp->service->CallMethod(mp->method, cntl.get(), &breq, &bres, NULL);
            break;
        }
        // Switch to service-specific error.
        non_service_error.release();
        method_status = mp->status;
        if (method_status) {
            int rejected_cc = 0;
            if (!method_status->OnRequested(&rejected_cc)) {
                cntl->SetFailed(ELIMIT, "Rejected by %s's ConcurrencyLimiter, concurrency=%d",
                                mp->method->full_name().c_str(), rejected_cc);
                break;
            }
        }
        google::protobuf::Service* svc = mp->service;
        const google::protobuf::MethodDescriptor* method = mp->method;
        accessor.set_method(method);
        if (span) {
            span->ResetServerSpanName(method->full_name());
        }
        const int reqsize = static_cast<int>(msg->payload.size());
        butil::IOBuf req_buf;
        butil::IOBuf* req_buf_ptr = &msg->payload;
        if (meta.has_attachment_size()) {
            if (reqsize < meta.attachment_size()) {
                cntl->SetFailed(EREQUEST,
                    "attachment_size=%d is larger than request_size=%d",
                     meta.attachment_size(), reqsize);
                break;
            }
            int att_size = reqsize - meta.attachment_size();
            msg->payload.cutn(&req_buf, att_size);
            req_buf_ptr = &req_buf;
            cntl->request_attachment().swap(msg->payload);
        }

        CompressType req_cmp_type = (CompressType)meta.compress_type();
        req.reset(svc->GetRequestPrototype(method).New());
        if (!ParseFromCompressedData(*req_buf_ptr, req.get(), req_cmp_type)) {
            cntl->SetFailed(EREQUEST, "Fail to parse request message, "
                            "CompressType=%s, request_size=%d", 
                            CompressTypeToCStr(req_cmp_type), reqsize);
            break;
        }
        res.reset(svc->GetResponsePrototype(method).New());
        
        // 构建closure,主要是在CallMethod之后,
        // 调用SendRpcResponse发送响应
        google::protobuf::Closure* done = ::brpc::NewCallback<
            int64_t, Controller*, const google::protobuf::Message*,
            const google::protobuf::Message*, const Server*,
            MethodStatus*, int64_t>(
                &SendRpcResponse, meta.correlation_id(), cntl.get(), 
                req.get(), res.get(), server,
                method_status, msg->received_us());

        // optional, just release resourse ASAP
        msg.reset();
        req_buf.clear();

        // 调用CallMethod
        if (!FLAGS_usercode_in_pthread) {
            // 正常就在原地调用CallMethod
            return svc->CallMethod(method, cntl.release(), 
                                   req.release(), res.release(), done);
        }
        // 在开启usercode_in_pthread选项的CallMethod流程
        if (BeginRunningUserCode()) {
            // 在并发不大的情况下,原地调用CallMethod
            svc->CallMethod(method, cntl.release(), 
                            req.release(), res.release(), done);
            return EndRunningUserCodeInPlace();
        } else {
            // 在并发比较大的情况下,将CallMethod入队
            // 由usercode后台线程处理CallMethod
            return EndRunningCallMethodInPool(
                svc, method, cntl.release(),
                req.release(), res.release(), done);
        }
    } while (false);
    
    // 在出错的情况下,发送异常响应
    SendRpcResponse(meta.correlation_id(), cntl.release(), 
                    req.release(), res.release(), server,
                    method_status, msg->received_us());
}

baidu-rpc协议

img

RPC Header表明协议是rpc协议类型,以及meta和body长度,用于message的切分。该字段的解析是在接收请求的阶段,序列化是在SendResponse阶段。

RPC Meta包含compress_type、attachment_size等信息,如果该message是请求,那么里面包含service_name、method_name等信息,如果message是响应,那么里面包含了错误码信息。

Request Body是用户定义的协议数据以及attachment数据(可选),比如:echo.proto等

协议格式的定义在src/brpc/policy/baidu_rpc_meta.proto

协议实现在:src/brpc/policy/baidu_rpc_protocol.cpp。

socket通信

socket连接是在监听fd触发Pollin事件时创建,同一时间仅有一个bthread从Socket中读取数据。这个并发控制的逻辑是在socket::StartInputEvent通过一个原子变量做的:

// source code: src/brpc/socket.cpp
int Socket::StartInputEvent(SocketId id, uint32_t events,
                            const bthread_attr_t& thread_attr) {
    SocketUniquePtr s;
    if (Address(id, &s) < 0) {
        return -1;
    }
	
	// 通过原子变量s->_nevent做了读的并发控制
	// 后来的读事件fetch_add之后直接返回,最先到的读事件会创建bthread,处理这批读事件。
    if (s->_nevent.fetch_add(1, butil::memory_order_acq_rel) == 0) {
        // According to the stats, above fetch_add is very effective. In a
        // server processing 1 million requests per second, this counter
        // is just 1500~1700/s
        g_vars->neventthread << 1;

        bthread_t tid;
        // transfer ownership as well, don't use s anymore!
        Socket* const p = s.release();

        bthread_attr_t attr = thread_attr;
        attr.keytable_pool = p->_keytable_pool;
        if (bthread_start_urgent(&tid, &attr, ProcessEvent, p) != 0) {
            LOG(FATAL) << "Fail to start ProcessEvent";
            ProcessEvent(p);
        }
    }
    return 0;
}

从socket读取数据是Socket::DoRead,主要的读取逻辑在IOBuf中:

// sorce code: src/brpc/socket.cpp
ssize_t Socket::DoRead(size_t size_hint) {
	// 忽略ssl的逻辑...
    return _read_buf.append_from_file_descriptor(fd(), size_hint);
}

// source code: src/butil/iobuf_inl.h
inline ssize_t IOPortal::append_from_file_descriptor(int fd, size_t max_count) {
    return pappend_from_file_descriptor(fd, -1, max_count);
}

// source code: src/butil/iobuf.cpp
ssize_t IOPortal::pappend_from_file_descriptor(
    int fd, off_t offset, size_t max_count) {
    iovec vec[MAX_APPEND_IOVEC];
    int nvec = 0;
    size_t space = 0;
    Block* prev_p = NULL;
    Block* p = _block;
    do {
    	// 根据要读的数据max_count,申请足够的block空间
        if (p == NULL) {
        	// 优先从tls的block链表中获取没用完的block,
        	// 如果从tls中没获取到,则创建一个新的block。
            p = iobuf::acquire_tls_block();
            if (BAIDU_UNLIKELY(!p)) {
                errno = ENOMEM;
                return -1;
            }
            if (prev_p != NULL) {
                prev_p->portal_next = p;
            } else {
                _block = p;
            }
        }
        // 构造iovector
        vec[nvec].iov_base = p->data + p->size;
        vec[nvec].iov_len = std::min(p->left_space(), max_count - space);
        space += vec[nvec].iov_len;
        ++nvec;
        // MAX_APPEND_IOVEC为64
        if (space >= max_count || nvec >= MAX_APPEND_IOVEC) {
            break;
        }
        prev_p = p;
        p = p->portal_next;
    } while (1);

	// 从socket的fd中读取数据
	// 并没有保证一定能读取到指定大小的数据
    ssize_t nr = readv(fd, vec, nvec);
    if (nr <= 0) {  // -1 or 0
        if (empty()) {
        	// 释放block,优先放到tls链表中,
        	// 如果tls链表长度=8,则释放。
            return_cached_blocks();
        }
        return nr;
    }

    size_t total_len = nr;
    do { // 将相关的block构建好ref,放到IOBuf中
        const size_t len = std::min(total_len, _block->left_space());
        total_len -= len;
        const IOBuf::BlockRef r = { _block->size, (uint32_t)len, _block };
        
        // 增加block的ref次数,增加之后ref次数>1
        _push_back_ref(r); 
        _block->size += len;
        if (_block->full()) { // 如果该block没有可用空间了,
            Block* const saved_next = _block->portal_next;
            _block->dec_ref();  // 则,降低ref次数,当ref变成0时,就施放资源。
            _block = saved_next;
        }
    } while (total_len);
    return nr;
}

写数据主要是在CallMethod执行完成后,由Closure调用,主要实现在SendRpcResponse中,根据compress_type以及协议类型做序列化,之后调用Socket:Write将接口响应发送给客户端。

由于CallMethod可能在不同的TaskGroup执行,也就是说存在多个线程同时往一个Socket fd发送消息的情况,而fd的写又不是原子的,所以如何高效率地排队不同线程写出的数据包是这里的关键。

brpc使用一种wait-free MPSC链表来实现这个功能。所有待写出的数据都放在一个单链表节点中,next指针初始化为一个特殊值(Socket::WriteRequest::UNCONNECTED)。当一个线程想写出数据前,它先尝试和对应的链表头(Socket::_write_head)做原子交换,返回值是交换前的链表头。如果返回值为空,说明它获得了写出的权利,它会在原地写一次数据。否则说明有另一个线程在写,它把next指针指向返回的头以让链表连通。正在写的线程之后会看到新的头并写出这块数据。

// source code: src/brpc/socket.cpp
// SendRpcResponse调用Socket::Write发送数据
// 该接口是线程安全的
int Socket::Write(SocketMessagePtr<>& msg, const WriteOptions* options_in) {
    WriteOptions opt = *options_in;
    // Set `req->next' to UNCONNECTED so that the KeepWrite thread will
    // wait until it points to a valid WriteRequest or NULL.
    req->next = WriteRequest::UNCONNECTED;
    req->id_wait = opt.id_wait;
    req->set_pipelined_count_and_user_message(opt.pipelined_count, msg.release(), opt.with_auth);
    return StartWrite(req, opt);
}

int Socket::StartWrite(WriteRequest* req, const WriteOptions& opt) {
	// _write_head是一个存放写请求的MPSC的链表的head,使用原子变量
    WriteRequest* const prev_head =
        _write_head.exchange(req, butil::memory_order_release);
    if (prev_head != NULL) {
    	// 已有其他线程在该Socket上做写操作了
    	// 那么就将当前的req通过“头插”的方式放到链表的前面
    	// 在其他线程完成之前的写操作后,会获取_write_head指针,处理后续的写
        req->next = prev_head;
        return 0;
    }
	// 当prev_head==NULL,意味着没有其他线程在该Socket写
	// 那么,当前线程获得了该Socket的写权限

    int saved_errno = 0;
    bthread_t th;
    SocketUniquePtr ptr_for_keep_write;
    ssize_t nw = 0;

    req->next = NULL;

    // NOTE: Setup() MUST be called after Connect which may call app_connect,
    // which is assumed to run before any SocketMessage.AppendAndDestroySelf()
    // in some protocols(namely RTMP).
    req->Setup(this);
    
    // 执行一次写操作
    nw = req->data.cut_into_file_descriptor(fd());
    if (nw < 0) {
        // RTMP may return EOVERCROWDED
        if (errno != EAGAIN && errno != EOVERCROWDED) {
            saved_errno = errno;
            // EPIPE is common in pooled connections + backup requests.
            PLOG_IF(WARNING, errno != EPIPE) << "Fail to write into " << *this;
            SetFailed(saved_errno, "Fail to write into %s: %s", 
                      description().c_str(), berror(saved_errno));
            goto FAIL_TO_WRITE;
        }
    } else {
        AddOutputBytes(nw);
    }

	// 如果req没有处理完,或者req->next有新的写请求
	// 那么后面会启动一个bthread处理
    if (IsWriteComplete(req, true, NULL)) {
        ReturnSuccessfulWriteRequest(req);
        return 0;
    }

KEEPWRITE_IN_BACKGROUND:
    ReAddress(&ptr_for_keep_write);
    req->socket = ptr_for_keep_write.release();
    if (bthread_start_background(&th, &BTHREAD_ATTR_NORMAL,
                                 KeepWrite, req) != 0) {
        LOG(FATAL) << "Fail to start KeepWrite";
        KeepWrite(req);
    }
    return 0;

// 异常处理。
FAIL_TO_WRITE:
    // `SetFailed' before `ReturnFailedWriteRequest' (which will calls
    // `on_reset' callback inside the id object) so that we immediately
    // know this socket has failed inside the `on_reset' callback
    ReleaseAllFailedWriteRequests(req);
    errno = saved_errno;
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值