了解 Infinispan 和 JGroups 中的故障检测

本指南介绍 JGroups 如何检测失败的实例以及如何配置它。它解释了可用于自定义的配置属性及其权衡,以实现所需的检测率。

JGroups 的使用场景以及故障检测的重要性

Keycloak 使用 Infinispan 在分布式缓存中存储会话相关信息,这些缓存存在于 Keycloak 内部以及 Infinispan 的外部实例中。Keycloak 实例以及外部 Infinispan 实例形成集群,集群节点之间的通信由 JGroups 处理。多站点设置中不同外部 Infinispan 集群之间的通信也由 JGroups 处理。

集群中的每个节点都提供会话数据,这对于 Keycloak 服务请求非常重要。无响应的节点会导致请求被阻塞,并可能导致调用方超时和错误。如果节点发生故障,则需要将请求重定向到剩余节点,并且其数据需要从集群中的备份节点重新分发。

断开的 TCP 连接导致通信故障

断开的 TCP 连接会对集群性能产生影响,因此有必要检测到它,以便可以关闭连接并建立新连接。请注意,TCP 是一种阻塞/同步协议,如果连接无法正常工作,线程将在 Socket::write 操作中被阻塞。内核参数 tcp_retries2 配置内核强制关闭连接之前的失败重试次数。引用内核文档

此值会影响活动 TCP 连接的超时时间,当 RTO 重传仍然未被确认时。默认值 15 会产生 924.6 秒的假设超时时间,并且是有效超时时间的下限。

在此期间,集群性能会下降。不希望在镜像或容器中更改内核参数,但 JGroups 具有可以配置的属性,以检测并强制关闭这些断开的连接。

对于跨站点网络通信,它在 Kubernetes 中使用 TUNNEL 协议,Infinispan Pod 流量通过 JGroups Gossip Router Pod 转发。超时在 Infinispan CR YAML 文件中配置,如下所示

infinispan.yaml
  service:
    type: DataGrid
    sites:
      local:
        discovery:
          heartbeats:
            interval: 10000 (1)
            timeout: 30000 (2)
1 每隔 interval 毫秒向 Gossip 路由器发送一次心跳。
2 在没有收到消息或心跳的最大时间(毫秒),超过此时间后,与 Gossip 路由器的连接将被关闭。
以上值是 Infinispan Operator 配置的默认值。

减少间隔和超时将提高 JGroups 检测断开连接的速度。但是,由于心跳频率的增加,它也会增加网络使用量。请注意,两个集群中的所有 Infinispan Pod 都将向所有配置的 Gossip Router Pod 发送心跳。

默认值应适用于 99% 的用例。

对于 Infinispan 或 Keycloak 集群中的集群内通信,断开的 TCP 连接由故障检测机制检测和关闭。有关更多信息,请参见 检测无响应节点 部分。

检测无响应节点

集群中的节点可能会发生故障,一旦发生故障,就需要重定向流量和数据。JGroups 中的故障检测由 FD_ALL3 协议提供,该协议存在于 Infinispan 附带的默认 JGroups 配置中。

FD_ALL3 是一种简单的心跳协议,其中每个成员定期多播一个心跳。当收到来自另一个 Pod 的数据或心跳时,该 Pod 被认为是存活的,否则该 Pod 被认为是可疑的,并可能从组视图中删除。

心跳间隔和超时配置如下所示

<infinispan>
   <jgroups>
      <stack name="my-tcp" extends="tcp"> (1)
         <FD_ALL3 stack.combine="COMBINE"
               interval="8000" (2)
               timeout="40000"/> (3)
      </stack>
   </jgroups>

   <cache-container>
      <transport stack="my-tcp"/> (4)
   </cache-container>
</infinispan>
1 自定义堆栈名称。
2 向集群发送心跳的间隔,以毫秒为单位。
3 超时时间,以毫秒为单位,如果在超时时间内既没有收到心跳也没有收到数据,则节点将被怀疑。
4 JGroups 要使用的堆栈名称。
上面的示例显示了默认值。

对于 M 个 Pod 的集群,每个 Pod 将在每个间隔生成 M-1 个心跳消息。 减少 intervaltimeout 可以提高崩溃 Pod 的检测速度,但会对网络产生更大的影响,因为会传输额外的消息。

如果启用了跨站点,FD_ALL3 心跳也会流向远程集群。跨站点通道有其自己的配置,可以独立于集群内通信进行配置。

默认值应适用于 99% 的用例。

在 Pod 被怀疑后,将触发不包含崩溃成员的新组视图更改。此事件将关闭与该 Pod 的任何断开的 TCP 连接,并解除可能被阻塞的线程。

读写操作的超时

Infinispan 具有三个可配置的超时,它们会影响 Keycloak 和 Infinispan 集群中的读或写操作。这些是锁超时、远程超时和跨站点远程超时。

如果超时彼此对齐,并且与上述故障检测超时和间隔对齐,它们允许在连接或节点故障后至少重试一次。这允许向调用方提供有效的响应并隐藏发生的错误。

锁超时是操作等待锁释放的时间。这应该是所有超时中最小的。

远程超时是 Infinispan Pod 等待来自同一集群中其他 Pod 的回复的时间。它会影响写入(当复制数据时)和读取(当 Pod 从其他 Pod 获取数据时,如果本地不存在副本)。

最后,对于多站点部署,跨站点远程超时是等待另一个集群确认更新的时间。

更改这些值可能会直接影响响应时间。尽管在正常操作期间 Infinispan 在几毫秒内回复,但如果工作负载较高、Pod 崩溃或发生网络中断,则可能会达到这些超时。

减少超时会缩短响应时间;Infinispan 会更快放弃,但会增加报告给用户的错误率。增加这些值可能会减少错误数量,因为它为 Infinispan 提供了更多时间来处理工作负载,但会增加最终用户观察到的响应时间。

要更新使用 Infinispan Operator 部署的 Infinispan 集群中的任何超时值,请更新您的 CacheCR,如下所示

apiVersion: infinispan.org/v2alpha1
kind: Cache
metadata:
  name: sessions
spec:
  clusterName: infinispan
  name: sessions
  template: |-
    distributedCache:
      mode: "SYNC"
      owners: 2
      statistics: "true"
      remoteTimeout: "15000" (1)
      locking:
        acquireTimeout: "10000" (2)
      backups:
        cluster-b:
          backup:
            strategy: "SYNC"
            timeout: "15000"  (3)
1 超时值,以毫秒为单位,等待来自本地集群中其他 Pod 的回复。
2 超时值,以毫秒为单位,等待锁定的锁被释放。
3 超时值,以毫秒为单位,等待来自远程集群的回复。
该示例显示了默认值。仅添加您要更新的超时的行。

对于在 Keycloak 服务器中运行的 Infinispan 集群,需要自定义的 Infinispan XML 文件。更改缓存配置,如下所示,并添加您要更新的属性(以粗体显示)

<distributed-cache name="sessions" owners="2" statistics="true" remote-timeout="15000"> (1)
    <locking acquire-timeout="10000"/> (2)
    <backups>
        <backup site="cluster-b" timeout="15000"/> (3)
    </backups>
</distributed-cache>
1 超时值,以毫秒为单位,等待来自本地集群中其他 Pod 的回复。
2 超时值,以毫秒为单位,等待锁定的锁被释放。
3 超时值,以毫秒为单位,等待来自远程集群的回复。
该示例显示了默认值。