<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-hazelcast</artifactId>
<version>5.0.1</version>
</dependency>
Hazelcast 集群管理器
这是 Vert.x 的一个集群管理器实现,它使用 Hazelcast。
它是 Vert.x CLI 中使用的默认集群管理器,但可以由其他实现替代,因为 Vert.x 集群管理器是可插拔的。
此实现打包在
在 Vert.x 中,集群管理器用于多种功能,包括
-
集群中 Vert.x 节点的发现和组成员管理
-
维护集群范围的主题订阅者列表(以便我们知道哪些节点对哪些事件总线地址感兴趣)
-
分布式 Map 支持
-
分布式锁
-
分布式计数器
集群管理器不处理事件总线节点间传输,这由 Vert.x 直接通过 TCP 连接完成。
使用此集群管理器
如果您通过命令行使用 Vert.x,则此集群管理器对应的 jar (名为 vertx-hazelcast-5.0.1.jar
) 应该位于 Vert.x 安装目录的 lib
文件夹中。
如果您想在 Vert.x Maven 或 Gradle 项目中使用此集群管理器进行集群化,只需在您的项目中添加对工件 io.vertx:vertx-hazelcast:5.0.1
的依赖即可。
如果 JAR 包如上所示位于您的类路径中,Vert.x 将自动检测并使用它作为集群管理器。请确保您的类路径中没有其他集群管理器,否则 Vert.x 可能会选择错误的管理器。
如果您嵌入 Vert.x,也可以在创建 Vert.x 实例时通过在选项中指定来以编程方式指定集群管理器,例如
ClusterManager mgr = new HazelcastClusterManager();
Vertx
.builder()
.withClusterManager(mgr)
.buildClustered()
.onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
配置此集群管理器
使用 XML 配置
通常,集群管理器由一个名为 default-cluster.xml
的文件配置,该文件打包在 jar 中。
如果您想覆盖此配置,可以在您的类路径上提供一个名为 cluster.xml
的文件,它将代替默认文件使用。如果您想将 cluster.xml
文件嵌入到胖 jar 中,它必须位于胖 jar 的根目录。如果它是外部文件,则必须将包含该文件的目录添加到类路径中。例如,如果您使用 Vert.x 的 launcher 类,类路径增强可以按如下方式完成:
# If the cluster.xml is in the current directory:
java -jar ... -cp . -cluster
vertx run MyVerticle -cp . -cluster
# If the cluster.xml is in the conf directory
java -jar ... -cp conf -cluster
另一种覆盖配置的方法是通过提供系统属性 vertx.hazelcast.config
并指定一个位置:
# Use a cluster configuration located in an external file
java -Dvertx.hazelcast.config=./config/my-cluster-config.xml -jar ... -cluster
# Or use a custom configuration from the classpath
java -Dvertx.hazelcast.config=classpath:my/package/config/my-cluster-config.xml -jar ... -cluster
当存在 vertx.hazelcast.config
系统属性时,它会覆盖类路径上的任何 cluster.xml
,但如果从该系统属性加载失败,则会回退到 cluster.xml
或 Hazelcast 默认配置。
Vert.x 不支持使用 -Dhazelcast.config 系统属性配置 Hazelcast,不应使用。 |
xml 文件是一个 Hazelcast 配置文件,Hazelcast 网站的文档中对其进行了详细描述。
以编程方式配置
如果嵌入,您可以以编程方式指定配置:
Config hazelcastConfig = new Config();
// Now set some stuff on the config (omitted)
ClusterManager mgr = new HazelcastClusterManager(hazelcastConfig);
Vertx
.builder()
.withClusterManager(mgr)
.buildClustered()
.onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
自定义现有 XML 配置也可能很有用。例如,您可能想更改集群名称:
Config hazelcastConfig = ConfigUtil.loadConfig();
hazelcastConfig.setClusterName("my-cluster-name");
ClusterManager mgr = new HazelcastClusterManager(hazelcastConfig);
Vertx
.builder()
.withClusterManager(mgr)
.buildClustered()
.onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
ConfigUtil#loadConfig
加载 Hazelcast 配置 XML 并将其转换为 Config
对象。内容从以下位置读取:
-
如果存在
vertx.hazelcast.config
系统属性所表示的位置,或者 -
如果存在类路径上的
cluster.xml
文件,或者 -
默认配置文件
使用系统属性更改本地和公共地址
有时,集群节点必须绑定到其他成员无法访问的地址。例如,当节点不在同一网络区域或在某些具有特定防火墙配置的云上时,可能会发生这种情况。
绑定地址和公共地址(通告给其他成员的地址)可以通过系统属性设置:
-Dhazelcast.local.localAddress=172.16.5.131 -Dhazelcast.local.publicAddress=104.198.78.81
使用现有 Hazelcast 集群
您可以在集群管理器中传递现有的 HazelcastInstance
以重用现有集群:
ClusterManager mgr = new HazelcastClusterManager(hazelcastInstance);
Vertx
.builder()
.withClusterManager(mgr)
.buildClustered()
.onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});
在这种情况下,Vert.x 不是集群所有者,因此在关闭时不会关闭集群。
请注意,自定义 Hazelcast 实例需要配置:
<member-attributes>
<attribute name="__vertx.nodeId">unique-identifier</attribute>
</member-attributes>
<multimap name="__vertx.subs">
<backup-count>1</backup-count>
<value-collection-type>SET</value-collection-type>
</multimap>
<map name="__vertx.haInfo">
<backup-count>1</backup-count>
</map>
<map name="__vertx.nodeInfo">
<backup-count>1</backup-count>
</map>
__vertx.nodeId 被 Vert.x 用作集群中节点的标识符。请确保为所有成员配置唯一值。 |
不支持 Hazelcast 客户端或智能客户端。 |
请确保 Hazelcast 在 Vert.x 之前启动并在 Vert.x 之后关闭。此外,应禁用 Hazelcast 关闭钩子(参见 xml 示例,或通过系统属性禁用)。 |
更改故障节点的超时时间
默认情况下,如果 Hazelcast 在 60 秒内没有收到心跳,节点将被从集群中移除。要更改此值,请使用 hazelcast.max.no.heartbeat.seconds
系统属性,例如:
-Dhazelcast.max.no.heartbeat.seconds=5
此后,如果节点在 5 秒内没有心跳,它将被从集群中移除。
请参阅 Hazelcast 系统属性。
集群故障排除
如果默认的多播配置不起作用,以下是一些常见原因:
机器上未启用多播。
在 OSX 机器上,多播默认被禁用是很常见的。请搜索有关如何启用此功能的信息。
使用错误的网络接口
如果您的机器上有多个网络接口(如果您在机器上运行 VPN 软件也可能如此),那么 Hazelcast 可能会使用错误的接口。
要告诉 Hazelcast 使用特定接口,您可以在配置的 interfaces
元素中提供接口的 IP 地址。请确保将 enabled
属性设置为 true
。例如:
<interfaces enabled="true">
<interface>192.168.1.20</interface>
</interfaces>
使用 VPN
这是上述情况的一个变体。VPN 软件通常通过创建一个虚拟网络接口来工作,该接口通常不支持多播。如果您运行着 VPN 并且没有在 hazelcast 配置和 Vert.x 中指定要使用的正确接口,则可能会选择 VPN 接口而不是正确的接口。
因此,如果您运行着 VPN,您可能需要按照上一节中的描述,配置 Hazelcast 和 Vert.x 都使用正确的接口。
当多播不可用时
在某些情况下,您可能无法使用多播,因为它在您的环境中可能不可用。在这种情况下,您应该配置其他传输,例如使用 TCP 套接字的 TCP,或在 Amazon EC2 上运行时使用 AWS。
有关可用 Hazelcast 传输及其配置方法的更多信息,请查阅 Hazelcast 文档。
启用日志记录
在对 Hazelcast 集群问题进行故障排除时,通常获取 Hazelcast 的一些日志输出来查看其是否正确形成集群会很有用。您可以通过在类路径中添加一个名为 vertx-default-jul-logging.properties
的文件来完成此操作(当使用默认 JUL 日志记录时)。这是一个标准的 java.util.logging (JUL) 配置文件。在其中设置:
com.hazelcast.level=INFO
并且
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.FileHandler.level=INFO
Hazelcast 日志记录
Hazelcast 使用的日志后端默认为 jdk
(即 JUL)。如果您想将日志重定向到其他库,您需要设置 hazelcast.logging.type
系统属性,例如:
-Dhazelcast.logging.type=slf4j
有关更多详细信息,请参阅 hazelcast 文档。
使用不同版本的 Hazelcast
您可能希望使用不同版本的 Hazelcast。默认版本是 ${hazelcast.version}
。为此,您需要:
-
将您想要的版本放入应用程序类路径中
-
如果您正在运行胖 jar,请配置您的构建管理器以使用正确的版本
在后一种情况下,您需要在 Maven 中:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>ENTER_YOUR_VERSION_HERE</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-hazelcast</artifactId>
<version>5.0.1</version>
</dependency>
根据版本,您可能需要排除一些传递依赖。
在 Gradle 上,您可以使用以下方法实现相同的重载:
dependencies {
compile ("io.vertx:vertx-hazelcast:5.0.1"){
exclude group: 'com.hazelcast', module: 'hazelcast'
}
compile "com.hazelcast:hazelcast:ENTER_YOUR_VERSION_HERE"
}
Kubernetes 配置
第一步是在您的 Hazelcast 配置中配置发现插件,通过提供自定义的 cluster.xml
文件或以编程方式,如配置此集群管理器中所述。
发现模式:Kubernetes API 和 DNS Lookup。请参考插件项目页面了解两种模式的优缺点。
在本文档中,我们将使用 DNS Lookup 发现。以下属性必须更改/添加:
<hazelcast>
<network>
<join>
<auto-detection enabled="false"/> (1)
<multicast enabled="false"/> (2)
<kubernetes enabled="true"> (3)
<service-dns>MY-SERVICE-DNS-NAME</service-dns> (4)
</kubernetes>
</join>
</network>
</hazelcast>
1 | 禁用发现插件自动检测 |
2 | 禁用多播发现 |
3 | 激活 Kubernetes 发现 |
4 | 服务 DNS,通常形式为 MY-SERVICE-NAME.MY-NAMESPACE.svc.cluster.local ,但取决于 Kubernetes 分发版本 |
上述配置摘录仅包含网络相关设置。实际配置必须包含最低所需设置。 |
MY-SERVICE-DNS-NAME
的值必须是一个无头 Kubernetes 服务名称,Hazelcast 将使用它来识别所有集群成员。无头服务可以通过以下方式创建:
apiVersion: v1
kind: Service
metadata:
namespace: MY-NAMESPACE
name: MY-SERVICE-NAME
spec:
type: ClusterIP
clusterIP: None
selector:
component: MY-SERVICE-NAME (1)
ports:
- name: hazelcast
port: 5701
protocol: TCP
1 | 按标签选择集群成员 |
最后,将 component
标签附加到所有应属于集群的部署:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
namespace: MY-NAMESPACE
spec:
template:
metadata:
labels:
component: MY-SERVICE-NAME
更多配置详情可在 Kubernetes 自动发现页面上找到。
滚动更新
在滚动更新期间,建议逐个替换 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 健康检查实现就绪探针的详细信息,请参阅集群管理部分。
集群管理
Hazelcast 集群管理器通过将 Vert.x 节点转换为 Hazelcast 集群的成员来工作。因此,Vert.x 集群管理器管理应遵循 Hazelcast 管理指南。
首先,让我们退一步介绍数据分区和脑裂现象。
数据分区
每个 Vert.x 节点都持有部分集群数据:事件总线订阅、异步映射条目、集群计数器等。
当成员加入或离开集群时,Hazelcast 会迁移数据分区。换句话说,它会移动数据以适应新的集群拓扑。这个过程可能需要一些时间,具体取决于集群数据量和节点数量。
脑裂现象
在一个完美的世界里,不会有网络设备故障。然而,现实是,您的集群迟早会分裂成更小的组,彼此无法看到。
Hazelcast 能够将节点重新合并成一个单一的集群。但与数据分区迁移一样,这个过程可能需要一些时间。在集群完全恢复功能之前,一些事件总线消费者可能无法接收消息。或者高可用性可能无法重新部署失败的 verticle。
很难(如果可能的话)区分网络分区和:
|
建议
考虑到上面讨论的常见集群问题,建议遵循以下良好实践。
一个接一个地
当滚动新版本的应用程序、扩展或缩减集群时,请一个接一个地添加或移除节点。
逐个停止节点可以防止集群误认为发生了网络分区。逐个添加节点可以实现干净、增量的数据分区迁移。
集群安全可以通过 Vert.x 健康检查进行验证。
Handler<Promise<Status>> procedure = ClusterHealthCheck.createProcedure(vertx);
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));
使用精简成员(Lite Members)
为了最大程度地减少 Vert.x 集群适应新拓扑所需的时间,您可以使用外部数据节点,并将 Vert.x 节点标记为 精简成员(Lite Members)。
精简成员(Lite Members)像普通成员一样参与 Hazelcast 集群,但它们不拥有任何数据分区。因此,当添加或移除此类成员时,Hazelcast 不需要迁移分区。
您必须事先启动外部数据节点,因为 Hazelcast 不会只由 精简成员(Lite Members)构成集群。 |
要启动外部节点,您可以使用 Hazelcast 分发启动脚本,或以编程方式进行。
Vert.x 节点可以在 XML 配置中标记为 精简成员(Lite Members):
<lite-member enabled="true"/>
您也可以通过编程方式进行:
Config hazelcastConfig = ConfigUtil.loadConfig()
.setLiteMember(true);
ClusterManager mgr = new HazelcastClusterManager(hazelcastConfig);
Vertx
.builder()
.withClusterManager(mgr)
.buildClustered()
.onComplete(res -> {
if (res.succeeded()) {
Vertx vertx = res.result();
} else {
// failed!
}
});