Redis高可用服务架构分析与构建实践
基于内存的Redis应该是各个Web开发企业最常用的key-value数据库了。我们经常用它来存储我们商店中的用户登录状态(Session 存储)。一些热点数据查询的加速(相比mysql有数量级的速度提升)、简单消息队列(LPUSH和BRPOP)、订阅发布(PUB/SUB)系统等。专门的团队为各种业务呼叫提供 Redis 托管作为核心服务。
然而,来电者询问所有基础服务提供商:你们的服务是否非常可用?最好不要因常见服务问题而损害我的业务。我最近还在我的项目中构建了一个小型的“高可用”Redis 服务。以上是我的总结和想法。
首先我们需要判断Redis服务的高可用,即在各种异常情况下是否能够继续正常提供服务。或者更放松一点。一旦出现异常情况,可以在短时间内恢复正常服务。所谓例外至少必须包含以下几种可能性:
[1.异常]某节点服务器进程突然停止(比如开发者杀死了某服务器的redis-server进程)
【异常2】某节点服务器宕机了,相当于该节点上所有进程停止(例如工作和维护不利导致服务器供电中断;例如某些旧机器出现硬件故障)
【3.例外】 任意两个节点服务器之间的通信中断(比如临时工用断手挖出了两个机房通信用的光缆)
其实,以上任何一个例外都是微不足道的。概率事件和实现高可用性的基本指导原则:几个低概率事件同时发生的概率可以忽略不计。只要系统设计为能够容忍短时间内的单点故障,就可以实现高可用性。
网络上有很多构建高可用Redis服务的解决方案,例如Keepalived、Codis、Twemproxy、Redis Sentinel等。其中Codis和Twemproxy主要应用于大型Redis集群。在Redis正式发布Redis Sentinel之前,Twitter和豌豆荚也有开源解决方案。
我的业务数据量不大,所以集群服务很浪费机器。我最终在 Keepalived 和 Redis Sentinel 之间进行选择,并选择了官方的 Redis Sentinel 解决方案。
Redis Sentinel可以理解为一个监控Redis Server服务是否正常的进程,一旦检测到异常,就可以自动启用备份(从)Redis Server,让外部用户察觉不到异常体制内。 Redis 服务。从简单到复杂,我们按照步骤构建一个最小且高可用的Redis服务。
1。选项:不带Sentinel的独立版Redis Server
一般情况下,我们搭建个人网站或者做日常开发时,都会搭建一个Redis Server的单实例。调用者可以直接连接Redis服务,甚至客户端本身和Redis在同一台服务器上。
此组合仅供个人学习和娱乐。毕竟这个配置总会有无法解决的bug。如果Redis服务进程卡住或者服务器1关闭,服务将不可用。并且如果没有配置Redis数据保留,已经存储在Redis中的数据将会丢失。
2。选项:主从同步Redis Server,单副本Sentinel
为了实现高可用,解决方案一中描述的单点故障问题,我们需要添加一个备份服务,即两台服务器各增加一个备份服务。启动 Redis 服务器进程。通常Master提供服务,Slave只负责同步和备份。
同时额外启动一个Sentinel进程,监控两个Redis Server实例的可用性,以便当master挂掉时,slave能够及时提升为master角色,继续提供服务。 ,从而实现Redis Server的高可用。
这是基于高可用服务设计,即单点故障本身就是低概率事件,同时多个单点故障(即master和slave同时挂掉)是(基本上)被认为是不可能的。事件。
对于 Redis 服务调用方,连接的是 Redis Sentinel 服务,而不是 Redis 服务器。常见的调用流程是,客户端首先连接Redis Sentinel,询问当前Redis服务器哪个服务为主,哪个服务为从,然后连接对应的Redis服务器进行操作。
当然,目前的第三方库通常已经实现了这个调用流程,我们不再需要手动实现(如Nodejs ioredis、PHP predis、Golang go-redis/redis、JAVA jedis等)。
然而Redis Server服务主从切换后,又出现了一个新的问题,那就是Redis Sentinel本身就是一个单点服务。如果 Sentinel 进程挂起,客户端将无法连接到 Sentinel。 。因此,选项2配置并不能实现高可用性。
3。选项:Redis Server主从同步、双实例Sentinel
为了解决方案二的问题,我们还额外启动一个Redis Sentinel进程,两个Sentinel进程同时向客户端提供服务发现功能时间。对于客户端,您可以连接任何Redis Sentinel服务来获取当前Redis Server实例的基本信息。我们通常在客户端配置多个Redis Sentinel引用地址。如果客户端发现某个地址无法连接,则会尝试连接其他Sentinel实例。当然,我们不必手动完成。在各种开发语言中比较流行的redis连接库帮助实现了这个功能。我们的期望是,即使一个Redis Sentinel宕机了,也会有另一个Sentinel可以提供服务。
然而,愿景很美好,现实却很残酷。采用这样的架构,仍然无法实现Redis服务的高可用。在选项3的示意图中,红线表示两台服务器之间的通信,我们设想的异常场景([异常2])是某台服务器完全宕机。我们还可以假设服务器 1 已关闭。目前,服务器 2 上只有 Redis Sentinel 和从属 Redis Server 正在处理。
Sentinel 目前不会将剩余的 Slave 切换为 Master 继续服务,这会导致 Redis 服务不可用,因为 Redis 设置是只有超过 50% 的 Sentinel 进程才能连接并投票,主从切换只有在创建新的master时才会发生。本例中两个Sentinel只能连接其中一个,对应50%,不存在主从切换的场景。
你可能会问为什么Redis会有这个50%的设置?假设我们允许小于等于50%的Sentinel连接速率,也可以进行主从切换。想象一下[3。异常],即服务器1和服务器2之间的网络宕机了,但是服务器本身是工作的。如下图所示:
实际上,对于服务器2来说,如果服务器1直接停止了,那么和服务器1无法连接网络的效果是一样的。反正突然就无法交流了。假设当网络出现故障时,我们允许服务器2上的Sentinel从slave切换到master。结果就是你现在拥有了两台可以对外提供服务的Redis服务器。客户执行的任何添加、删除或修改操作都可能落入服务器 1 Redise 或服务器 2 Redise(取决于客户连接到哪个 Sentinel),从而导致数据中断。如果稍后服务器1和服务器2之间的网络恢复,我们仍然无法合并数据(两个不同的数据,我们应该相信谁?)并且数据一致性被完全破坏。
4。方案:主从同步Redis Server,三个Sentinel实例
由于方案3没有实现高可用,最终版本为方案4,如上图所示。我们实际上构建了这个架构。我们部署了服务器3,并在3上构建了Redis Sentinel进程。现在三个 Sentinel 进程管理两个 Redis Server 实例。在这种场景下,无论是单进程故障、单机故障,还是两台机器之间的网络通信故障,Redis服务仍然对外可用。
另外,如果你的机器比较空闲,当然你也可以在服务器3上开一个Redis Server,打造1主+2从架构。所有数据都有两个备份副本,可用性将得到提高。当然,奴隶越多越好。毕竟主从同步也是需要时间的。
场景 4 中,如果服务器 1 与其他服务器之间的通信完全丢失,则服务器 2 和 3 将从从服务器变为主服务器。该客户端当前由2个master提供服务,网络恢复后,中断期间落在服务器1上的任何新数据都将丢失。如果想部分解决这个问题,可以配置Redis Server进程,使其在检测到自身网络出现问题时,立即停止服务,避免在网络故障期间接收新数据(参见Redis min.slaves-to) -write 和 min-slaves-max-lag 是两个配置项)。
此刻,我们已经使用3台机器构建了一个高可用的Redis服务。其实网上有一种对机器更友好的方法,就是在Client的机器上放置一个Sentinel进程,而不是在provider的机器上。只是公司的总服务者和调用者不是来自同一个团队。当两个团队一起操作同一台机器时,很容易因为沟通问题而出现故障。因此,考虑到人为因素,我们仍然采用方案4的架构。并且由于服务器3只运行一个Sentinel进程,因此不会消耗大量服务器资源。服务器 3 还可以用于运行其他服务。
易于使用:使用Redis Sentinel作为Redis的独立版本
作为服务提供商,我们总是谈论用户体验。上述的解决方案中,总有一些让客户使用起来不太方便。对于独立版本的 Redis,客户端直接连接到 Redis 服务器。我们只需要提供IP和端口,客户端就可以使用我们的服务。切换到Sentinel模式后,客户端需要使用一些支持Sentinel模式的外部依赖包,并修改自身的Redis连接配置,这对于“要求较高”的用户来说显然是无法接受的。有没有办法只给Client一个固定的IP地址和端口来提供服务,例如使用独立版本的Redis?
答案当然是肯定的。这就可能需要引入虚拟IP(Virtual IP,VIP),如上图所示。虚拟IP可以指向Redis Server master所在的服务器。当Redis主从切换发生时,会触发回调脚本。回调脚本将VIP切换到slave所在的服务器。这样,在客户端看来,他们仍然在使用独立版本的高可用 Redis 服务。
结论
构建任何服务并使其“可用”实际上非常容易,就像独立版本的Redis一样。但一旦你想实现“高可用性”,事情就变得复杂了。商店中额外使用了两台服务器,3个Sentinel进程+1个Slave进程,只是为了确保在不太可能发生的崩溃情况下服务仍然可用。在实际业务流程中,我们也让主管能够对流程进行监控。一旦进程意外退出,它就会自动尝试重新启动。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。