核心概念:命名服务 (Naming Service)
在分布式系统和RPC框架(如bRPC)中,命名服务(Naming Service) 扮演着服务地址注册与发现的角色。它维护着一个映射关系:服务名 -> 一组提供该服务的服务器实例(Endpoint列表)。
- 例如: 一个服务名叫
SearchService
,在命名服务(如Nacos, ZooKeeper, Consul, bRPC内置的DNS/File等)中注册了3个服务器地址:192.168.1.101:8000
,192.168.1.102:8000
,192.168.1.103:8000
。 - Channel的作用: 在bRPC中,一个
Channel
对象通常对应一个服务名。当你初始化一个Channel时,给它配置一个命名服务地址和该服务名,这个Channel就能自动地从命名服务获取到当前所有可用的服务器Endpoint列表,并负责后续的负载均衡(如轮询、随机等)、连接管理、健康检查等工作。
区别详解
- 后端server挂在同一个命名服务内
- 含义: 所有提供相同逻辑服务的后端服务器实例(Server),都注册在同一个服务名下,位于同一个命名服务中。
- 示例: 如上所述,所有
SearchService
的服务器实例都注册在命名服务的SearchService
条目下。 - bRPC备份请求实现:
- 使用单个
Channel
对象即可。这个Channel配置了指向该服务名和命名服务。 - 在
ChannelOptions
中设置backup_request_ms = X
(例如2ms)。 - 工作流程:
- 客户端通过这个Channel发起RPC调用。
- 负载均衡器(如轮询)选择列表中的一个Server(假设叫Server A)发送第一个请求。
- 如果在
X
毫秒后,第一个请求(发给Server A)还没有返回响应: - bRPC框架自动触发备份请求。
- 负载均衡器再次从当前可用的Server列表中选择另一个Server(假设叫Server B)(通常不会是A,但负载均衡策略决定)发送同一个请求的副本。
- 哪个Server(A或B)先返回有效响应,客户端就使用哪个响应。 后到的响应会被忽略(或根据框架内部机制处理)。
- 优点:
- 简单: 只需配置一个Channel。
- 高效: 大部分情况下(在
backup_request_ms
设置合理时),只会发送一个请求(Server A响应及时)。只有少数慢请求会触发备份请求,因此平均请求量接近1倍,对后端集群压力小。 - 透明: 负载均衡、服务发现、连接管理都由Channel自动处理。
- 适用场景: 同一个集群内部,通过增加冗余请求提高服务可用性(容错单机慢节点或瞬时故障)。
- 使用单个
- 后端server不能挂在一个命名服务内
- 含义: 提供相同逻辑服务的后端服务器实例,不属于同一个逻辑集群,或者物理/逻辑上完全隔离(如两个不同的IDC机房、两个不同的云厂商、两个独立部署的异构系统)。因此,它们无法(或不适合)注册在同一个服务名下的同一个命名服务中。
- 示例:
- 主集群
SearchService-A
(注册在命名服务A)。 - 备份集群
SearchService-B
(注册在命名服务B,或同一个命名服务的另一个服务名)。 - 或者两个完全独立的不同服务(如
Service-X
和Service-Y
),但客户端需要它们互备。
- 主集群
- bRPC备份请求实现 - 推荐方法 (SelectiveChannel):
- 使用一个
SelectiveChannel
对象作为主访问点。 - 为每一个独立的集群/服务创建一个子Channel (sub Channel):
- 子Channel1:配置指向集群A的服务名/命名服务 (如
SearchService-A
)。 - 子Channel2:配置指向集群B的服务名/命名服务 (如
SearchService-B
)。
- 子Channel1:配置指向集群A的服务名/命名服务 (如
- 将这些子Channel Add 到 SelectiveChannel 中。
- 在
SelectiveChannel
的ChannelOptions
中设置backup_request_ms = X
(例如2ms)。 - 工作流程:
- 客户端通过 SelectiveChannel 发起RPC调用。
- SelectiveChannel 选择其中一个子Channel(假设是子Channel1,对应集群A) 发送第一个请求。
- 如果在
X
毫秒后,第一个请求(发给集群A)还没有返回响应: - bRPC框架自动触发备份请求。
- SelectiveChannel 选择另一个子Channel(即子Channel2,对应集群B) 发送同一个请求的副本。
- 哪个子Channel(即哪个集群)先返回有效响应,客户端就使用哪个响应。
- 优点:
- 实现了跨集群/跨服务的互备。一个集群整体故障或网络分区时,备份请求能打到另一个可用集群。
- 继承了核心备份请求机制的高效性:合理设置
backup_request_ms
后,大部分请求也只发往一个集群(主集群),平均请求量接近1倍。 - 结构清晰,易于管理不同的后端源。
- 适用场景: 跨机房容灾、多活部署、异构系统互备。需要确保一个集群完全不可用时,服务仍能降级运行。
- 使用一个
- 不推荐方法 (Join + Cancel):
- 手动创建两个独立的Channel:ChannelA(指向集群A),ChannelB(指向集群B)。
- 对同一个请求,同时异步发起两个RPC调用:一个通过ChannelA发往集群A,另一个通过ChannelB发往集群B。
- 使用
Join
等待任意一个完成。 - 在各自的
done
回调中,取消 (Cancel) 另一个还在进行中的RPC。 - 缺点:
- 总是发送两个请求,无论第一个请求是否快速响应。这导致对后端的压力是2倍,非常不经济。
- 需要手动管理取消逻辑,代码复杂。
- 无法利用到bRPC内置的备份请求优化机制。
- 文档态度: 明确标注“不推荐”、“你应该尽量避免用这个方法”。
总结对比表
特性 | 同一个命名服务 (单Channel) | 不同命名服务 (SelectiveChannel) | 不同命名服务 (Join+Cancel - 不推荐) |
---|---|---|---|
后端关系 | 同一集群内服务器 | 不同集群/异构服务 (互备) | 不同集群/异构服务 (互备) |
bRPC对象 | 单个 Channel |
一个 SelectiveChannel + 多个 sub Channel |
多个独立的 Channel |
配置备份请求 | ChannelOptions.backup_request_ms |
SelectiveChannelOptions.backup_request_ms |
无内置机制,需手动实现 |
请求发送策略 | 先发集群内一台,超时发集群内另一台 | 先发一个集群,超时发另一个集群 | 同时发往所有目标集群 |
平均请求量 | ~1倍 (合理设置 backup_request_ms 时) |
~1倍 (合理设置 backup_request_ms 时) |
总是2倍 (或更多) |
后端压力 | 低 | 低 | 高 (2倍) |
主要优势 | 容错单机慢节点/瞬时故障,高效 | 实现跨集群/服务互备,高效 | 能实现互备 (但代价高) |
主要劣势 | 无法应对整个集群故障 | 配置略复杂 | 效率低,压力大,代码复杂 |
适用场景 | 同集群高可用 | 跨集群容灾/多活/异构互备 | 应避免使用 |
文档推荐度 | 推荐 | 推荐 | 不推荐 |
关键点强调
- 命名服务的核心作用: 它定义了“哪些服务器属于同一个逻辑服务/集群”。同一个命名服务下的同一个服务名,意味着这些服务器是可以互相替代、负载均衡的。
- 备份请求的本质: 是一种超时驱动的冗余请求机制,旨在通过发送少量额外请求(主要在超时后)来掩盖个别节点的慢响应或瞬时故障,提升请求成功率(SLA)和降低尾部延迟(Latency Tail)。
- **
backup_request_ms
的智慧:** 合理设置这个阈值是高效的关键。设置得太小,会过早触发不必要的备份请求,增加压力;设置得太大,备份请求的降延迟效果就不明显。文档建议通过延时分布(如latency_cdf
图)来观察和选择,目标是覆盖大部分请求(如95%或99%),让备份请求只在少数慢请求上触发。 - SelectiveChannel vs Join+Cancel: SelectiveChannel 巧妙地将“选择不同源”和“备份请求”机制结合起来,在提供跨集群互备能力的同时,最大程度地保留了备份请求的高效性(平均接近1倍请求)。而Join+Cancel简单粗暴地发双份请求,效率低下,是文档明确反对的方案。
Reference
- brpc documentation