apache / brpc

brpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".
https://brpc.apache.org
Apache License 2.0
16.55k stars 3.97k forks source link

Redis cluster smart client support draft #2142

Open wayslog opened 1 year ago

wayslog commented 1 year ago

Is your feature request related to a problem? (你需要的功能是否与某个问题有关?)

redis cluster 是现在 redis 官方推荐的集群方案,使用的地方也比较多。建议 brpc 在现在的 redis 协议的基础上增加 redis cluster 支持。

具体支持方式不难:

  1. RedisClusterNamingService 初始化 redis cluster 地址列表 作为种子节点。
  2. RedisClusterNamingService 通过发送 CLUSTER NODES 或者 CLUSTER SLOTS 命令拉取初始化的集群节点列表并将对应的 SLOT 信息通过 ServerNode 结构体的 tag 字段(e.g.: SLOTS:"1-30 50 [93-<-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]")传递给自定义的 PreHashLoadBalancer。
  3. 自定义的 RedisClusterNamingService 继承 PeriodicNamingService,周期性的发送 CLUSTER NODES 命令给随机节点,更新集群拓扑。
  4. 通过 Tag 传递给 ServerId,进而传递给 PreHashLoadBalancer 做初始化。
  5. 自定义的 PreHashLoadBalancer 通过传入的 ServerId 的 tag 字段解析槽位信息。并初始化一个 16384 个槽位的环。
  6. 计算节点的时候,通过请求带的 request_code 来(request_code 应该由 RedisClusterRequest 自动计算填充,通过 CRC32/16384 计算)。
  7. 增加一个 Controller 逻辑,当客户端发现 redis cluster 返回 MOVED 之后,立即触发一次 RedisClusterNamingService 同步更新。同时修改 PreHashLoadBalancer 的路由表。同时修改请求地址,做重定向。
  8. 增加 Controller 逻辑,当客户端发现 redis cluster 返回 ASK 之后,直接修改请求地址,并在请求之前增加一个 ASKING 指令并获得其返回值(ASKING 在每个 SLOT 变化的时候都需要重新发送至少一次),再重新 IssueRPC,做重定向。
  9. 重定向应该有最大请求次数限制。

需要确定和讨论的又如下几点:

  1. 是否需要自定义 Controller 来处理和控制 Redis Cluster 请求的 MOVE 和 ASK
  2. 是否需要重新注册一个 Redis Cluster protocol 协议。因为从协议上来看,redis cluster protocol 和 redis 都用的 RESP2 协议。
  3. 是否需要单独独立一个 RedisClusterRequest ,还是在 RedisRequest 上加个字段来区分是否使用的 cluster 模式?
  4. 如果要计算 request_code 需要 aware 用户请求,那么需要修改 RedisRequest 的解析段。是否让用户主动传入,我们不做计算?(感觉这里如果放开了会是个暗坑)。

实现的时候需要注意控制 ASK/MOVED 的触发 NS 更新 和 LoadBalancer 的更新频率(可以通过设置一个指数函数来逐步减少触发,但要在更新成功后及时 reset),因为一旦 redis cluster 发生迁移的时候,可能会触发大量的 ASK 和 MOVED。并且,由于 redis cluster gossip 协议的特殊性,我们甚至需要对全部的 redis cluster 都发送一次 CLUSTER NODES 指令来确定是否更新 NS 的节点列表,这一步在 Smart Client 里一般被称为 wait-consistent。

希望 maintainer 考虑一下这个协议,如果可以的话我可以负责后续的实现。

Describe the solution you'd like (描述你期望的解决方法)

以上

Describe alternatives you've considered (描述你想到的折衷方案)

Additional context/screenshots (更多上下文/截图)

wwbmmm commented 1 year ago

计算节点的时候,通过请求带的 request_code 来(request_code 应该由 RedisClusterRequest 自动计算填充,通过 CRC32/16384 计算)。

目前RedisRequest支持批量,但是多个批量请求的key可能hash到不同的server,而当前一次RPC过程会把请求都发到同一个server,所以可能要加个限制,一次RPC的多个批量请求的key必须相同,否则实现起来会很复杂

增加一个 Controller 逻辑,当客户端发现 redis cluster 返回 MOVED 之后,立即触发一次 RedisClusterNamingService 同步更新。同时修改 PreHashLoadBalancer 的路由表

目前Controller能拿到LB,但是拿不到NamingService对象。Controller耦合NamingService似乎不是一个好主意,可以考虑只更新LB就可以。

增加 Controller 逻辑,当客户端发现 redis cluster 返回 ASK 之后,直接修改请求地址,并在请求之前增加一个 ASKING 指令并获得其返回值(ASKING 在每个 SLOT 变化的时候都需要重新发送至少一次),再重新 IssueRPC,做重定向。

这里需要一定的抽象,可以抽象一个通用的Controller Redirect流程

是否需要自定义 Controller 来处理和控制 Redis Cluster 请求的 MOVE 和 ASK

继承Controller父类,来实现Redis Cluster请求流程,这也是一个思路,但需要考虑Controller父类需要留出哪些接口给子类实现。毕竟Controller里的逻辑很复杂,不可能让子类完全重写一遍。

是否需要重新注册一个 Redis Cluster protocol 协议。因为从协议上来看,redis cluster protocol 和 redis 都用的 RESP2 协议。

看起来好像没必要?在协议解析这方面,并没有什么区别。

是否需要单独独立一个 RedisClusterRequest ,还是在 RedisRequest 上加个字段来区分是否使用的 cluster 模式?

如果实现了自定义的RedisController,也可以放在那里做一些cluster特有逻辑,就不用在Request上做区分

如果要计算 request_code 需要 aware 用户请求,那么需要修改 RedisRequest 的解析段。是否让用户主动传入,我们不做计算?(感觉这里如果放开了会是个暗坑)。

最好不要让用户传入

wayslog commented 1 year ago

目前RedisRequest支持批量,但是多个批量请求的key可能hash到不同的server,而当前一次RPC过程会把请求都发到同一个server,所以可能要加个限制,一次RPC的多个批量请求的key必须相同,否则实现起来会很复杂

可以不相同,只要 key hash 之后的 slot 相同就可以了。另外现在的 redis 客户端大部分其实都未做拆分,这个拆分了之后对一些特殊的命令比如 MSETNX 之类产生不可知的影响。 我们这里可以先只支持同 server 的请求,把自动拆 key (涉及到的命令主要有 MGET/MSET/DEL/EXISTS... 这几个)的作为后续的支持实现。

目前Controller能拿到LB,但是拿不到NamingService对象。Controller耦合NamingService似乎不是一个好主意,可以考虑只更新LB就可以。

我也感觉耦合 NamingService 不是一个好主意。NamingService 可以通过周期性的发送命令自己更新自己。

这里需要一定的抽象,可以抽象一个通用的Controller Redirect流程

Controller 这里重定向逻辑作为一个插件或者策略组什么的插入 Controller 里吧,继承下来感觉不如另加逻辑。就是我得注意一下不要把 Controller 污染。

jiangdongzi commented 1 year ago

你这边开发的怎么样了, 我可以协助一起开发

cooper-zhzhang commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

jiangdongzi commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

可以参考下sougou的workflow, workflow 支持 异步redis cluster

jiangdongzi commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

可以参考下sougou的workflow, workflow 支持 异步redis cluster

简单看了下源码啊, 好像不支持smart client, 只是简单处理了MOVED ASK错误

wayslog commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

可以参考下sougou的workflow, workflow 支持 异步redis cluster

简单看了下源码啊, 好像不支持smart client, 只是简单处理了MOVED ASK错误

我这边仍然在持续开发中,不过最近工作的事情比较忙,还需要一段时间来实现它。 这里 MOVED/ASK 需要在 Controller 中加入一个抽象层级比较高、逻辑比较干净的重试策略。因为 Controller 本身涉及到的流程相当多,所以改起来是会比较难的。

jiangdongzi commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

可以参考下sougou的workflow, workflow 支持 异步redis cluster

简单看了下源码啊, 好像不支持smart client, 只是简单处理了MOVED ASK错误

我这边仍然在持续开发中,不过最近工作的事情比较忙,还需要一段时间来实现它。 这里 MOVED/ASK 需要在 Controller 中加入一个抽象层级比较高、逻辑比较干净的重试策略。因为 Controller 本身涉及到的流程相当多,所以改起来是会比较难的。

redis专门定义一个retry_policy如何, 专门处理MOVED ASKING @wwbmmm

wwbmmm commented 1 year ago

redis专门定义一个retry_policy如何, 专门处理MOVED ASKING @wwbmmm

这也是一个思路

jiangdongzi commented 1 year ago

请问你那边开发到什么进度了? 我这边已经实现了moved,但是在实现ASK时遇到了问题。能否交流下

可以参考下sougou的workflow, workflow 支持 异步redis cluster

简单看了下源码啊, 好像不支持smart client, 只是简单处理了MOVED ASK错误

我这边仍然在持续开发中,不过最近工作的事情比较忙,还需要一段时间来实现它。 这里 MOVED/ASK 需要在 Controller 中加入一个抽象层级比较高、逻辑比较干净的重试策略。因为 Controller 本身涉及到的流程相当多,所以改起来是会比较难的。

redis专门定义一个retry_policy如何, 专门处理MOVED ASKING @wwbmmm

@wwbmmm @wayslog image image 这种方式实现MOVED和ASKING看起来自然而然, 基本无侵

jiangdongzi commented 1 year ago

计算节点的时候,通过请求带的 request_code 来(request_code 应该由 RedisClusterRequest 自动计算填充,通过 CRC32/16384 计算)。

目前RedisRequest支持批量,但是多个批量请求的key可能hash到不同的server,而当前一次RPC过程会把请求都发到同一个server,所以可能要加个限制,一次RPC的多个批量请求的key必须相同,否则实现起来会很复杂

增加一个 Controller 逻辑,当客户端发现 redis cluster 返回 MOVED 之后,立即触发一次 RedisClusterNamingService 同步更新。同时修改 PreHashLoadBalancer 的路由表

目前Controller能拿到LB,但是拿不到NamingService对象。Controller耦合NamingService似乎不是一个好主意,可以考虑只更新LB就可以。

增加 Controller 逻辑,当客户端发现 redis cluster 返回 ASK 之后,直接修改请求地址,并在请求之前增加一个 ASKING 指令并获得其返回值(ASKING 在每个 SLOT 变化的时候都需要重新发送至少一次),再重新 IssueRPC,做重定向。

这里需要一定的抽象,可以抽象一个通用的Controller Redirect流程

是否需要自定义 Controller 来处理和控制 Redis Cluster 请求的 MOVE 和 ASK

继承Controller父类,来实现Redis Cluster请求流程,这也是一个思路,但需要考虑Controller父类需要留出哪些接口给子类实现。毕竟Controller里的逻辑很复杂,不可能让子类完全重写一遍。

是否需要重新注册一个 Redis Cluster protocol 协议。因为从协议上来看,redis cluster protocol 和 redis 都用的 RESP2 协议。

看起来好像没必要?在协议解析这方面,并没有什么区别。

是否需要单独独立一个 RedisClusterRequest ,还是在 RedisRequest 上加个字段来区分是否使用的 cluster 模式?

如果实现了自定义的RedisController,也可以放在那里做一些cluster特有逻辑,就不用在Request上做区分

如果要计算 request_code 需要 aware 用户请求,那么需要修改 RedisRequest 的解析段。是否让用户主动传入,我们不做计算?(感觉这里如果放开了会是个暗坑)。

最好不要让用户传入

感觉可以考虑让用户单独传入key, 这样省去了从cmd中抽取key的开销, 代码也简单很多

jiangdongzi commented 1 year ago

@wayslog hi, 开发的怎么样了, 我可以协助一起开发

jiangdongzi commented 1 year ago

@wwbmmm @wayslog 又看了下draft, 感觉是不是不用定义新的PreHashLoadBalancer, 只是新定义 ReplicaPolicy即可 image

heartplus commented 3 months ago

https://github.com/bilibili/redis_sdk_cxx 来试试看这个