<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-infinispan</artifactId>
<version>5.0.1</version>
</dependency>
Infinispan 集群管理器
这是一个为 Vert.x 使用 Infinispan 的集群管理器实现。
这个实现被打包在
在 Vert.x 中,集群管理器用于多种功能,包括
-
集群中 Vert.x 节点的发现和组成员管理
-
维护集群范围的主题订阅者列表(以便我们知道哪些节点对哪些事件总线地址感兴趣)
-
分布式 Map 支持
-
分布式锁
-
分布式计数器
集群管理器**不**处理事件总线节点间的传输,这由 Vert.x 直接通过 TCP 连接完成。
使用此集群管理器
如果您从命令行使用 Vert.x,则此集群管理器对应的 JAR 包(它将被命名为 vertx-infinispan-5.0.1.jar
)应位于 Vert.x 安装的 lib
目录中。
如果您希望在 Vert.x Maven 或 Gradle 项目中使用此集群管理器进行集群,只需在您的项目中添加对工件 io.vertx:vertx-infinispan:5.0.1
的依赖即可。
如果 JAR 包如上所示位于您的类路径中,Vert.x 将自动检测并使用它作为集群管理器。请确保您的类路径中没有其他集群管理器,否则 Vert.x 可能会选择错误的管理器。
如果您嵌入 Vert.x,也可以在创建 Vert.x 实例时通过在选项中指定来以编程方式指定集群管理器,例如
ClusterManager mgr = new InfinispanClusterManager();
Vertx.builder()
.withClusterManager(mgr)
.buildClustered().onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
配置此集群管理器
默认的集群管理器配置可以通过 infinispan.xml
和/或 jgroups.xml
文件进行修改。前者配置数据网格,后者配置组管理和成员发现。
您可以将其中一个或两个文件放在您的类路径中。如果您想将自定义文件嵌入到 fat jar 中,它必须位于 fat jar 的根目录。如果它是外部文件,则必须将包含该文件的**目录**添加到类路径中。例如,如果您正在使用 Vert.x 的 launcher 类,可以通过以下方式增强类路径:
# If infinispan.xml and/or jgroups.xml files are in the current directory:
java -jar my-app.jar -cp . -cluster
# If infinispan.xml and/or jgroups.xml files are in the conf directory:
java -jar my-app.jar -cp conf -cluster
覆盖配置的另一种方法是通过系统属性提供文件位置:vertx.infinispan.config
和/或 vertx.jgroups.config
。
# Use a cluster configuration located in an external file
java -Dvertx.infinispan.config=./config/my-infinispan.xml -jar ... -cluster
# Or use a custom configuration from the classpath
java -Dvertx.infinispan.config=my/package/config/my-infinispan.xml -jar ... -cluster
集群管理器将首先在类路径中搜索文件,然后回退到文件系统。
系统属性(如果存在)将覆盖类路径中的任何 infinispan.xml
或 jgroups.xml
。
这些 XML 文件是 Infinispan 和 JGroups 的配置文件,在 Infinispan 和 JGroups 网站的文档中有详细描述。
如果 jgroups.xml 文件在类路径中,或者您设置了 vertx.jgroups.config 系统属性,它将覆盖 Infinispan 配置文件中定义的任何 JGroups stack-file 路径。 |
默认的 JGroups 配置使用多播进行发现,使用 TCP 进行组管理。请确保您的网络上启用了多播以使其正常工作。
有关如何不同地配置传输或使用不同传输的完整文档,请查阅 Infinispan / JGroups 文档。
使用现有的 Infinispan 缓存管理器
您可以在集群管理器中传递一个现有的 DefaultCacheManager
以重用现有的缓存管理器
ClusterManager mgr = new InfinispanClusterManager(cacheManager);
Vertx.builder()
.withClusterManager(mgr)
.buildClustered().onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
在这种情况下,Vert.x 不是缓存管理器的所有者,因此在关闭时不会将其关闭。
请注意,自定义 Infinispan 实例需要配置为
<cache-container default-cache="distributed-cache">
<distributed-cache name="distributed-cache"/>
<replicated-cache name="__vertx.subs"/>
<replicated-cache name="__vertx.haInfo"/>
<replicated-cache name="__vertx.nodeInfo"/>
<distributed-cache-configuration name="__vertx.distributed.cache.configuration"/>
</cache-container>
打包可执行的 uber JAR
Infinispan 使用 Java 的 ServiceLoader
机制在运行时发现一些类的实现。在创建可执行的 uber JAR(也称为 "fat" JAR)时,您必须配置您的构建工具以合并服务描述符文件。
如果您使用 Maven 和 Maven Shade Plugin,插件配置应如下所示:
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<!-- ... -->
</transformers>
<!-- ... -->
</configuration>
如果您使用 Gradle 和 Gradle Shadow Plugin
shadowJar {
mergeServiceFiles()
}
此外,Infinispan 依赖于 多版本 JAR 文件(Multi-Release JAR Files)来允许某些类的多个、特定于 Java 版本的版本。因此,可执行 uber JAR 的清单文件必须包含以下条目
Multi-Release: true
为 Kubernetes 配置
在 Kubernetes 上,JGroups 可以配置为使用 Kubernetes API (KUBE_PING
) 或 DNS (DNS_PING
) 进行发现。本文档中,我们将使用 DNS 发现。
首先,通过系统属性强制 JVM 使用 IPv4。
-Djava.net.preferIPv4Stack=true
然后,将 vertx.jgroups.config
系统属性设置为 default-configs/default-jgroups-kubernetes.xml
。此 JGroups 栈文件位于 infinispan-core
JAR 中,并已为 Kubernetes 预配置。
-Dvertx.jgroups.config=default-configs/default-jgroups-kubernetes.xml
另外,设置 JGroups DNS 查询以查找成员。
-Djgroups.dns.query=MY-SERVICE-DNS-NAME
MY-SERVICE-DNS-NAME
的值必须是一个**无头(headless)** Kubernetes 服务名称,JGroups 将使用它来标识所有集群成员。无头服务可以通过以下方式创建:
apiVersion: v1
kind: Service
metadata:
name: clustered-app
spec:
selector:
cluster: clustered-app (2)
ports:
- name: jgroups
port: 7800 (1)
protocol: TCP
publishNotReadyAddresses: true (3)
clusterIP: None
1 | JGroups TCP 端口 |
2 | 由 cluster=clustered-app 标签选择的集群成员 |
3 | 设置为 true,以便在不干扰您的就绪探测逻辑的情况下发现成员 |
最后,将 cluster=clustered-app
标签应用于所有应属于集群的部署
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
labels:
cluster: clustered-app
滚动更新
在滚动更新期间,Infinispan 团队建议逐个替换 Pod。
为此,我们必须配置 Kubernetes 以
-
一次启动不超过一个新 Pod
-
过程中禁止出现一个以上不可用的 Pod
spec:
strategy:
type: Rolling
rollingParams:
updatePeriodSeconds: 10
intervalSeconds: 20
timeoutSeconds: 600
maxUnavailable: 1 (1)
maxSurge: 1 (2)
1 | 更新过程中可以不可用的 Pod 的最大数量 |
2 | 可以创建的 Pod 的最大数量,超过所需的 Pod 数量 |
此外,Pod 就绪探测必须考虑集群健康状况。有关如何使用 Vert.x 健康检查实现就绪探测的详细信息,请参阅集群管理部分。
为 Docker Compose 配置
确保使用这些系统属性启动 Java 虚拟机
-Djava.net.preferIPv4Stack=true -Djgroups.bind.address=NON_LOOPBACK
这将使 JGroups 选择 Docker 创建的虚拟专用网络的接口。
集群故障排除
如果默认的多播发现配置不起作用,这里有一些常见原因
机器上未启用多播。
在 OSX 机器上,多播默认被禁用的情况非常常见。请搜索相关信息以了解如何启用它。
使用错误的网络接口
如果您的机器上有多个网络接口(如果您正在运行 VPN 软件,也可能出现这种情况),那么 JGroups 可能会使用错误的接口。
要告诉 JGroups 使用特定接口,您可以在配置的 bind_addr
元素中提供该接口的 IP 地址。例如
<TCP bind_addr="192.168.1.20"
...
/>
<MPING bind_addr="192.168.1.20"
...
/>
另外,如果您想坚持使用捆绑的 jgroups.xml
文件,可以设置 jgroups.bind.address
系统属性。
-Djgroups.bind.address=192.168.1.20
当 Vert.x 运行在集群模式下时,您还应该确保 Vert.x 知道正确的接口。在命令行运行时,通过指定 cluster-host
选项来完成此操作
vertx run myverticle.js -cluster -cluster-host your-ip-address
其中 your-ip-address
是您在 JGroups 配置中指定的相同 IP 地址。
如果以编程方式使用 Vert.x,您可以使用 setHost
来指定。
使用 VPN
这是上述情况的一种变体。VPN 软件通常通过创建虚拟网络接口来工作,而这种接口通常不支持多播。如果您正在运行 VPN 并且没有在 JGroups 配置和 Vert.x 中指定要使用的正确接口,那么可能会选择 VPN 接口而不是正确的接口。
因此,如果您正在运行 VPN,您可能需要按照上一节所述,配置 JGroups 和 Vert.x 以使用正确的接口。
当多播不可用时
在某些情况下,您可能无法使用多播发现,因为它在您的环境中可能不可用。在这种情况下,您应该配置其他协议,例如使用 TCPPING
来使用 TCP 套接字,或者在 Amazon EC2 上运行时使用 S3_PING
。
有关可用 JGroups 发现协议以及如何配置它们的更多信息,请查阅 JGroups 文档。
IPv6 问题
如果您在配置 IPv6 主机时遇到问题,请使用 java.net.preferIPv4Stack
系统属性强制使用 IPv4。
-Djava.net.preferIPv4Stack=true
启用日志记录
在排查集群问题时,通常获取 Infinispan 和 JGroups 的一些日志输出以查看是否正确形成集群会很有用。您可以通过在类路径中添加一个名为 vertx-default-jul-logging.properties
的文件来完成此操作(当使用默认的 JUL 日志记录时)。这是一个标准的 java.util.logging (JUL) 配置文件。在其中设置
org.infinispan.level=INFO
org.jgroups.level=INFO
并且
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.FileHandler.level=INFO
Infinispan 日志记录
Infinispan 依赖于 JBoss logging。JBoss Logging 是一个日志桥接,提供与众多日志框架的集成。
将您选择的日志 JAR 添加到类路径中,JBoss Logging 将自动识别它们。
如果您的类路径中有多个日志后端,您可以使用 org.jboss.logging.provider
系统属性强制选择。例如
-Dorg.jboss.logging.provider=log4j2
有关更多详细信息,请参阅此 JBoss Logging 指南。
JGroups 日志记录
JGroups 默认使用 JDK 日志记录。如果类路径中找到相应的 JAR,则支持 log4j 和 log4j2。
如果您需要更多详细信息或想实现自己的日志后端,请参阅 JGroups 日志记录文档。
SharedData 扩展
AsyncMap 内容流
InfinispanAsyncMap
API 允许以流的形式检索键、值和条目。如果您需要遍历大型映射的内容进行批量处理,这会很有用。
InfinispanAsyncMap<K, V> infinispanAsyncMap = InfinispanAsyncMap.unwrap(asyncMap);
ReadStream<K> keyStream = infinispanAsyncMap.keyStream();
ReadStream<V> valueStream = infinispanAsyncMap.valueStream();
ReadStream<Map.Entry<K, V>> entryReadStream = infinispanAsyncMap.entryStream();
集群管理
Infinispan 集群管理器通过将 Vert.x 节点转换为 Infinispan 集群的成员来工作。因此,Vert.x 集群管理器管理应遵循 Infinispan 管理指南。
首先,让我们退一步,介绍一下再平衡(rebalancing)和脑裂(split-brain syndrome)。
再平衡
每个 Vert.x 节点都持有集群数据的一部分:事件总线订阅、异步映射条目、集群计数器等。
当成员加入或离开集群时,Infinispan 会在新成员集合上重新平衡缓存条目。换句话说,它会移动数据以适应新的集群拓扑。这个过程可能需要一些时间,具体取决于集群数据量和节点数量。
脑裂(Split-brain syndrome)
在一个完美的世界里,不会有网络设备故障。然而,现实是,您的集群迟早会分裂成更小的组,彼此之间无法看到。
Infinispan 能够将节点合并回单个集群。但就像再平衡一样,这个过程可能需要一些时间。在集群再次完全正常运行之前,一些事件总线消费者可能无法接收消息。或者高可用性可能无法重新部署失败的 verticle。
区分网络分区和以下情况是很困难的(如果可能的话):
|
建议
考虑到上面讨论的常见集群问题,建议遵循以下良好实践。
逐个节点
在推出应用程序新版本、扩展或缩减集群时,请逐个添加或删除节点。
逐个停止节点可以防止集群误认为发生了网络分区。逐个添加它们则可以实现干净、增量的再平衡操作。
集群的健康状况可以通过 Vert.x 健康检查进行验证。
Handler<Promise<Status>> procedure = ClusterHealthCheck.createProcedure(vertx, true);
HealthChecks checks = HealthChecks.create(vertx).register("cluster-health", procedure);
创建后,健康检查可以通过 Vert.x Web 路由处理器通过 HTTP 公开。
Router router = Router.router(vertx);
router.get("/readiness").handler(HealthCheckHandler.createWithHealthChecks(checks));