news 2026/9/11 6:26:22

深入解析 gRPC EndpointInfo 手握手器:连接端点信息的采集与授权消费链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析 gRPC EndpointInfo 手握手器:连接端点信息的采集与授权消费链路

深入解析 gRPC EndpointInfo 手握手器:连接端点信息的采集与授权消费链路

【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc

导读

在 gRPC 的握手(Handshake)阶段,连接的对端与本地地址信息是安全授权(如 RBAC)、审计与排障的重要输入。本文以 gRPC C++ 核心库中 endpoint_info 目录为切入点,剖析EndpointInfo数据结构的定位、EndpointInfoHandshaker的实现细节,以及端点地址如何通过ChannelArgs一路传递到授权评估模块(evaluate_args.cc)。读完本文,你将掌握 gRPC 握手框架的扩展方式、端点信息采集的完整调用链,以及如何基于源码与测试验证这一机制。

EndpointInfo:连接端点的信息载体

根据 endpoint_info/AGENTS.md 的说明,本目录承载的是EndpointInfo类——一个包含连接本地端点(local endpoint)与远端端点(remote/peer endpoint)信息的数据结构。它的核心价值在于:为握手器(handshaker)提供一种便捷的方式来访问连接双方的地址信息。

从源码结构看,该目录的实际实现由两个文件构成:

  • endpoint_info_handshaker.h:声明端点信息相关的两个 ChannelArgs 键,以及注册函数RegisterEndpointInfoHandshaker
  • endpoint_info_handshaker.cc:实现EndpointInfoHandshakerEndpointInfoHandshakerFactory,完成地址采集与写入。

需要说明的是,目录内并未单独存放名为endpoint_info.h的类定义文件,EndpointInfo的实际实现被内联在EndpointInfoHandshaker及其工厂类中,这一点在阅读源码时需要留意。

握手框架:EndpointInfo 所处的执行环境

要理解 EndpointInfo 手握手器,先要了解它所依附的 gRPC 握手框架。在 src/core/handshaker/AGENTS.md 中,握手框架被描述为“在客户端发送首个请求之前,对连接执行初始握手的可插拔机制”,典型用途包括 HTTP CONNECT(客户端侧)与各类安全初始化(TLS、ALTS 等)。

框架的核心抽象在 handshaker.h 中定义:

  • Handshaker(handshaker.h):单个握手操作的抽象基类,通过DoHandshake执行具体握手,通过name()标识自身;
  • HandshakeManager(handshaker.h):按加入顺序串行调用一组 handshaker,前一个完成后将HandshakerArgs传递给下一个;
  • HandshakerArgs(handshaker.h):在 handshaker 之间流转的输入/输出参数结构,包含endpointgrpc_endpoint指针)、argsChannelArgs)、read_bufferexit_earlydeadline等成员。

关键在于HandshakerArgs.args是一个贯穿整个握手链的可变ChannelArgs——每个 handshaker 都可以向其中写入或改写键值对。EndpointInfo 手握手器正是利用这一点,把端点地址以 ChannelArgs 的形式注入到后续流程中。

EndpointInfoHandshaker 实现解析

地址写入的核心逻辑

EndpointInfoHandshaker::DoHandshake 是整个机制的入口,其实现非常简洁:

void DoHandshake( HandshakerArgs* args, absl::AnyInvocable<void(absl::Status)> on_handshake_done) override { args->args = args->args .Set(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS, grpc_endpoint_get_local_address(args->endpoint.get())) .Set(GRPC_ARG_ENDPOINT_PEER_ADDRESS, grpc_endpoint_get_peer(args->endpoint.get())); InvokeOnHandshakeDone(args, std::move(on_handshake_done), absl::OkStatus()); }

它做了两件事:

  1. 从当前连接的grpc_endpoint对象中提取地址:本地地址通过grpc_endpoint_get_local_address获取,远端(对端)地址通过grpc_endpoint_get_peer获取;
  2. 将结果写入args->argsChannelArgs)的两个键中,这两个键在 endpoint_info_handshaker.h 中定义:
// Set by the handshaker to indicate the local address of the endpoint. #define GRPC_ARG_ENDPOINT_LOCAL_ADDRESS "grpc.internal.endpoint_local_address" // Set by the handshaker to indicate the peer address of the endpoint. #define GRPC_ARG_ENDPOINT_PEER_ADDRESS "grpc.internal.endpoint_peer_address"

随后立即以absl::OkStatus()完成握手(InvokeOnHandshakeDone),即该手握手器不消费也不改写任何字节流,只是纯信息采集,不会阻塞或延迟后续握手。

优先级与执行时机

EndpointInfoHandshaker由工厂类EndpointInfoHandshakerFactory创建,其Priority()返回 HandshakerPriority::kSecurityHandshakers,源码注释明确要求“必须在 kTCPConnectHandshakers 之后执行”。

参考 handshaker_factory.h 中定义的优先级枚举,握手链的执行顺序为:

优先级含义适用侧
kPreTCPConnectHandshakersTCP 连接建立之前主要客户端
kTCPConnectHandshakers实际建立 TCP 连接主要客户端
kHTTPConnectHandshakers实际建立 HTTP CONNECT主要客户端
kReadAheadSecurityHandshakers连接建立后、安全握手前主要服务端
kSecurityHandshakers连接建立后的安全握手客户端与服务端
kTemporaryHackDoNotUseEndpointWrappingHandshakers临时端点包装(勿用)

对比 tcp_connect_handshaker.cc 中返回的kTCPConnectHandshakers优先级,可以确认:EndpointInfo 手握手器在TCP 连接已经建立之后、安全握手(TLS/ALTS)同优先级阶段采集地址,因此拿到的grpc_endpoint已经处于可查询地址的就绪状态。

客户端与服务端双侧注册

RegisterEndpointInfoHandshaker 同时将工厂注册到客户端与服务端两种握手类型:

void RegisterEndpointInfoHandshaker(CoreConfiguration::Builder* builder) { builder->handshaker_registry()->RegisterHandshakerFactory( HANDSHAKER_CLIENT, std::make_unique<EndpointInfoHandshakerFactory>()); builder->handshaker_registry()->RegisterHandshakerFactory( HANDSHAKER_SERVER, std::make_unique<EndpointInfoHandshakerFactory>()); }

这意味着无论客户端发起连接还是服务端接受连接,其握手链中都会包含这一个手握手器。该注册函数的实际调用点位于插件注册中心 grpc_plugin_registry.cc,属于 gRPC 核心配置(CoreConfiguration)初始化的一部分,随库启动自动生效,无需用户额外配置。

数据流向:从 grpc_endpoint 到授权决策

采集到的两个地址以字符串形式写入 ChannelArgs 后,真正的消费方是 gRPC 的安全授权模块。以 evaluate_args.cc 为例:

EvaluateArgs::PerChannelArgs::PerChannelArgs(grpc_auth_context* auth_context, const ChannelArgs& args) { // ... 从 auth_context 提取 transport_security_type、spiffe_id、uri_sans 等 local_address = ParseEndpointUri( args.GetString(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS).value_or("")); peer_address = ParseEndpointUri( args.GetString(GRPC_ARG_ENDPOINT_PEER_ADDRESS).value_or("")); }

授权评估器在构造评估参数时,会从 ChannelArgs 中按GRPC_ARG_ENDPOINT_LOCAL_ADDRESSGRPC_ARG_ENDPOINT_PEER_ADDRESS两个键读取地址字符串(缺失时回退为空串),再通过ParseEndpointUri解析为结构化的地址对象。

ParseEndpointUri(evaluate_args.cc)的解析逻辑说明了地址字符串的格式约定:

  1. 先按 URI 语法解析,取出 path 部分;
  2. 调用SplitHostPort切分 host 与 port;
  3. SimpleAtoi将端口转为整型;
  4. StringToSockaddr尝试将地址解析为 IPv4/IPv6 的 socket 地址结构。

由此可推断grpc_endpoint_get_peer/grpc_endpoint_get_local_address返回的地址字符串形如ipv4:1.2.3.4:123ipv6:[2001:0db8:...]:456,这与测试代码中的输入格式完全吻合。解析后的本地/远端地址会被用于 RBAC(基于角色的访问控制)授权匹配,即 gRPC 的授权策略可以依据“连接来自哪个对端地址、落在哪个本地端口”等维度做出放行或拒绝的决策。

测试验证:如何确认端点信息链路正确

仓库中的测试用例为上述行为提供了直接验证:

  • evaluate_args_test_util.h 提供了两个测试辅助方法,将端点地址写入 ChannelArgs:
void SetLocalEndpoint(absl::string_view local_uri) { args_ = args_.Set(GRPC_ARG_ENDPOINT_LOCAL_ADDRESS, local_uri); } void SetPeerEndpoint(absl::string_view peer_uri) { args_ = args_.Set(GRPC_ARG_ENDPOINT_PEER_ADDRESS, peer_uri); }
  • evaluate_args_test.cc 展示了典型用法,例如util_.SetLocalEndpoint("ipv6:[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:456")util_.SetPeerEndpoint("ipv4:255.255.255.255:123")
  • authorization_matchers_test.cc 中大量使用SetLocalEndpoint/SetPeerEndpoint来构造授权匹配场景,覆盖 IPv4、IPv6 与非法地址等边界情况(例如ipv6:[1:2::3::]:456这类格式错误的输入),用于验证授权匹配器的解析与容错行为。

此外,构建系统层面,test/core/test_util/BUILD 中引用了//src/core:endpoint_info_handshaker目标,说明测试工具链与 EndpointInfo 手握手器之间的依赖关系是显式声明的。

小结:EndpointInfo 在 gRPC 安全体系中的位置

回顾整条链路,EndpointInfo 的设计体现了 gRPC 握手框架“可插拔、职责单一”的架构思想:

  1. 采集EndpointInfoHandshaker在 TCP 连接建立后、安全握手阶段,从grpc_endpoint提取本地与对端地址;
  2. 传递:地址以ChannelArgs键值对(grpc.internal.endpoint_local_address/grpc.internal.endpoint_peer_address)随握手链流转;
  3. 消费:授权评估模块从 ChannelArgs 中读取地址并解析为结构化数据,支撑 RBAC 授权决策。

对于希望在 gRPC 中获取连接端点信息的开发者而言,这一机制提供了明确的参考范式:只需在握手链中挂载一个与EndpointInfoHandshaker同构的采集器,即可让连接元数据随握手过程自然沉淀到ChannelArgs,供后续任意环节消费。相关实现细节可进一步参阅 endpoint_info_handshaker.cc、handshaker.h 与 evaluate_args.cc。

【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 6:25:16

西门子S7-1200变频恒压供水系统设计与PID控制

1. 西门子S7-1200变频恒压供水系统概述在工业自动化领域&#xff0c;恒压供水系统是典型的闭环控制应用场景。我最近完成的一个项目就是基于西门子S7-1200 PLC的变频恒压供水系统设计&#xff0c;这个系统通过PID算法精确控制水泵转速&#xff0c;实现了管网压力的稳定输出。相…

作者头像 李华
网站建设 2026/9/11 6:23:07

金刚石绳锯技术革新:高效切割与安全性能提升

1. 金刚石绳锯技术的行业现状与痛点金刚石绳锯作为一种高效切割工具&#xff0c;在石材开采、建筑拆除、混凝土切割等领域已有多年应用历史。传统绳锯采用钢丝绳外镀金刚石颗粒的结构&#xff0c;依靠高速运动实现切割。但长期以来&#xff0c;行业普遍存在几个核心痛点&#x…

作者头像 李华
网站建设 2026/9/11 6:20:38

Docker镜像构建优化与前后端部署实践

1. Docker镜像构建基础概念解析 在现代化应用部署流程中&#xff0c;Docker镜像已成为软件交付的标准单元。对于前后端分离架构的项目&#xff0c;合理的镜像构建策略直接影响着部署效率和运行稳定性。我经历过数十个企业级项目的容器化改造&#xff0c;发现90%的构建问题都源于…

作者头像 李华
网站建设 2026/9/11 6:20:34

Mojo 贡献区域指南:编译器与标准库的贡献边界与实操路径

Mojo 贡献区域指南&#xff1a;编译器与标准库的贡献边界与实操路径 【免费下载链接】mojo The Modular Platform (includes MAX & Mojo) 项目地址: https://gitcode.com/GitHub_Trending/mo/mojo 本文档基于 Mojo 开源仓库的 contribution-areas.md 编写&#xff0c…

作者头像 李华