需求背景
需求背景
目前的Redis集群方案如Codis、Twemproxy、Redis Cluster都各有优缺点。其中,Codis和Twemproxy的使用最为典型。然而,Codis已经停止开源维护,并且只支持Redis的特定版本。此外,由于其组件众多,运维部署较为不便。Twemproxy的主要问题在于不支持数据迁移,这使得它难以满足线上需求。
针对以上问题,我们希望实现一个支持redis协议的集群方案,以解决上面存在的问题。
技术背景
我们将在twemproxy的开源版本的基础上,实现redis的集群方案。twemproxy的整体架构如下:
但当前twemproxy存在一些问题,如不支持数据迁移、不支持配置热加载、监控缺失等问题,在该开源基础上,主要解决以下问题:
- 数据分片的问题
- 数据迁移的问题
- 高可用的问题
- 高性能的问题
- 部署的易用性问题
设计目标
高可用
解决后端redis节点出现故障时,能够自动进行故障检测和恢复,实现故障自愈。
数据动态迁移
数据迁移主要是为了实现数据的动态扩缩容。当后端Redis实例的数据量达到阈值或低于阈值时,可能需要进行扩缩容,以满足业务的发展需求或节省资源。
高性能
redis的特点之一就是高性能,所以redis proxy的设计目标之一就是要高性能,高效完成redis请求的转发处理
监控
各个组件的情况能够及时上报监控平台并进行监控
易用性
这个暂时不是重点,可以采用ansible进行一键部署即可
整体设计
总体设计
总体架构图如下:
- slot:槽位,跟codis中使用的槽位一个概念,默认1024个槽位,一个key通过crc哈希映射到一个slot,相当于一个slot负责一部分数据
- redis group:一个由redis主备实例组成的集群,一个主,多个备,一个redis group负责一部分slot,用户的redis请求根据key所属的slot会被路由到某个redis group,然后由该主备实例提供服务
- 负载均衡层:对外提供一个vip给用户使用即可,业界方案可以用公司内部的负载均衡方案或者由keepalived/haproxy也可实现,不是本文重点
- etcd:元数据管理,保存数据迁移任务、slot映射关系、redis group下面的主备实例ip信息等
- configSvr: 管理服务,负责管理集群中各个组件,如管理redis proxy、管理redis group等、采用多可用区部署,此外还负责接收dashboard请求、redis server的HA、处理数据迁移等,基于etcd服务发现实现高可用以及使用raft协议实现HA
- redis proxy:redis的代理,无状态服务,接收redis的请求,将请求转发到后端redis节点,基于开源twemproxy改造而来,采用master worker机制实现高性能,后期融入dpdk来实现高性能转发
总体流程
访问redis流程:用户访问vip,vip转发请求到某个proxy节点,proxy节点根据key的请求计算得到slot,再根据slot到redis group的映射关系,将请求转发到某个redis group中去处理
HA流程:configSvr集群定期对redis group中的redis实例进行HA检测,发现故障,则通过raft协议实现故障转移和恢复
数据迁移:调用configSvr发起数据迁移任务,迁移任务入etcd,configSvr从etcd取迁移任务并进行并发的数据迁移
监控采集上报:redis proxy和configSvr分别实现exporter接口,开源promethus定期拉取接口进行监控上报即可,可采用prometheus/pushgateway + alertmanager + grafana实现监控数据拉取,展示和告警
详细设计
下面针对每个组件以及组件之间数据交互详细设计下。
开发约定
configSvr: 集群配置管理服务,负责集群中各个组件的管理、HA、数据迁移等
slot:槽位,抽象的一个概念,一个key会属于一个slot,一个slot会包含多个key
redis group: redis主备实例组成的redis实例组,一般由一主多备组成,一个redis group负责一部分slot
HA: 高可用故障检测和恢复,由configSvr定期对redis group中的server进行故障检测,发生故障,自动进行故障恢复,即故障自愈
raft协议:分布式共识协议,用于多个节点共识的一致
etcd:一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调,这里用来进行configSvr的服务发现以及元数据管理,如slot映射关系,slot迁移任务数据等
redis proxy: 集群redis代理节点,负责接收请求并转发处理
功能实现
数据结构设计
slot到redis group的映射关系:
type action struct {
Index int
State string // 迁移的状态:1:准备状态 2:待迁移 3: 迁移中 4: 迁移完成
TargetGroupId int // 目标迁移的redis group id
UpdatedAt int64
}
type slotIdInfo struct {
SlotID int
GroupID int
Action action
}
redis group到主备实例的映射关系:
type Server struct {
hostIp string
port int
}
type serverGroupInfo struct {
GroupID int
GroupName string
Servers []Server
}
存储设计
使用etcd来保存,slot到group的映射关系,key是一个slot路径,value是上面数据结构的json字符串即可,如下:
/redis-cluster/slot-to-group-mapping/slot1 -> {"group_id": "redis-group1", "slogId": "slot1", "action":{}"}
/redis-cluster/slot-to-group-mapping/slot2 -> {"group_id": "redis-group2", "slogId": "slot2", "action":{}}
/redis-cluster/slot-to-group-mapping/slot3 -> {"group_id": "redis-group1", "slogId": "slot3", "action":{}}
redis group对应的数据存储,key是group id, values是一个json字符串,保存了主备实例信息,如下:
/redis-cluster/group-instances/redis-group1 -> {
"master": "192.168.1.101:6379",
"replicas": [
"192.168.1.102:6379",
"192.168.1.103:6379"
]
}
/redis-cluster/group-instances/redis-group2 -> {
"master": "192.168.1.104:6379",
"replicas": [
"192.168.1.105:6379",
"192.168.1.106:6379"
]
}
redis proxy组件设计
设计思想:基于twemproxy开源基础上,实现master-多worker进程模型,master负责管理多个worker子进程,两者通过管道通信,多个worker子进程使用SO_REUSEPORT特性同时监听端口进行服务,由内核层面进行负载均衡的请求转发,并实现一些特性:
- 配置热加载:对外提供http配置更新接口,实现配置的热更新
- 支持按slot进行分片:维护slot映射数据以及redis group的配置数据,转发请求按slot转发到后端的节点
- 支持woker进程异常,自动拉起等
- worker子进程数可配置:根据需要可配置多个worker子进程来处理请求,实现高并发
- 监控添加exporter接口,监控数据采集完善
配置更新逻辑:配置数据维护在etcd中,configserver检测到etcd中配置数据有变动,则从etcd获取到最新配置,调用proxy的http接口更新配置,父进程收到配置,写本地文件,发信号给子进程进行更新,即
- 调用http接口更新配置:configSvr检测到配置变动,调用redis proxy的http接口更新配置
- redis proxy Update更新本地磁盘配置文件
- Signal给子进程
- 子进程Reload加载配置文件
configSvr组件设计
开发语言:go语言开发
- 组件管理:redis group管理、slot和redis group配置管理、redis proxy管理等
- dashboard服务:api服务,提供dashboard接口对外提供服务
- HA保障:redis server的HA,redis proxy HA
- 动态扩缩容:slot槽位数据迁移、redis group上下线等
- 协议:raft实现高可用以及HA
数据迁移的设计
数据迁移逻辑:通过configSvr提交数据迁移任务,任务数据入etcd,configSvr检测到数据迁移任务,更新proxy的配置信息,利用多协程调用redis的接口对目标slot进行迁移即可,数据迁移完成更新配置信息 redis proxy在迁移过程中的读写处理:当前处理的key所属的slot正在迁移,同步迁移时则调用 SLOTSMGRTTAGSLOT 命令将这个key迁移完成再返回给客户端,同时缓存该key已迁移,下次 访问即请求到新的redis group
HA流程的设计
HA保障逻辑:多个configSvr定期探测redis server接口,发现master节点故障,则利用raft协议选举leader进行故障恢复,具体过程是多个configSvr确认某个master节点故障,由leader进行故障恢复,具体逻辑是leader选一个最佳备节点提升为主节点,并更新redis group信息到etcd中,调用proxy的接口更新配置即可,跟redis sentinel有点类似,不过configSvr之间是直接通信,效率更高
监控设计
各个组件实现exporter接口即可
方案优缺点
优点:
- 相比twemproxy的优点:继承Twemproxy的优点,支持数据迁移、配置reload、提供dashboard接口可运维
- 相比codis的优点:基本具备codis的优点,无需改造redis,兼容开源的redis版本,可适配各种类redis产品,部署相比更简单,configSvr高效实现HA
- master worker进程模型,可横向扩展子worker数
- c语言开发,更加偏向底层,后续可尝试采用io uring或者dpdk来提高性能
缺点:
- proxy会带来额外的网络开销,会有性能损失
- 维护多个组件:etcd,configSvr
部署拓扑
redis proxy部署:多机器部署
redis server主备:不同机房主备异地部署
configSvr: 多可用区部署
etcd:部署etcd集群即可