Home
img of docs

讲解RabbitMQ的核心概念,以及如何协作实现灵活的消息路由,如何使用 HAProxy 进行负载均衡的

chou403

/ RabbitMQ

/ c:

/ u:

/ 51 min read


一学一个不吱声

RabbitMQ 2007年发布,是一个在 AMQP 基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。

代理效应

代理和负载平衡器在客户端与其目标节点之间引入了额外的网络跃点(甚至多个)。中介也可以成为网络争用点: 它们的吞吐量将成为整个系统的限制因素。因此,代理和负载平衡器的网络带宽超额配置和吞吐量监视非常重要。

当中间商在一段时间内没有任何活动时,它们也可以终止”空闲” TCP连接。在大多数情况下,这是不可取的。此类事件将导致服务器端的突然关闭连接日志消息以及客户端的I / O异常。

在连接上启用心跳后,将导致周期性的轻型网络流量。因此,心跳具有保护客户端连接的副作用,该客户端连接可能会闲置一段时间,以防止代理和负载平衡器过早关闭。

从10到30秒的心跳超时将经常产生周期性的网络流量(大约每5到15秒一次),以满足大多数代理工具和负载均衡器的默认设置。太低的值将产生误报。

主要特性

  1. 可靠性: 提供了多种技术可以让你在性能和可靠性质检进行权衡。这些技术包括持久性机制,投递确认,发布者证实和高可用性机制;
  2. 灵活的路由: 消息在到达队列前是通过交换机进行路由的。RabbitMQ 为典型的路由逻辑提供了多钟内置交换机类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做 RabbitMQ 的插件使用;
  3. 消息集群: 在相同局域网中的多个 RabbitMQ 服务器可以聚合在一起,作为一个独立的逻辑代理来使用;
  4. 队列高可用: 队列可以在集群中的机器上进行镜像,以确保在硬件问题下还保证消息安全;
  5. 多种协议的支持: 支持多种消息队列协议;
  6. 多语言支持: 服务器端用 Erlang 语言编写,支持只要是你能想到的所有编程语言;
  7. 管理界面: RabbitMQ 有一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面;
  8. 跟踪机制: 如果消息异常,RabbitMQ 提供消息跟踪机制,使用者可以找出发生了什么;
  9. 插件机制: 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。

使用 RabbitMQ 需要:

  • Erlang 语言包
  • RabbitMQ 安装包

RabbitMQ 可以运行在 Erlang 语言所支持的平台上:

  • Solaris
  • BSD
  • Linux
  • MacOSX
  • TRU64
  • Windows NT/2000/XP/Vista/Windows 7/Windows 8
  • Windows Server 2003/2008/2012
  • Windows 95, 98
  • VxWorks

优点

  1. 由于 Erlang 语言的特性,MQ 性能较好,高并发;
  2. 健壮,稳定,易用,跨平台,支持多种语言,文档齐全;
  3. 有消息确认机制和持久化机制,可靠性高;
  4. 高度可定制的路由;
  5. 管理界面较丰富,在互联网公司也有较大规模的应用;
  6. 社区活跃度高。

缺点

  1. 尽管结合 Erlang 语言本身的并发优势,性能较好,但是不利于做二次开发和维护;
  2. 实现了代理架构,意味着消息在发送到客户端之前可以在中央节点上排队,此特性使得 RabbitMQ 易于使用和部署,但是使得其运行速度较慢,因为中央节点增加了延迟,消息封装后也比较大;
  3. 需要学习比较复杂的接口和协议,学习和维护成本较高。

工作原理

image-20230517155538862

  • Broker: 接收和分发消息的应用,RabbitMQ Server就是 Message Broker。
  • Virtual Host: 出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等。
  • Connection: publisher/consumer 和 broker 之间的 TCP 连接。
  • Channel: 如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销。
  • Exchange: message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有: direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。
  • Queue: 消息最终被送到这里等待 consumer 取走。
  • Binding: exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据。

如何实现生产者和消费者

交换机类型

  • Direct Exchange(直连交换机)

    路由键与队列完全匹配交换机。此种类型交换机,通过 RoutingKey 路由键将交换机和队列进行绑定,消息被发送到 exchange 时,需要根据消息的 RoutingKey 进行匹配,只将消息发送到完全匹配到此 RoutingKey 的队列(如果匹配了多个队列,则每个队列都会收到相同的消息)。

    比如: 如果一个队列绑定到交换机要求路由键为 “key”,则只转发 RoutingKey 标记为 “key” 的消息,不会转发 “key1”,也不会转发 “key2” 等等。它是完全匹配,单播的模式。

    image-20230519161049311

  • Fanout Exchange(扇型交换机)

    Fanout,此种交换机,会将消息分发给所有绑定了次交换机的队列,此时 RoutingKey 参数无效。这个模式类似于广播。它是所有交换机速度最快的。

    Fanout 类型交换机下发送消息一条,无论 RoutingKey 是什么,queue1,queue2,queue3,queue4 都可以收到消息。

    image-20230519161221269

  • Topic Exchange(主题交换机)

    应用范围最广的交换机类型,消息队列通过消息主题与交换机绑定。一个队列可以通过多个主题与交换机绑定,多个消息队列也可以通过相同消息主题和交换机绑定。并且可以通过通配符(*或者#)进行多个消息主题的适配。

    Topic,主题类型交换机,此种交换机与 Direct 类似,也是需要通过 RoutingKey 路由键进行匹配分发,区别在于 Topic 可以进行模糊匹配,Direct 是完全匹配。

    1. Topic 中,将 RoutingKey 通过 ”.” 来分成多个部分。
    2. ”*” 代表一个部分。
    3. ”#” 代表0个或多个部分(如果绑定的路由键为 ”#” 时,则接收所有消息,路由键所有都匹配)。

    image-20230519162427816

    然后发送一条消息,RoutingKey 为 “key1.key2.key3.key4”,那么根据 ”.” 将这个路由键分为了四个部分,此条路由键将会匹配:

    1. key1.key2.key3.*: 成功匹配,因为*可以代表一个部分。
    2. key1.#: 成功匹配,因为 可以代表0或多个部分。
    3. *.key3.*.key4: 成功匹配,因为第一和第三部分分别为key1和key3,且为四个部分,刚好匹配。
    4. #.key3.key4: 成功匹配,#可以代表多个部分,正好匹配中了key1和key2。

    如果发送消息 RoutingKey 为 “key1”,那么将只能匹配中 key1.#,#可以代表0个部分。

  • Headers Exchange(头交换机)

    与 RoutingKey 无关,匹配机制是匹配消息头中的属性信息。在绑定消息队列与交换机之前声明一个map键值对,通过这个map对象实现消息队列和交换机的绑定。当消息发送到 RabbitMQ 时会取到该消息的 headers 与 exchange 绑定时指定的键值对进行匹配,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。

    匹配规则 x-match 有下列两种类型:

    • x-match = all: 表示所有的键值对都匹配才能接收到消息。
    • x-match = any: 表示只要有键值对匹配就能接收到消息。

image-20230523095354400

镜像队列

背景

单节点的 RabbitMQ 存在性能上限,可以通过垂直或者水平扩容的方式增加 RabbitMQ的吞吐量。垂直扩容指的是提高 CPU 和内存的规格;水平扩容指部署 RabbitMQ 集群。

通过将单个节点的队列相对平均地分配到集群的不同节点,单节点的压力被分散,RabbitMQ 可以充分利用多个节点的计算和存储资源,以提升消息的吞吐量。

但是多节点的集群并不意味着有更好的可靠性—每个队列仍只存在于一个节点,当这个节点故障,这个节点上的所有队列都不再可用。

在 3.8 以前的版本,RabbitMQ 通过镜像队列(Classic Queue Mirroring)来提供高可用性。但镜像队列存在很大的局限性,在 3.8 之后的版本 RabbitMQ 推出了 Quorum queues 来替代镜像队列,在之后的版本中镜像队列将被移除。

镜像队列通过将一个队列镜像(消息广播)到其他节点的方式来提升消息的高可用性。当主节点宕机,从节点会提升为主节点继续向外提供服务。

描述

RabbitMQ 以队列维度提供高可用的解决方案——镜像队列。

配置镜像队列规则后,新创建的队列按照规则成为镜像队列。每个镜像队列都包含一个主节点(Leader)和若干个从节点(Follower),其中只有主节点向外提供服务(生产消息和消费消息),从节点仅仅接收主节点发送的消息。

从节点会准确地按照主节点执行命令的顺序执行动作,所以从节点的状态与主节点应是一致的。

配置方法

使用策略(Policy)来配置镜像策略,策略使用正则表达式来配置需要应用镜像策略的队列名称,以及在参数中配置镜像队列的具体参数。

按此步骤创建镜像策略,该策略为所有 mirror* 开头的队列创建 3 副本镜像。

image-20230523112017966

创建完的策略如下图显示:

image-20230523112042677

参数解释:

  • Name: policy的名称,用户自定义。
  • Pattern: queue的匹配模式(正则表达式)。^表示所有队列都是镜像队列。
  • Definition: 镜像定义,包括三个部分ha-sync-mode,ha-mode,ha-params。
    • ha-mode: 指明镜像队列的模式,有效取值范围为all/exactly/nodes。
      • all: 表示在集群所有的代理上进行镜像。
      • exactly: 表示在指定个数的代理上进行镜像,代理的个数由ha-params指定。
      • nodes: 表示在指定的代理上进行镜像,代理名称通过ha-params指定。
    • ha-params: ha-mode模式需要用到的参数。
    • ha-sync-mode: 表示镜像队列中消息的同步方式,有效取值范围为: automatic,manually。
      • automatic: 表示自动向master同步数据。
      • manually: 表示手动向master同步数据。
  • Priority: 可选参数, policy的优先级。

配置规则

配置完 Policy 后,创建新的队列,或者原有的的队列,如果队列名称符合 Policy 的匹配规则,则该队列会自动创建为镜像队列。

下图中 mirror_queue 匹配之前创建的镜像策略,为镜像队列。normal_queue 为普通队列。

image-20230523112755735

镜像队列显示的蓝色 +2 表示同步副本数为 2 个。此处如果用红色显示,则表示为同步副本数

显示的 mirror-policy 为该队列应用的镜像策略。

点击队列名称可以进入查看队列详细信息,从中可以看出队列的主节点,从节点和镜像策略。

image-20230523112856113

配置参数

镜像策略
ha-modeha-params结果
exactlycount集群中队列副本的数量(主队列加上镜像)。count值为1表示一个副本: 只有主节点。如果主节点不可用,则其行为取决于队列是否持久化。count值为2表示两个副本: 一个队列主队列和一个队列镜像。换句话说:“镜像数=节点数-1”。如果运行队列主服务器的节点变得不可用,队列镜像将根据配置的镜像提升策略自动提升到主服务器。如果集群中的可用节点数少于count,则将队列镜像到所有节点。如果集群中有多个计数节点,并且一个包含镜像的节点宕机,那么将在另一个节点上创建一个新镜像。使用’ exactly ‘模式和’ ha-promot-on-shutdown ’: ’ always ‘可能是危险的,因为队列可以跨集群迁移,并在停机时变得不同步。
all不设置队列跨集群中的所有节点镜像。当一个新节点被添加到集群中时,队列将被镜像到该节点。这个设置非常保守。建议设置的副本值为大多数节点N / 2 + 1。镜像到所有节点会给所有集群节点带来额外的负担,包括网络I/O,磁盘I/O和磁盘空间的使用。
nodes节点名称队列被镜像到节点名中列出的节点。节点名是在rabbitmqctl cluster_status中出现的Erlang节点名;它们的形式通常是”rabbit@hostname”。如果这些节点名中有任何一个不是集群的一部分,则不构成错误。如果在声明队列时列表中的节点都不在线,则将在声明客户机连接的节点上创建队列。
新镜像同步策略
ha-sync-mode说明
manual这是默认模式。新队列镜像将不接收现有消息,它只接收新消息。一旦使用者耗尽了仅存在于主服务器上的消息,新的队列镜像将随着时间的推移成为主服务器的精确副本。如果主队列在所有未同步的消息耗尽之前失败,则这些消息将丢失。您可以手动完全同步队列,详情请参阅未同步的镜像部分。
automatic当新镜像加入时,队列将自动同步。值得重申的是,队列同步是一个阻塞操作。如果队列很小,或者您在RabbitMQ节点和ha-sync-batch-size之间有一个快速的网络,那么这是一个很好的选择。
从节点晋升策略

镜像队列主节点出现故障时,最老的从节点会被提升为新的主节点。如果新提升为主节点的这个副本与原有的主节点并未完成数据的同步,那么就会出现数据的丢失,而实际应用中,出现数据丢失可能会导致出现严重后果。

rabbitmq 提供了 ha-promote-on-shutdown,ha-promote-on-failure 两个参数让用户决策是保证队列的可用性,还是保证队列的一致性;两个参数分别控制正常关闭,异常故障情况下从节点是否提升为主节点,其可设置的值为 when-synced 和 always。

ha-promote-on-shutdown/ha-promote-on-failure说明
when-synced从节点与主节点完成数据同步,才会被提升为主节点
always无论什么情况下从节点都将被提升为主节点

这里要注意的是ha-promote-on-failure设置为always,插拔网线模拟网络异常的两个测试场景: 当网络恢复后,其中一个会重新变为mirror,具体是哪个变为mirror,受cluster_partition_handling处理策略的影响。

例如两台节点A,B组成集群,并且cluster_partition_handling设置为autoheal,队列的master位于节点A上,具有全量数据,mirror位于节点B上,并且还未完成消息的同步,此时出现网络异常,网络异常后两个节点交互决策: 如果节点A节点成为赢家,此时B节点内部会重启,这样数据全部保留不会丢失;相反如果B节点成为赢家,A需要重启,那么由于ha-prromote-on-failure设置为always,B节点上的mirror提升为master,这样就出现了数据丢失。

主队列选择策略

RabbitMQ中的每个队列都有一个主队列。该节点称为队列主服务器。所有队列操作首先经过主队列,然后复制到镜像。这对于保证消息的FIFO排序是必要的。

通过在策略中设置 queue-master-locator 键的方法可以定义主队列选择策略,这是常用的方法。

image-20230523144411570

此外,也可以用队列参数 x-queue-master-locator 或配置文件中定义 queue_master_locator 的方式指定,此处不再赘述。

下面是该策略的可选参数列表:

queue-master-locator说明
min-masters选择承载最小绑定主机数量的节点
client-local选择客户机声明队列连接到的节点
min-masters随机选择一个节点

负载均衡-HAProxy

对 RabbitMQ集群中的节点做负载均衡:

  • 客户端负载均衡
  • HAProxy实现负载均衡

客户端负载均衡

要实现一个完整的负载均衡主要是实现以下功能:

  • 请求需要按照规则打散到各个集群的节点。

  • 节点的宕机需要负载均衡器自我感知并且进行剔除,这样就避免节点都宕掉了还在向宕掉的节点发送请求,导致大量的请求失败。

  • 节点的新增其实还好,可以自我感知并上线,也可以手动配置。

  • haproxy ip-hash

    整型的Hash算法使用的是Thomas Wang’s 32 Bit / 64 Bit Mix Function ,这是一种基于位移运算的散列方法。基于移位的散列是使用Key值进行移位操作。通常是结合左移和右移。每个移位过程的结果进行累加,最后移位的结果作为最终结果。这种方法的好处是避免了乘法运算,从

如果实现将请求打散到各个节点,负载均衡器需要遵循一定得规则,规则主要是一下几种:

  • 轮询: 将请求轮流到发送到后端的机器,不关系节点的实际连接数和负载能力。
  • 加权轮询: 对轮询的优化,考虑每个节点的性能,配置高的机器分配较高的权重,配置低的机器分配较低的权重,并将请求按照权重分配到后端节点。
  • 随机法: 通过随机算法,在众多节点中随机挑选一个进行请求。随着客户端调用服务端的次数增多,其实际效果越接近轮询。
  • 加权随机法: 对随机的优化,根据机器性能分配权重,按照权重访问后端节点。
  • 源地址哈希法: 根据客户端的IP地址,通过hash函数获取一个数值,用这个数值对后端节点数进行取模,这样在后端节点数保持不变的情况下,同一个客户端访问的 后端节点也是同一个。
  • 最小连接数: 根据后端节点的连接情况,动态选举一个连接积压最小的节点进行访问,尽可能的提高节点的利用率。

HAProxy实现负载均衡

HAProxy 简介
  1. HAProxy 是一款提供高可用性,负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,它是免费,快速并且可靠的一种解决方案。 HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支持数以万计的 并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。
  2. HAProxy 实现了一种事件驱动,单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 ,系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户端(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。
  3. HAProxy 支持连接拒绝 : 因为维护一个连接的打开的开销是很低的,有时我们很需要限制攻击蠕虫(attack bots),也就是说限制它们的连接打开从而限制它们的危害。 这个已经为一个陷于小型DDoS攻击的网站开发了而且已经拯救了很多站点,这个优点也是其它负载均衡器没有的。
  4. HAProxy 支持全透明代理(已具备硬件防火墙的典型特点): 可以用客户端IP地址或者任何其他地址来连接后端服务器,这个特性仅在 Linux 2.4/2.6内核打了cttproxy补丁后才可以使用,这个特性也使得为某特殊服务器处理部分流量同时又不修改服务器的地址成为可能。
四层负载均衡与七层负载均衡
  • 四层负载均衡

    以常见的 TCP 应用为例,负载均衡器在接收到第一个来自客户端的 SYN 请求时,会通过设定的负载均衡算法选择一个最佳的后端服务器,同时将报文中目标 IP 地址修改为后端服务器 IP,然后直接转发给该后端服务器,这样一个负载均衡请求就完成了。从这个过程来看,一个 TCP 连接是客户端和服务器直接建立的,而负载均衡器只不过完成了一个类似路由器的转发动作。在某些负载均衡策略中,为保证后端服务器返回的报文可以正确传递给负载均衡器,在转发报文的同时可能还会对报文原来的源地址进行修改。

  • 七层负载均衡

    这里仍以常见的 TCP 应用为例,由于负载均衡器要获取到报文的内容,因此只能先代替后端服务器和客户端建立连接,接着,才能收到客户端发送过来的报文内容,然后再根据该报文中特定字段加上负载均衡器中设置的负载均衡算法来决定最终选择的内部服务器。纵观整个过程,七层负载均衡器在这种情况下类似于一个代理服务器。

    对比四层负载均衡和七层负载均衡运行的整个过程,可以看出,在七层负载均衡模式下, 负载均衡器与客户端及后端的服务器会分别建立一次 TCP 连接,而在四层负载均衡模式下, 仅建立一次 TCP 连接。由此可知,七层负载均衡对负载均衡设备的要求更高,而七层负载均衡的处理能力也必然低于四层模式的负载均衡。

HAProxy配置详解

HAProxy 配置文件根据功能和用途,主要有 5 个部分组成,但有些部分并不是必须的, 可以根据需要选择相应的部分进行配置。

  • global 部分

    用来设定全局配置参数,属于进程级的配置,通常和操作系统配置有关。

    • log: 全局的日志配置,local0 是日志设备,info 表示日志级别。其中日志级别有err,warning,info,debug 四种可选。这个配置表示使用 127.0.0.1 上的 rsyslog 服务中的local0 日志设备,记录日志等级为info。
    • maxconn: 设定每个 haproxy 进程可接受的最大并发连接数,此选项等同于 Linux命令行选项”ulimit -n”。
    • user/ group: 设置运行 haproxy 进程的用户和组,也可使用用户和组的 uid 和gid 值来替代。
    • daemon: 设置 HAProxy 进程进入后台运行。这是推荐的运行模式。
    • nbproc: 设置 HAProxy 启动时可创建的进程数,此参数要求将HAProxy 运行模式设置为”daemon”,默认只启动一个进程。根据使用经验,该值的设置应该小于服务器的 CPU 核数。创建多个进程,能够减少每个进程的任务队列,但是过多的进程可能会导致进程的崩溃。
    • pidfile: 指定 HAProxy 进程的 pid 文件。启动进程的用户必须有访问此文件的权限。
  • defaults 部分

    默认参数的配置部分。在此部分设置的参数值,默认会自动被引用到下面的 frontend,backend 和 listen 部分中,因此,如果某些参数属于公用的配置,只需在 defaults 部分添加一次即可。而如果在 frontend,backend 和 listen 部分中也配置了与 defaults 部分一样的参数,那么defaults 部分参数对应的值自动被覆盖。

    • mode: 设置 HAProxy 实例默认的运行模式,有 tcp,http,health 三个可选值。

      • tcp 模式

        在此模式下,客户端和服务器端之间将建立一个全双工的连接,不会对七层报文做任何类型的检查,默认为 tcp 模式,经常用于 SSL,SSH,SMTP 等应用。

      • http 模式

        在此模式下,客户端请求在转发至后端服务器之前将会被深度分析,所有不与 RFC 格式兼容的请求都会被拒绝。

      • health 模式

        目前此模式基本已经废弃,不在多说。

    • retries: 设置连接后端服务器的失败重试次数,连接失败的次数如果超过这里设置的值,HAProxy 会将对应的后端服务器标记为不可用。此参数也可在后面部分进行设置。

    • timeout connect: 设置成功连接到一台服务器的最长等待时间,默认单位是毫秒,但也可以使用其他的时间单位后缀。

    • timeout client: 设置连接客户端发送数据时最长等待时间,默认单位是毫秒,也可以使用其他的时间单位后缀。

    • timeout server: 设置服务器端回应客户度数据发送的最长等待时间,默认单位是毫秒,也可以使用其他的时间单位后缀。

    • timeout check: 设置对后端服务器的检测超时时间,默认单位是毫秒,也可以使用其他的时间单位后缀。

  • frontend 部分

    此部分用于设置接收用户请求的前端虚拟节点。frontend 是在 HAProxy1.3 版本之后才引入的一个组件,同时引入的还有 backend 组件。通过引入这些组件,在很大程度上简化了 HAProxy 配置文件的复杂性。frontend 可以根据 ACL 规则直接指定要使用的后端。

    • bind: 此选项只能在 frontend 和 listen 部分进行定义,用于定义一个或几个监听的套接字。bind 的使用格式为 :bind [<address>:<port_range>] interface <interface> 其中,address 为可选选项,其可以为主机名或IP 地址,如果将其设置为”*“或”0.0.0.0”,将监听当前系统的所有 IPv4 地址。port_range 可以是一个特定的 TCP 端口,也可是一个端口范围,小于 1024 的端口需要有特定权限的用户才能使用。interface 为可选选项,用来指定网络接口的名称,只能在 Linux 系统上使用。
    • option httplog: 在默认情况下,haproxy 日志是不记录 HTTP 请求的,这样很不方便 HAProxy 问题的排查与监控。通过此选项可以启用日志记录 HTTP 请求。
    • option forwardfor: 如果后端服务器需要获得客户端的真实 IP,就需要配置此参数。由于 HAProxy 工作于反向代理模式,因此发往后端真实服务器的请求中的客户端 IP 均为 HAProxy 主机的 IP,而非真正访问客户端的地址,这就导致真实服务器端无法记录客户端真正请求来源的 IP,而”X-Forwarded-For”则可用于解决此问题。通过使用”forwardfor”选项,HAProxy 就可以向每个发往后端真实服务器的请求添加”X-Forwarded-For”记录,这样后端真实服务器日志可以通过”X-Forwarded-For”信息来记录客户端来源 IP。
    • option httpclose: 此选项表示在客户端和服务器端完成一次连接请求后,HAProxy 将主动关闭此 TCP 连接。这是对性能非常有帮助的一个参数。
    • log global: 表示使用全局的日志配置,这里的” global”表示引用在HAProxy 配置文件 global 部分中定义的 log 选项配置格式。
    • default_backend: #指定默认的后端服务器池,也就是指定一组后端真实服务器,而这些真实服务器组将在 backend 段进行定义。这里的htmpool 就是一个后端服务器组。
  • backend 部分

    此部分用于设置集群后端服务集群的配置,也就是用来添加一组真实服务器,以处理前端用户的请求。添加的真实服务器类似于 LVS 中的real server 节点。

    • option redispatch: 此参数用于 cookie 保持的环境中。在默认情况下,HAProxy会将其请求的后端服务器的 serverID 插入到 cookie 中,以保证会话的 SESSION 持久性。而如果后端的服务器出现故障,客户端的 cookie 是不会刷新的,这就出现了问题。此时,如果设置此参数,就会将客户的请求强制定向到另外一个健康的后端服务器上,以保证服务的正常。

    • option abortonclose: 如果设置了此参数,可以在服务器负载很高的情况下, 自动结束掉当前队列中处理时间比较长的链接。

    • balance: 此关键字用来定义负载均衡算法。目前 HAProxy 支持多种负载均衡算法,常用的有如下几种:

      算法描述
      roundrobin是基于权重进行轮询调度的算法,在服务器的性能分布比较均匀的时候,这是一种最公平的,最合理的算法。此算法经常使用。
      static-rr也是基于权重进行轮询调度的算法,不过此算法为静态方法,在运行时调整其服务器严重不会生效。
      source是基于请求源IP的算法。此算法先对请求的源IP 进行hash 运算,然后将结果与后端服务器的权重总数相除后转发至某个匹配的后端服务器。这种方式可以使同一客户端 IP的请求始终被转发到某特定的后端服务器。
      leastconn此算法会将新的连接请求转发到具有最少连接数目的后端服务器。在会话时间较长的场景中推荐使用此算法,例如数据库复杂均衡等。此算法不适合会话时间比较短的环境中,例如基于 HTTP 的应用。
      url此算法会对部分或整个 URL 进行 hash 运算,再经过与服务器的总权重相除,最后转发到某台匹配的后端服务器上。
      url_param此算法会根据 URL 路径中的参数进行转发,这样可保证在后端真实服务器数量不变时,同一个用户的请求始终分发到同一机器上。
      hdr(<name>):此算法根据 http 头进行转发,如果指定的 http 头名称不存在,则使用 roundrobin 算法进行策略转发。
    • cookie: 表示允许向 cookie 插入 SERVERID,每台服务器的 SERVERID 可在下面的 server 关键字中使用 cookie 关键字定义。

    • option httpchk: 此选项表示启用 HTTP 的服务状态检测功能。HAProxy 作为一款专业的负载均衡器,它支持对 backend 部分指定的后端服务节点的健康检查,以保证在后端 backend 中某个节点不能服务时,把从 frotend 端进来的客户端请求分配至 backend 中其他健康节点上,从而保证整体服务的可用性。“option httpchk”的用法如下:

      参数描述
      method表示 http 请求的方式,常用的有 OPTIONS,GET,HEAD 几种方式, 一般的健康检查可以使用 HEAD 方式进行,而不是采用 GET 方式,这是因为 HEAD 方式没有数据返回,仅检查 response 的 HEAD 是不是 200 状态。因此相对 GET 方式,HEAD 方式更快,更简单。
      uri表示要检测的 URL 地址,通过执行此 URL ,可以获取后端服务器的运行状态。在正常情况下将返回状态码 200,返回其他状态码均为异常状态。
      version指定心跳检测时的 http 版本号。
    • server: 这个关键字用来定义多个后端真实服务器,不能用于 defaults 和frontend部分。使用格式为: server <name> <address>[:port] [param*] 其中,每个参数含义如下:

      • check: 表示启用对此后端服务器执行健康状态检查。

      • inter: 设置健康状态检查的时间间隔,单位为毫秒。

      • rise: 设置从故障状态转换至正常状态需要成功检查的次数,例如。“rise 2”表示 2 次检查正确就认为此服务器可用。

      • fall: 设置后端服务器从正常状态转换为不可用状态需要检查的次数,例如,“fall 3”表示 3 次检查失败就认为此服务器不可用。

      • cookie: 为指定的后端服务器设定 cookie 值,此处指定的值将在请求入站时被检查,第一次为此值挑选的后端服务器将在后

        name为后端真实服务器指定一个内部名称,随便定义一个即可。
        address后端真实服务器的 IP 地址或主机名。
        port

        指定连接请求发往真实服务器时的目标端口。在未设定时,将使用客户端请求时的同一端口。

        [params*]为后端服务器设定的一系列参数,可用参数较多,仅介绍常用的参数:
        check表示启用对此后端服务器执行健康状态检查。
        inter设置健康检查的时间间隔,单位为毫秒。
        rise

        设置从故障状态转化为正常状态需要成功检查的次数,例如: “rise: 2”表示两次检查正确就认为此服务可用。

        fall

        设置后端服务器从正常状态转换为不可用状态需要检查的次数,例如: “fall: 3”表示三次检查失败就认为此服务器不可用。

        cookie

        为指定的后端服务器设置 cookie 值,此处指定的值将在请求入站时被检查,第一次为此值挑选的后端服务器将在后续的请求中一直被选中,其目的在于实现持久连接的功能。上面的”cookie server1” 表示 web1 的 serverid 为 server1。同理,“cookie server2” 表示 web2 的 serverid 为 server2。

        weight设置后端真实服务器的权重,默认为 1,最大值为 256。设置为 0 表示不参与负载均衡。
        backup设置后端真实服务器的备份服务器,仅仅在后端真实服务器均不可用时才会启用。
  • listen 部分

    此部分是 frontend 部分和 backend 部分的结合体。在 HAProxy1.3 版本之前,HAProxy 的所有配置选项都在这个部分中设置。为了保持兼容性,HAProxy 新的版本仍然保留了 listen 组件的配置方式。目前在 HAProxy 中,两种配置方式任选其一即可。

    这个部分通过listen 关键字定义了一个名为”admin_stats”的实例,其实就是定义了一个 HAProxy 的监控页面,每个选项的含义如下:

    • stats refresh: 设置 HAProxy 监控统计页面自动刷新的时间。
    • stats uri: 设置 HAProxy 监控统计页面的URL 路径,可随意指定。例如,指定”stats uri /haproxy-status”,就可以过 <http://IP:9188/haproxy-status> 查看。
    • stats realm: 设置登录 HAProxy 统计页面时密码框上的文本提示信息。
    • stats auth: 设置登录 HAProxy 统计页面的用户名和密码。用户名和密码通过冒号分割。可为监控页面设置多个用户名和密码,每行一个。
    • stats hide-version: 用来隐藏统计页面上 HAProxy 的版本信息。
    • stats admin if TRUE: 通过设置此选项,可以在监控页面上手工启用或禁用后端真实服务器,仅在 haproxy1.4.9 以后版本有效。
一份完整的配置
   global
    log 127.0.0.1 local0 info
    maxconn 4096
    user nobody
    group nobody
    daemon
    nbproc 1
    pidfile /usr/local/haproxy/logs/haproxy.pid
defaults
    mode http
    retries 3
    timeout connect 10s
    timeout client 20s
    timeout server 30s
    timeout check 5s
frontend www
    bind *:80
    mode  http
    option  httplog
    option  forwardfor
    option  httpclose
    log global
    #acl host_www hdr_dom(host) -i www.zb.com
    #acl host_img hdr_dom(host) -i img.zb.com

    #use_backend htmpool if host_www
    #use_backend imgpool if host_img
    default_backend htmpool
backend htmpool
    mode http
    option redispatch
    option abortonclose
    balance static-rr
    cookie SERVERID
    option httpchk GET /index.jsp
    server 237server 192.168.81.237:8080 cookie server1 weight 6 check inter 2000 rise 2 fall 3
    server iivey234 192.168.81.234:8080 cookie server2 weight 3 check inter 2000 rise 2 fall 3
backend imgpool
    mode http
    option redispatch
    option abortonclose
    balance static-rr
    cookie SERVERID
    option httpchk GET /index.jsp
    server host236 192.168.81.236:8080 cookie server1 weight 6 check inter 2000 rise 2 fall 3

listen admin_stats
    bind 0.0.0.0:9188
    mode http
    log 127.0.0.1 local0 err
    stats refresh 30s
    stats uri /haproxy-status
    stats realm welcome login\ Haproxy
    stats auth admin:admin123
    stats hide-version
    stats admin if TRUE

安装HAProxy

下载HAProxy相关版本,这里下载haproxy-1.8.12.tar.gz,之后准备安装。

安装之前查看内核版本,根据内核版本选择编译参数

   uname -r

解压HAProxy,并安装:

   tar xf haproxy-1.8.12.tar.gz
cd haproxy-1.7.5
make TARGET=linux2628 PREFIX=/usr/local/haproxy
make install PREFIX=/usr/local/haproxy

安装成功之后,查看版本

   /usr/local/haproxy/sbin/haproxy -v

配置HAProxy

配置启动文件,复制haproxy文件到/usr/sbin下 ,复制haproxy脚本,到/etc/init.d下

   cp /usr/local/haproxy/sbin/haproxy /usr/sbin/
cp ./examples/haproxy.init /etc/init.d/haproxy
chmod 755 /etc/init.d/haproxy

创建系统账号

   useradd -r haproxy

创建配置文件

   mkdir /etc/haproxy
vi /etc/haproxy/haproxy.cfg

更改配置文件

   #全局配置
global
    #设置日志
    log 127.0.0.1 local0 info
    #当前工作目录
    chroot /usr/local/haproxy
    #用户与用户组
    user haproxy
    group haproxy
    #运行进程ID
    uid 99
    gid 99
    #守护进程启动
    daemon
    #最大连接数
    maxconn 4096

#默认配置
defaults
    #应用全局的日志配置
    log global
    #默认的模式mode {tcp|http|health}
    #TCP是4层,HTTP是7层,health只返回OK
    mode tcp
    #日志类别tcplog
    option tcplog
    #不记录健康检查日志信息
    option dontlognull
    #3次失败则认为服务不可用
    retries 3
    #每个进程可用的最大连接数
    maxconn 2000
    #连接超时
    timeout connect 5s
    #客户端超时
    timeout client 120s
    #服务端超时
    timeout server 120s

#绑定配置
listen rabbitmq_cluster
        bind 0.0.0.0:5671
        #配置TCP模式
        mode tcp
        #简单的轮询
        balance roundrobin
        #RabbitMQ集群节点配置
        server rmq_node1 10.110.8.34:5672 check inter 5000 rise 2 fall 3 weight 1
        server rmq_node2 10.110.8.38:5672 check inter 5000 rise 2 fall 3 weight 1

#haproxy监控页面地址
listen monitor
        bind 0.0.0.0:8100
        mode http
        option httplog
        stats enable
        stats uri /stats
        stats refresh 5s

启动haproxy

   service haproxy start

Haproxy 解决集群 session 共享问题,二种方法保持客户端 session 一致。

  • 用户 IP 识别

    Haproxy 将用户 IP 经过 hash 计算后 指定到固定的真实服务器上(类似于 nginx 的 IP hash 指令)。

    配置指令: balance source。

       backend htmpool
            mode http
            option redispatch
            option abortonclose
            balance source
            cookie SERVERID
            option httpchk GET /index.jsp
            server 237server 192.168.81.237:8080 cookie server1 weight 6 check inter 2000 rise 2 fall 3
            server iivey234 192.168.81.234:8080 cookie server2 weight 3 check inter 2000 rise 2 fall 3
  • cookie 识别

    haproxy 将WEB 服务端发送给客户端的 cookie 中插入(或添加加前缀)haproxy 定义的后端的服务器COOKIE ID。

    配置指令例举 cookie SESSION_COOKIE insert indirect nocache。

       backend htmpool
            mode http
            option    redispatch
            option    abortonclose
            balance  static-rr
            cookie    SERVERID   #cookie参数
            option    httpchk GET /index.jsp
            server    237server 192.168.81.237:8080 cookie server1 weight 6 check inter 2000 rise 2 fall 3   #server里面的cookie参数
            server    iivey234 192.168.81.234:8080 cookie server2 weight 3 check inter 2000 rise 2 fall 3   #server里面的cookie参数