导读:本期聚焦于宋琮安创作的《如何用Ruby实现VRRP抢占模式下的优先级比较与Master选举?》,敬请观看详情。实现一个简单的VRRP抢占模式不一定需要引入复杂路由套件,用Ruby就能把优先级比较和Master选举的核心逻辑跑通。VRRP通过组播报文同步多台路由器状态,抢占模式下高优先级节点会立即取代当前Master。本文先梳理VRRP状态机与报文格式,再给出一个基于内存队列的Ruby实现,重点拆解优先级相同与不同时的比较分支、抢占延迟参数以及状态切换的边界条件。代码构建了一个可运行的本地模拟环境,通过调整优先级数值可以直观看到Backup抢占为Master的完整过程,文中所有代码均基于Ruby标准库,不依赖第三方gem,方便直接运行和调试。

VRRP(虚拟路由冗余协议)常被用于网关高可用,而抢占模式是其中最容易被误解的一环。很多教程只讲配置,很少深入优先级比较和Master选举的代码逻辑。本文用Ruby构建一个简化的VRRP节点模型,用内存队列模拟组播网络,重点展示不同优先级下状态切换的细节。

如何用Ruby实现VRRP抢占模式下的优先级比较与Master选举?

实现前先明确一个关键点:抢占模式并不是简单的高优先级覆盖,它受到报文间隔、skew_time以及状态机顺序的约束。下面的内容会从协议基础一直走到可运行的Ruby代码。

一、VRRP抢占模式的工作机制

VRRP将一组路由器组成一个虚拟路由器,对外暴露一个虚拟IP地址。组内所有节点运行同样的VRID,某一时刻只有一个节点处于Master状态,负责转发发往虚拟IP的流量,其余节点处于Backup状态并持续监听Master的Advertise报文。协议标准定义优先级范围为1到254,默认值为100,数值越大表示选举优势越明显。0和255是保留值:0表示当前Master主动放弃角色,255表示该节点拥有虚拟IP地址,一般不参与普通选举。

抢占模式是VRRP默认开启的行为。当Backup收到来自Master的Advertise报文后,会提取报文中的优先级字段并与本地优先级比较。如果本地优先级高于报文优先级,Backup会立即或延迟一段时间后切换为Master,并开始发送自己的Advertise报文。即使当前Master运行正常,只要出现更高优先级节点,抢占就会发生。关闭抢占模式后,即便Backup优先级更高,也必须等待当前Master失效才能参与下一次选举。

抢占延迟并不是协议必须实现的参数,但在实际系统中经常需要配置。标准的skew_time计算公式为(256减去优先级)除以256,它用于让高优先级节点在Master失效后更快超时。计算master_down_interval时使用3倍advert间隔加上skew_time。优先级越高,skew_time越短,抢占响应越快。这个细节在后面代码中会体现为定时器的动态调整。

二、用Ruby构建VRRP核心类与状态机

为了让代码可以直接运行,这里不使用真实网络套接字,而是引入一个共享的消息总线对象。两个节点实例通过该总线交换报文,每个报文就是一个简单的哈希结构,包含VRID、发送方优先级、发送方状态以及时间戳。真实环境中这些字段会编码进ICMP或UDP报文,但简化后不影响核心逻辑。

首先定义VrrpNode类。初始化时传入优先级、VRID和消息总线,状态初始为Backup,并启动一个监听线程。状态机包含三个状态:Initialize、Backup和Master。Initialize只在启动瞬间存在,随后根据是否有更高优先级节点决定进入Master或Backup。监听线程不断从总线中取出报文,调用handle_advertise方法处理。

require 'thread'

class VrrpNode
  attr_reader :vrid, :priority, :state

  def initialize(vrid:, priority: 100, bus:)
    @vrid = vrid
    @priority = priority
    @bus = bus
    @state = :initialize
    @master_down_interval = 3.0
    @last_advert = Time.now
    start_listener
  end

  def start_listener
    Thread.new do
      loop do
        msg = @bus.pop
        handle_advertise(msg) if msg[:vrid] == @vrid
      end
    end
  end

  def handle_advertise(msg)
    return if msg[:sender].equal?(self)
    case msg[:type]
    when :advertise
      process_advert(msg)
    when :shutdown
      process_shutdown(msg)
    end
  end
end

上面代码中的线程只负责从总线取出报文并分发。因为多个节点可能同时操作总线,实际项目里需要用Mutex或Queue保证线程安全。这里为了突出VRRP状态机,省略了互斥处理。真实环境里的VRRP报文通过组播发送,每个节点都会收到自己发出的报文,所以代码中通过sender对象判断忽略自身报文。

Advertise报文的发送不是由监听线程负责,而是由状态机触发。节点进入Master状态后会以固定间隔向总线广播Advertise报文。该间隔默认1秒,在测试中可以调小。Master还需维护一个master_down计时器,用于在长时间未收到其他节点报文时确认自身角色是否仍然有效。

三、优先级比较与Master选举逻辑实现

优先级比较需要覆盖三种情况:本地优先级高于对方、本地优先级低于对方、两者相等。前两种情况可以直接决定是否抢占或保持Backup,第三种情况则要引入IP地址比较。真实VRRP在优先级相同时选举拥有较大IP地址的节点为Master。我们的简化模型没有IP地址,可以给每个节点增加一个唯一的比较键,比如节点名称或编号。

process_advert方法的核心分支如下。当本地优先级高于对方时,如果当前不是Master并且抢占模式开启,则切换到Master。如果本地优先级低于对方,并且当前是Master,则需要降级为Backup,同时更新master_down定时器。优先级相等时比较比较键,比较键较大的一方拥有选举优势。

  def process_advert(msg)
    return if @state == :master && @priority > msg[:priority]

    if @priority > msg[:priority]
      become_master if @preempt_mode
    elsif @priority < msg[:priority]
      become_backup if @state == :master
    else
      if @compare_key > msg[:compare_key]
        become_master if @preempt_mode
      elsif @compare_key < msg[:compare_key]
        become_backup if @state == :master
      end
    end
    @last_advert = Time.now
  end

  def become_master
    return if @state == :master
    @state = :master
    puts "Node #{@compare_key} becomes Master"
    start_advert_timer
  end

  def become_backup
    return if @state == :backup
    @state = :backup
    puts "Node #{@compare_key} falls back to Backup"
  end

代码中的&&表示逻辑与,优先级比较使用了大于和小于号。需要留意一个容易忽略的细节:当本地节点已经是Master且收到优先级相同的报文时,如果比较键较小,也会降级为Backup。这符合VRRP规范,因为选举原则是优先级加IP地址双重比较。

抢占延迟的实现依赖定时器。一个常见的做法是记录收到低优先级报文的时间,然后启动一个延迟定时器,延迟时间可以配置为固定值,也可以使用skew_time。延迟到期后再检查当前是否仍是Backup状态,如果是才执行become_master。这种方式可以避免因网络抖动或瞬时报文导致的频繁角色切换。

四、本地模拟测试与抢占行为观察

测试脚本创建了两个节点,优先级分别为100和200。低优先级节点先启动并成为Master,高优先级节点随后加入。因为抢占模式默认开启,高优先级节点一旦收到低优先级Advertise报文,就会立即抢占。可以通过输出观察状态变化。

bus = Queue.new
node_a = VrrpNode.new(vrid: 1, priority: 100, compare_key: 'A', bus: bus)
node_b = VrrpNode.new(vrid: 1, priority: 200, compare_key: 'B', bus: bus)

node_a.instance_variable_set(:@state, :master)
node_a.send(:start_advert_timer)

sleep 0.5
bus << { vrid: 1, priority: 100, compare_key: 'A', type: :advertise, sender: node_a }

sleep 1.5
puts "Final state A: #{node_a.state}, B: #{node_b.state}"

运行这段代码后,节点B会收到节点A的Advertise报文并发现自身优先级更高,随即切换到Master状态,而节点A会降级到Backup。如果关闭节点B的抢占模式,即便优先级更高,节点A也会继续担任Master,直到自己失效。这个行为可以通过在类中增加@preempt_mode = false来测试。

除了优先级,使用skew_time计算master_down_interval也值得验证。当Master突然停止发送Advertise报文时,所有Backup会启动超时计时器,超时时间等于3倍advert间隔加skew_time。优先级较高的节点skew_time更短,会率先超时成为Master,从而避免多个Backup同时抢占。这个机制保证了在Master失效后,高优先级节点总是更快接管虚拟IP。

最后一个边界情况是优先级0和255。优先级0的报文表示当前Master主动放弃角色,收到该报文后Backup会立即开始选举,而不会等待超时。优先级255一般用于拥有虚拟IP地址的节点,它在所有节点中具有绝对优势,即便优先级不是最高也会被视为Master。实现中可以将这两个值作为特殊分支单独处理,避免与普通优先级混淆。

到这里,一个基于Ruby的简化VRRP抢占模式实现就完成了。虽然省略了真实网络报文编解码和组播细节,但状态机、优先级比较和Master选举的核心逻辑都可以直接运行并观察。理解了这些机制后,再去看Keepalived等成熟实现就不会感到陌生。

VRRP抢占模式Master选举修改时间:2026-09-18 19:54:42

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0918/58941.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。