使用Redis分布式锁实现主备

使用Redis分布式锁实现集群的主备

最近工作中遇到一个问题,我们会调用业务部门提供的HTTP接口获取所有的音视频任务信息,这些任务会被分发到各个机器节点进行处理。有两个方案:

方案一

为每台机器编号,比如有5台机器,编号为0,1,2,3,4,然后每台机器读取全量任务信息,将每个任务ID用机器总数量取余,然后和机器编号比较,相等的表示这个任务在此机器上执行。

  • 优点 可以达到任务分开处理的目的
  • 缺点 任务分配不均/一台机器死掉,分给这台机器的任务将永远不会被执行到/每台机器都需要读取HTTP信息,浪费资源。

方案二

我们使用其中一台机器将任务投递到Kafka中,然后所有机器消费这些任务。

但是需要做到以下2点:

  • 需要解决投递机器单点故障的问题,最好能达到一主多备。
  • 任务分配要均匀。

第一个问题是本文的重点,我们采用了Redis的分布式锁,下面要详细介绍。关于Kafka任务均匀投递的问题,需要自己实现调度模块,根据机器性能来投递到不同机器消费的partition中。

方案二解决了方案一的所有缺点,下面详细说一下分布式锁,做一个记录。

关于主备

主备是高可用集群中绕不开的问题,服务端一般使用nginx反向代理做一次负载均衡,但是如果nginx挂了呢,这就需要做主备(或者主主也可以),网上这个帖子很详细Nginx负载均衡高可用(keepalived+nginx实现主备)。但是我们遇到的问题优点特殊,我们做的是客户端的负载均衡,每次主动调用任务接口获取任务数据来进行处理。并且只能做主备,不能主主,不然会造成任务的重复投递。

Redis 分布式锁实现主备

第一次在工作中接触到redis,发现redis真是个好东西。分布式锁原理

我们的主备方案中,使用分布式锁来实现一个类似单例模式的逻辑。

  • 使用一个键值_master_IP来存储主机IP,并且设置过期时间(类似单例模式类里面的数据成员)。
  • 定义一个分布式锁,只有在键值_master_IP的值为空的时候,才会获取锁,设置键_deliver_task_IP的值(类似单例模式中的第一次构造函数调用)。

下面是流程图:
使用Redis分布式锁实现主备

  • 主备系统启动的时候Redis中没有键值_master_IP,所有机器会抢占Redis分布式锁_master_lock。
  • 抢锁成功的机器会变为主机,启动投递任务。并将_master_IP 值Set成自己的IP,并设置键值过期时间,这些操作完成后释放分布式锁。
  • 主机释放锁后,其他备机有可能抢占到锁,为了防止备机启动投递任务和写_master_IP,获取锁之后会再次判断_master_IP是否有值,如果有值说明主机已经起来了,直接返回即可。(有点类似于单例模式的双重锁)
  • 主机任务起来之后,各个机器每隔固定时间会去检测键值_master_IP,主机每次读取键值_master_IP后会自动Extend这个键值的Expire Time。备机发现键值有值并且不是自己就返回了。
  • 主机死掉之后,过了键值_master_IP的Expire Time, 键值会被删除。其他备机器会像整个系统启动的时候一样开始抢占锁并启动新的主机。

注意:一个Redis集群(只有一个Master)有可能出现一个锁被不同服务获取的情况(Master宕机,锁状态还没有来得及同步到Slave就会出现),这样会在不同的机器上启动投递任务,上面的流程中在下一个5秒后会判断,投递任务IP是否为本机IP,只保留本机的服务,其他服务全部停止。

相关推荐