服务端对网络请求的处理大致可以分为三个阶段:
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协议
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;
}