当前位置: 首页 > 科技观察

如何防止网站离线从Redis2迁移到Redis4

时间:2023-03-20 20:47:25 科技观察

我们在SkyBetting&Gaming中使用Redis作为共享内存缓存,用于需要跨API服务器或Web服务器的身份验证令牌的操作。在CoreTribe中,它用于帮助处理不断增加的登录数量,尤其是在我们可以在一分钟内处理超过20,000次登录的繁忙时段。这在很大程度上适用于数据驻留在大量服务器上的情况(在70个ApacheHTTPD服务器的SSO令牌的情况下)。我们最近开始升级我们的Redis服务器以使用Redis3.2提供的原生集群功能。这篇博客希望能说明我们为什么使用集群,我们遇到的问题,以及我们的解决方案。一开始(或至少在升级之前),我们的遗留缓存由每个缓存的一对Redis服务器组成,使用保活来确保始终有一个主节点侦听浮动IP地址。当出现问题时,这些服务器对需要付出大量努力来管理,而且故障模式有时非常多样。有时,只允许读取其保存的数据但不允许写入的从节点会获得一个浮动IP地址。这个问题比较容易诊断,但它让任何程序都很难尝试使用缓存。麻烦。新应用程序因此,在这种情况下,我们需要构建一个新应用程序,一个使用共享内存缓存的应用程序,但我们不希望该缓存有一个迂回的故障转移过程。所以我们的需求是一个没有单点故障的共享内存缓存,能够在尽可能少的人为干预的情况下处理多种不同的故障模式,并且能够在事件恢复后以很少的人为干预进行恢复,一个额外的需求是提高缓存的安全性以减少数据泄露的范围(稍后会详细介绍)。当时RedisSentinel看起来很有前途,支持代理Redis连接的程序很多,比如twemproxy。这导致安装了许多其他组件,它应该以最少的人机交互工作,但它很复杂,需要大量服务器和服务才能运行并相互通信。会有大量的应用服务器与twemproxy通信,twemproxy会将它们的调用路由到合适的Redismaster节点,twemproxy会从sentinal集群获取master节点信息,并控制哪个Redis实例是master,哪个是master从。此设置很复杂,并且仍然存在单点故障,它依赖于twemproxy来处理分片,以连接到正确的Redis实例。它具有对应用程序透明的优点,因此我们理论上可以将现有应用程序移动到此Redis配置,而无需更改应用程序。但我们是从头开始构建应用程序,因此迁移应用程序不是必需的。好在这时候Redis3.2出来了,内置了原生集群,不再需要单独的sentinel集群。它具有更简单的设置,但twemproxy不支持Redis集群分片,它可以为您分片数据,但如果您尝试在与分片不一致的集群中进行分片,则会导致问题。有参考指南使其匹配,但集群可以自动更改形式并更改分片的设置方式。它仍然存在单点故障。正是在这一点上,我将永远感谢我的一位同事,他发现了一个用于Redis的Node.js集群发现驱动程序,使我们能够完全抛弃twemproxy。因此,我们能够自动分片数据,故障转移和故障恢复基本上是自动的。应用程序知道哪些节点存在,并且在写入数据时,如果写入错误的节点,集群将自动重定向该写入。这是选择的配置,它使我们的共享内存缓存在无需干预的情况下对基本故障模式相当健壮。在测试过程中,我们确实发现了一些错误。复制是在逐个节点的基础上完成的,所以如果我们丢失了一个主节点,它的从节点就会成为单点故障,直到死节点恢复服务,只有主节点投票支持集群健康,所以如果我们一次失去了太多主节点,集群无法自行恢复。但它比我们习惯的要好。向前迈进我们对使用集群Redis配置的新程序的遗留Redis实例的状态越来越不满意,但新程序无法扩展到现有程序(超过30GB的内存专用于我们最大的旧Redis实例数据库)。因此,随着Redis集群在幕后得到验证,我们决定将旧版Redis实例迁移到新的Redis集群。由于我们有一个原生支持Redis集群的Node.jsRedis驱动程序,我们开始将我们的Node.js程序迁移到Redis集群。但是,如何将千兆字节的数据从一个地方移动到另一个地方而不引起重大问题呢?特别是考虑到这些数据是身份验证令牌,所以如果它们有误,我们的最终用户将被注销。一种选择是让网站完全离线,将所有内容指向一个新的Redis集群,然后将数据迁移到其中,希望一切顺利。另一种选择是切换到新集群并强制所有用户重新登录。出于显而易见的原因,这些都不是很合适。我们决定采取的替代方案是将数据写入遗留Redis实例和替换它的集群,同时随着时间的推移逐渐向集群读取更多数据。由于数据的有效期有限(令牌在几个小时后过期),这种方法可以实现零停机并且不存在数据丢失的风险。所以我们做到了。迁移成功。剩下的是一个为我们的PHP代码提供服务的Redis实例(其中一项是有用的,其他的最终都是不必要的),我们在此过程中遇到了一个困难,实际上是两个困难。首先也是最紧迫的是找到要在PHP中使用的Redis集群发现驱动程序,以及我们正在使用的PHP版本。当我们升级到最新版本的PHP时,这被证明是有效的。我们选择的驱动程序不喜欢使用Redis进行授权的方式,因此我们决定使用Redis集群作为额外的安全步骤(我告诉你,它会有更多的安全性)。当我们用Redis集群替换每个遗留Redis实例时,修复似乎很简单,关闭Redis授权以便它响应所有请求。然而,事实并非如此,出于某种原因,Redis集群不接受来自Web服务器的连接。Redis在版本3中引入的一项名为“保护模式”的新安全特性将在Redis绑定到任何接口时停止监听来自外部IP地址的连接,并且不需要配置Redis授权密码。事实证明这很容易修复,但我们要小心。现在?那就是我们所在的地方。我们已经迁移了一些遗留的Redis实例,并且正在迁移其余实例。通过这样做,我们解决了一些技术债务并提高了平台的稳定性。使用RedisCluster,我们还可以扩展内存数据库并扩展它们。Redis是单线程的,因此只要在单个实例中保留更多内存就可以实现如此大的增长,而我们已经紧随其后。我们期待新集群带来的性能提升,这也为我们提供了更多扩展和负载平衡选项。未来呢?我们解决了一些技术债务,这让我们的服务更容易支持,也更稳定。但这并不意味着工作已经完成,Redis4似乎有一些我们可能想要研究的功能。Redis并不是我们使用的唯一软件。我们将继续致力于改进平台并减少处理技术债务所花费的时间,但随着我们客户群的增长以及我们努力提供更丰富的服务,我们总会遇到需要改进的地方。下一个挑战可能与从每分钟超过20,000次登录扩展到超过40,000次甚至更高有关。