brpc源码解析(二)—— brpc收到请求的处理过程

article/2025/10/1 0:06:11

文章目录

  • 一、基本设计思路
  • 二、实现细节
  • 三、总结

作为rpc服务器,在启动过后,最主要的一个过程就是收到请求后的处理,而这就牵涉到一个网络编程相关最基本的部分:如何有效地处理socket传过来地数据,这篇文章就来详细聊一聊brpc是怎么处理收到的请求的。

一、基本设计思路

作为服务器,业界最典型的实现就是区分I/O线程和工作线程,一个或多个I/O线程负责从socket读取数据放入一个队列,然后一堆worker线程来从队列里取数据并处理,或者I/O线程读完数据直接交给worker,此类严格区分I/O线程和worker线程的机制会有几种典型的问题:

1.一个I/O线程同时只能读取一个fd,I/O线程通常只有一个或者几个,如果某个fd上数据量很大,读取很耗时会容易导致其他fd数据的阻塞。
2.如果要操作队列会产生竞争,高并发的时候严重影响性能。
3.当前主流的多核cpu机制下,各个核之间进行cache同步是微妙级的,并不是很快,容易出现cache bouncing,I/O线程将任务交给worker涉及到跨核,效率不是很高,之间的同样cpu缓存到内存之间交换数据也比较慢。

和很多rpc框架不同,brpc有一套独特的机制来解决上述问题,总的来说就是,不区分I/O线程和worker线程,通过全局的EventDispatcher来负责监听fd上的事件,如果fd上已经有bthread在处理了直接返回,否则启动一个bthread处理,被EventDispatcher启动的bthread负责读取fd上的消息,每读到一条就启动bthread去处理,最后一条原地执行,直到读完为止,因为EventDispatcher本身既不负责读消息也不负责处理消息,自身吞吐量可以做得很大,fd间和fd内的消息都能获得并发,所有线程都是wait-free的,这使得brpc在高负载时仍能及时处理不同来源的消息。下面就结合源码详细阐述相关机制。

二、实现细节

在上篇文章里已经介绍过了,服务器启动后会由EventDispatcher监听epoll事件,作为服务器,收到一个新连接属于epoll_in事件,会调用Socket::StartInputEvent进行处理。StartInputEvent函数如下:
在这里插入图片描述
StartInputEvent函数里,首先是调用Address方法根据SocketId获取socket s,因为一个fd上会不断地发生事件,socket类里面设置了一个butil::atomic _nevent变量,用来保证对于一个fd,同时只会有一个bthread在处理,当收到事件时,EventDispatcher给nevent加1,只有当加1前的值是0时启动一个bthread处理对应fd上的数据,前值不为0说明已经有bthread在处理该fd上的数据了,可以直接返回,因为正在处理的bthread会一直读下去。总的来说StartInputEvent仅仅是负责判断发生事件的fd上有没有bthread在处理了,没有就原地启动一个,有就直接返回,注意调用的是bthread_start_urgent,这个函数启动bthread会让出当前bthread,也就是官方文档所说的,“EventDispatcher把所在的pthread让给了新建的bthread,使其有更好的cache locality,可以尽快地读取fd上的数据”,不需要pthread切换对性能有帮助”。如果因为某些原因bthread_start_urgent启动失败,则原地执行ProcessEvent兜底。

调用的ProcessEvent如下:
在这里插入图片描述
也就是执行socket 里的_on_edge_triggered_events,对于目前收到新连接请求的情况,事件发生在监听端口上,对应的_on_edge_triggered_events是上篇文章提到的StartAccept里赋值的OnNewConnection,也就是来了新连接要调用的回调函数

OnNewConnections内部调用OnNewConnectionsUntilEAGAIN函数,OnNewConnectionsUntilEAGAIN核心如下:
在这里插入图片描述
首先accept监听fd来得到clinet fd
在这里插入图片描述
然后调用Create新建一个socket来具体处理本次新连接的后续消息,在里面添加了一个新的epoll_in 事件,注册的处理函数是InputMessenger::OnNewMessages,注意和前面的OnNewConnections对比,前面是处理新连接,这个是新连接处理后来处理新连接上的新消息。在InputMessenger::OnNewMessages函数里面,核心是读取消息然后交由相应协议的处理函数,进而调用用户服务里的实际业务函数。

对于clinet fd上收到的epoll_in事件,入口仍然是StartInputEvent,只不过随后的ProcessEvent调用的处理函数是InputMessenger::OnNewMessages,核心部分代码如下:
在这里插入图片描述
一直读取直到读完。
在这里插入图片描述
做一次读取
在这里插入图片描述
messenger是InputMessenger对象,负责从fd上切割和处理消息。上篇文章介绍了brpc的自定义协议,每个协议其中最重要的就是ParseXXXmessage和ProcessXXX,分别对应处理消息的两个基本步骤,Parse和Process,Parse一般是把消息从二进制流上切割下来,运行时间较固定;Process则是进一步解析消息(比如反序列化为protobuf)后调用用户回调,时间不确定。因为brpc是支持单端口多协议的,因此InputMessenger会逐一尝试用户指定的种协议的回调,当某一个Parse成功切割下一个消息后,调用对应的ProcessXXX。由于一个连接上往往只有一种消息格式,InputMessenger会记录下上次的选择,而避免每次都重复尝试。
在这里插入图片描述
在这里插入图片描述
QueueMessage是启动一个bthread执行ProcessInputMessage来处理一个msg。若连续从某个fd读取出n个消息(n > 1),InputMessenger会启动n-1个bthread分别处理前n-1个消息,最后一个消息则会在原地被Process。
根据handler里的解析器处理消息得到解析后的结果
ProcessInputMessage如下,调用的是msg->_process,也就是对应协议的process_request来处理cut下来的message:
在这里插入图片描述
这里实现的关键就是last_msg,定义如下,是一个自定义deleter的unique_ptr:
在这里插入图片描述
每次循环都会去执行对last_msg的处理,如果last_msg非null,则新启一个bthread去处理,在第一次循环,也就是处理第一个msg的时候,last_msg为null,随后的每一次循环都会先调用QueueMessage处理last_msg,

随后last_msg置成这次循环的msg,待下次循环处理,分别会对msg->_process (消息处理函数)和msg->_arg 赋值供后续上述ProcessInputMessage调用
在这里插入图片描述在这里插入图片描述
所以最后一次循环完了后还剩一个last_msg没有处理,怎么办呢,last_msg的deleter是RunlastMessage,如下:
在这里插入图片描述
RunlastMessage跟queueMessage一样都是调用的ProcessInputMessage,析构的时候会自动调用deleter,从而实现最后一个msg原地调用。
Msg->_process是对应协议的process_request请求处理函数,其中最终会调用用户函数,以http协议为例,Msg->_proces指向的是协议注册时我们看到的ProcessHttpRequest,
在这里插入图片描述
可以看到msg作为参数进来后,通过msg->arg()拿到了指向server的指针,msg->arg()返回的就是msg->_arg,前面图里可以看到msg->_arg 所赋予的是handler 的arg,回忆一下,handler 的arg其实就是所启动的server的指针,是在BuildAcceptor()里面添加各种handler的时候赋值的:
在这里插入图片描述
ProcessHttpRequest核心处理部分如下:
先拿到MethodProperty sp
在这里插入图片描述
得到service和method
在这里插入图片描述
调用业务代码,比如echo示例:
在这里插入图片描述
在这里插入图片描述
具体的打包发送则是通过调用done->Run(),在推荐用法里,由RAII机制保证,也就是上图的done_guard。
在这里插入图片描述

三、总结

brpc在处理接收到的请求的时候,不区分io线程核worker线程,二是利用EventDispatcher基于边缘触发的epoll做event的分发,这样并发可以做得很大,具体的消息切割、协议相关的请求处理、以及后续的用户逻辑均交由独立的bthread来处理,实现了wait-free,不用担心某个请求io耗时过大造成批量IO阻塞,也没有生产者消费者队列所带来的瓶颈。并且是让出当前pthread的方式进行调度,保证了cache locality。以上消息处理上的优势也是brpc高性能的基石之一。


http://chatgpt.dhexx.cn/article/eJRcJAN4.shtml

相关文章

libpng warning iCCP 错误处理方法

png图片缺乏某些库,导致损坏,或者多余了一些数据会导致以下报错: libpng warning: iCCP: known incorrect sRGB profile libpng warning iccp extra compressed data一些可能的解决方案: 已有方案 来自:https://blo…

创建线程提示SCB_CFSR_BFSR:0x04 IMPRECISERR 错误

在RTthread的编程出现了问题 这种问题并不是系统出现的问题,而是在处理自己的函数内部出现了数组越界,内存出现错误导致的。 关键还是自己指针访问的非法访问导致这些问题。遇到问题还是要多检查自己写的接口问题。

无法运行rc.exe(已解决)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言原因解决一:在C:\Program Files (x86)下搜索rc.exe二:右击,打开找到的rc.exe的文件夹然后进入Visual Studio 2019 前言 解决…

RTCP(一): RR--Receiver Reports 接收者报告

RTCP RR的格式 接受者报告的RTCP类型是201,如图1.1所示。 图1.1 reporter ssrc rr报告发送者的ssrc,也就是rtp报文接受者自己的ssrc. reportee ssrc rr报告接受者的ssrc,也就是rtp报文发送者的ssrc. cumulative number of packet lo…

MFC生成错误msado15.tlh(3991):fatal error C1003: 错误计数超过100;正在停止编译

MFC生成过程产生错误msado15.tlh(3991): fatal error C1003: 错误计数超过 100;正在停止编译 1 问题描述 在MFC生成解决方案过程中,当点击工具栏的生成按钮时, 会出现编译错误的情况: msado15.tlh(3991): fatal error C1003: 错…

webrtc编译中的错误解决

webrtc编译记录 错误1:该错误的意思是python的安装路径要和你此时的webrtc源码的编译路径相同。 解决方法:将python的安装路径和webrtc编译源码的路径放在同一个磁盘下。 错误2:Windows 默认不支持文件名或目录名长于260个字符的&#xff…

rvtptcontrol failed

rvtptcontrol failed RVT00-010:子例行程序 rvtooperation()返回错误。 原因:子例行程序 rvtooperation() 返回内部错误。返回的错误信息为: 措施: 请记下此错误编号以及无法读取例程 &routine 中配置文件选项 INV_DEBUG_TRACE 的值”这…

error C2338: /RTCc rejects conformant code错误解决

在编译一个项目时,发现在调试版本时提示这个出错: 1>------ 已启动生成: 项目: simulation2, 配置: Debug Win32 ------1>precompiled.cpp1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\yvals.h(112): error C2338: /RTC…

CSHOP后台设置SMTP发邮件提示 Error: need RCPT command 错误解决

其实错误原因并不是因为此错误,经检测,邮件服务器返回的真实错误是 501 mail from address must be same as authorization user 。只因为同时返回了 503 Error: need MAIL command 和 503 Error: need RCPT command ,而ECSHOP只提示了最后一…

阿里云企业邮箱代理商:foxmal邮件发送RCPT错误怎么办?

阿里云企业邮箱代理商:foxmal邮件发送RCPT错误怎么办? 聚搜云是上海聚搜信息技术有限公司旗下品牌,坐落于魔都上海,服务于全球、2019年成为阿里云代理商生态合作伙伴。与阿里云代理商、腾讯云、西部数码、美橙互联、聚搜云,长期战…

foxmail发生RCPT错误

一, 问题 在前几天来到万达之后电脑要重新装系统,也没管别的就直接装了,然后电脑上所有的东西就没了,在装好之后要安装所需要的软件。安装之后就开始使用,就在使用foxmail的时候遇到问题了,也不知道发生了…

Windows下bat命令启动和关闭jar包

一、启动 这里需要将jar包和bat文件放在同一个目录下 启动命令代码如下 echo off start javaw -jar springboot.jar exit二、关闭 关闭命令代码如下 echo off set port8888 for /f "tokens1-5" %%i in (netstat -ano^|findstr ":%port%") do taskkil…

利用Bat命令批量修改文件名

因为科研需求,需要把文件名规范统一命名。 整体思路: 先获得原始文件名字(带后缀),再导到excel里搞好新名字,构建好Bat的ren函数,完成修改。 具体措施: 1)读取原本文件…

Excel、bat命令批量新建、修改文件或文件名

目录 1.批量新建文件夹2.批量新建文件3.批量提取文件名4.批量重命名文件5.生成目录树 1.批量新建文件夹 打开记事本输入 md 文件;第二个;第三个文件 另存为bat的后缀 双击运行bat文件即可 2.批量新建文件 打开Excel,第一列输入文件名 第二列输入保存路径&a…

windows的bat命令记录

查看防火墙开放端口 首先可以同时按下winr快捷键调出运行菜单,输入cmd命令确定。 点击确定之后进入了命令行的界面,在界面中输入netstat -a的命令。 cd命令 //进入G盘 G://获取帮助 cd /?// 跳转到上一层目录 cd ..// 跳转到其他目录 d://进入到E盘的指定文件夹 cd /d e:\e…

windows10 bat命令获取日期时间

系统版本 win10 英文OS Windows Edition:Windows 10 ProSettings-Language:English(United States) 获取日期命令 完整的日期:date(输出如下图) 裁剪方法:echo %date:~起点位,数据长度% 【英文版】对…

.bat脚本基本命令合集

bat脚本的基本命令语法 一、批处理的常见命令 1、REM 和 :: 2、ECHO 和 3、PAUSE 4、ERRORLEVEL 5、TITLE 6、COLOR 7、mode 配置系统设备 8、GOTO 和 : 9、FIND 10、START 11、assoc 和 ftype 12、pushd 和 popd 13、CALL …

bat命令大全

文章目录 一、bat(批处理文件类型)二、命令简介1.基础语法2.关闭或打开回显命令3.定义变量4.调用变量5.已管理员身份执行了命令提示符6.进入指定路径7.防止dos窗口关闭8.延迟执行命令9.输出信息到控制台10.循环11.调用某个bat文件并执行12.打开某个文件夹…

华为性格测试指导

这是华为网测之后性格测试的指导文章,有兴趣的可以看下。主要是以截图的方式显示的FDF文件,不影响查看。PS:一般网测表现的“拼命点”都能过,除非心理确实不适合华为氛围的,那就不要强求了吧。

【软件测试岗面经分享】华为测试工程师面经,已成功上岸

努力不会白费,成功不会白来 关注小编公众号:《阿里大叔说测试》即可获得由小编精心整理好的大厂面试问题和答案并能够在第一时间获取们后续发布的最新分享和面经。 您的关注就是对我们后续创作的最大支持 前言 十分感谢J的投稿,很高兴能向…