新协议

了解如何将新协议添加到bRPC中。

服务器端的多协议支持

brpc服务器支持同一端口中的所有协议,在大多数情况下,它使部署和维护更加方便。由于不同协议的格式差异很大,很难明确支持同一端口中的所有协议。考虑到解耦和可扩展性,也很难为所有协议构建多路复用器。因此,我们的方法是将所有协议分为三类,并逐一尝试:

  • 第一类协议:在协议数据前标记特殊字符,例如协议数据百度_std和hulu_pbrpc分别以“PRPC”和“hulu”开头。解析器只需检查前四个字符,以了解协议是否匹配。首先检查这类协议,所有这些协议都可以共享一个TCP连接。
  • 第二类协议:一些没有特殊标记字符的复杂协议只有在解析多个输入数据后才能检测到。目前只有HTTP被归类为这一类。
  • 三级协议:特殊字符位于协议数据的中间,例如nshead协议的幻数是第25-28个字符。处理这种情况很复杂,因为如果不读取前28个字节,我们就无法确定协议是否为nshead。如果在http之前尝试,则可能无法解析小于28字节的http消息,因为解析器将其视为不完整的nshead消息。

考虑到大多数连接中只有一个协议,我们记录最后一个选择的结果,以便在有更多数据时首先尝试。对于长连接,它将匹配协议的开销减少到几乎为零。虽然每次都会对短连接运行匹配协议的过程,但短连接的瓶颈并不在这里,而且这种方法仍然足够快。如果将来有很多新的协议加入到brpc中,我们可以考虑一些启发式方法来匹配协议。

客户端的多协议支持

与服务器端不同的是,协议必须根据连接上的数据动态确定,客户端作为发起方,自然知道自己的协议格式。只要协议数据是通过连接池或短连接发送的,这意味着它只能使用该连接,那么协议可以有任何复杂(或错误)的格式。由于客户端在发送数据时会记录协议,因此当响应返回时,它将使用记录的协议解析数据,而不会产生任何匹配开销。memcache、redis等协议中没有神奇数字,在服务器端很难区分,但在客户端没有问题。

支持新协议

brpc旨在随时添加新协议,只需按以下步骤进行操作:

以nshead开头的协议具有统一支持,请阅读.

添加协议类型

在中的ProtocolType中添加新协议类型选项.协议。如果您需要添加新协议,请联系我们为您添加,以确保与其他人的协议没有冲突。

目前,我们支持ProtocolType(2018年年中):

枚举 协议类型 {
    协议_UNKNOWN = 0;
    协议_BAIDU_STD = 1;
    协议_STREAMING_RPC = 2;
    协议_HULU_PBRPC = ;
    协议_SOFA_PBRPC = 4;
    协议_临时 = 5;
    协议_HTTP = 6;
    协议_PUBLIC_PBRPC = 7;
    协议_NOVA_PBRPC = 8;
    协议_客户端 = 9;        //在brpc-ub中实现    协议_ SHEAD = 10;
    协议_HADOOP_RPC = 11;
    协议COL_HADOOP_SERVER_RPC = 12;
    协议_MONGO = 13;               //仅服务器端    协议_UBRPC_COMPACK = 14;
    协议_DIDX_CLIENT = 15;         //仅客户端    协议_REDIS = 16;               //仅客户端    协议_MEMCACHE = 17;            //仅客户端    协议_ITP = 18;
    协议_NSHEAD_MCPACK = 19;
    协议_DISP_IDL = 20;            //仅客户端    协议_ERSDA_CLIENT = 21;        //仅客户端    协议_UBRPC_MCPACK2 = 22;       //仅客户端    //为cds-agent保留特殊协议,该协议现在取决于FIFO    协议_CDS_代理 = 23;           //仅客户端    协议_ESP = 24;                 //仅客户端    协议_THRIFT = 25;              //仅服务器端}

实施回调

所有回调都在struct Protocol中定义,该协议在协议。小时在所有这些回调中,解析是必须实现的回调。此外,进程请求必须在服务器端实现,并且序列化请求,程序包请求,处理_响应必须在客户端实现。

很难实现协议的回调。这些代码不同于普通用户使用的具有良好提示和保护的代码。您必须弄清楚如何处理其他协议中的类似代码并实现自己的协议,然后将其发送给我们进行代码审查。

解析

类型定义 分析结果 (*分析)(但是::IOBuf公司* 来源, 插座 *插座, 布尔 读取(_E), 常数 空隙 *参数);

此函数用于从源中剪切消息。客户端和服务器端必须共享相同的解析功能。返回的消息将传递给进程请求(服务器端)或处理_响应(客户端)。

参数:source是远程端的二进制内容,socket是对应的连接,如果远程关闭连接,read_eof为true,arg是指向服务器客户端中相应服务器的指针,而客户端为NULL。

ParseResult可能是错误或剪切消息,其可能的值包含:

  • PARSE_ERROR_TRY_OTHERS:当前协议不匹配,框架将尝试下一个协议。无法消耗源中的数据。
  • PARSE_ERROR_NOT_ENOUGH_DATA:输入数据尚未违反当前协议,但也无法检测到整个消息。当连接中有新数据时,新数据将附加到源中,并再次调用解析函数。如果我们可以确定数据符合当前协议,那么源的内容也可以传输到协议的内部状态。例如,如果源代码不包含完整的http消息,它将被http解析器使用,以避免重复解析。
  • PARSE_ERROR_TOO_BIG_DATA:消息大小太大,将关闭连接以保护服务器。
  • PARSE_ERROR_NO_RESOURCE:内部错误,如资源分配失败。连接将被关闭。
  • PARSE_ERROR_ABSOLUTELY_WRONG:它应该是一些协议(幻数匹配),但格式不符合预期。连接将被关闭。

序列化请求

类型定义 布尔 (*序列化请求)(但是::IOBuf公司* 请求_buf,
                                 控制器* 碳纳米管,
                                 常数 谷歌::原蟾蜍::消息* 请求);

此函数用于将请求序列化为客户端必须实现的request_buf。它发生在RPC调用之前,并且只会被调用一次。一些协议(如http)所需的必要信息包含在cntl中。如果成功则返回true,否则返回false。

程序包请求

类型定义 整数 (*打包请求)(但是::IOBuf公司* 消息, 
                           单位64_t 相关id,
                           常数 谷歌::原蟾蜍::方法描述符* 方法,
                           控制器* 控制器,
                           常数 但是::IOBuf公司& 请求_buf,
                           常数 身份验证器* 授权);

此函数用于将request_buf打包到msg中,每次向服务器发送消息之前都会调用msg(包括重试)。当auth不为NULL时,还需要打包身份验证信息。如果成功,则返回0,否则返回-1。

进程请求

类型定义 空隙 (*处理请求)(输入消息库* 消息_基础);

此函数用于解析服务器端必须实现的请求消息。它和parse()可以在不同的线程中运行。多个process_request可以同时运行。处理完成后,必须调用msg_base->Destroy()。为了防止忘记调用Destroy,请考虑使用DestroyingPtr。

处理_响应

类型定义 空隙 (*处理响应)(输入消息库* 消息);

此函数用于解析客户端必须实现的消息响应。它和parse()可以在不同的线程中运行。多个process_request可能同时运行。处理完成后,必须调用msg_base->Destroy()。为了防止忘记调用Destroy,请考虑使用DestroyingPtr。

验证

类型定义 布尔 (*验证)(常数 输入消息库* 消息);

此函数用于验证连接,在收到第一条消息时调用。它必须由需要身份验证的服务器实现,否则函数指针可以为NULL。如果成功则返回true,否则返回false。

解析服务器地址

类型定义 布尔 (*分析服务器地址)(但是::端点* 外面的, 常数 烧焦* 服务器地址和端口);

此函数用于将server_addr_and_port(Channel.Init的一个参数)转换为butil::EndPoint,这是可选的。某些协议可能在服务器地址的表达和理解方面有所不同。

获取方法名称

类型定义 常数 标准::一串& (*获取方法名称)(常数 谷歌::原蟾蜍::方法描述符* 方法,
                                            常数 控制器*);

此函数用于自定义方法名,这是可选的。

支持的连接类型

用于标记支持的连接方法。如果支持所有连接方法,则该值应设置为connection_TYPE_all。如果支持连接池和短连接,则该值应设置为connection_TYPE_POOLED_and_short。

名称

出现在各种配置和显示中的协议名称应尽可能短,并且必须是字符串常量。

注册到全局

应调用RegisterProtocol寄存器实现协议到brpc,就像:

协议 http_协议 = { 解析HttpMessage,
                           序列化HttpRequest, 打包HttpRequest,
                           处理HttpRequest, 处理HttpResponse,
                           验证HttpRequest, 解析HttpServerAddress,
                           获取Http方法名称,
                           连接类型_ POOLED_AND_SHORT,
                           “http” };
如果 (寄存器协议(协议-HTTP, http_协议) != 0) {
    出口(1);
}

上次修改时间:2024年5月6日更新index.md(66353dc)