Vert.x 高可用性不起作用
Vert.x high availability is not working
简要说明
我刚刚开始使用 VertX,我想通过一个小玩具示例来尝试高可用性功能。在我的设置中,我有一个部署到多个 docker 容器的 fatjar 应用程序。该应用程序以编程方式创建一个 VertX 实例并启动一个名为 ContainerVerticle
的 Verticle。这个 运行 是一个 HTTP 服务器并充当 "launcher" - 当收到 "SPAWN" 命令时,它会在高可用性模式下部署另一个名为 AppVerticle
的 Verticle。我的想法是,我想在 3 个容器上 运行,然后在其中一个容器上终止 JVM,这应该将 AppVerticle 重新部署到另一个 docker 容器。
实际结果:Verticle 可以使用事件总线相互通信,集群似乎也正常工作:根据日志文件,成员可以看到彼此。但是,当我杀死一个 Verticle 时,它并没有被重新部署。
更多详情
(所有源代码均使用 Kotlin 编写)
顶点初始化:
val hzConfig = Config()
val mgr = HazelcastClusterManager(hzConfig) // empty config -> use default
val hostAddress = getAddress() // get the local ip address (not localhost!)
val options = VertxOptions()
.setClustered(true)
.setClusterHost(hostAddress)
.setClusterPort(18001)
.setClusterManager(mgr)
//.setQuorumSize(2)
.setHAEnabled(true)
val eventBusOptions = EventBusOptions()
eventBusOptions
.setClustered(true)
.setHost(hostAddress)
.setPort(18002)
options.setEventBusOptions(eventBusOptions)
Vertx.clusteredVertx(options) { res ->
if (res.succeeded()) {
val vertx = res.result()
vertx.deployVerticle(ContainerVerticle::class.java.name,
DeploymentOptions()
.setHa(false)) // ContainerVerticle should not restart
}
}
ContainerVerticle(我们的'launcher')
class ContainerVerticle : AbstractVerticle() {
...
override fun start(startFuture: Future<Void>?) {
val router = createRouter()
val port = config().getInteger("http.port", 8080)
vertx.eventBus().consumer<Any>("mynamspace.container.spawn") { message ->
val appVerticleID = message.body()
log.info(" - HANDLE SPAWN message \"${appVerticleID}\"")
val appVerticleConfig = JsonObject().put("ID", appVerticleID)
vertx.deployVerticle(AppVerticle::class.java.name, // Deploy the APP!!!
DeploymentOptions()
.setConfig(appVerticleConfig)
.setInstances(1)
.setHa(true))
}
vertx.createHttpServer()... // omitted (see github link)
}
private fun createRouter(): Router { ... } // omitted (see github link)
val handlerRoot = Handler<RoutingContext> { routingContext ->
val cmd = routingContext.bodyAsString
val tokens = cmd.split(" ")
if (tokens[0] == "spawn") {
vertx.eventBus().send("mynamspace.container.spawn", tokens[1]) // round-robin
routingContext.response().end("Successfully handled command ${cmd}\n")
} else if (tokens[0] == "send") {
vertx.eventBus().send("mynamspace.app.${tokens[1]}", tokens[2])
routingContext.response().end("success\n")
} else {
routingContext.response().end("ERROR: Unknown command ${cmd}\n")
}
}
}
最后一部分:AppVerticle:
class AppVerticle : AbstractVerticle() {
var timerID = 0L
override fun start(startFuture: Future<Void>?) {
val id = config().getString("ID")
log.info(" SPAWNED app verticle \"${id}\"")
vertx.eventBus().consumer<Any>("mynamspace.app.${id}") { message ->
val cmd = message.body()
log.info(" - app verticle \"${id}\" handled message ${cmd}")
}
timerID = vertx.setPeriodic(1000) {
log.info(" - app verticle \"${id}\" is alive")
}
}
}
运行
打开 3 个终端和 运行 3 docker 个实例。次要细节:这里我们将端口 8080 重新映射到三个不同的端口 8081、8082、8083,我们还为容器指定了唯一的名称:cont1、cont2、cont3
docker run --name "cont1" -it --rm -p 8081:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
docker run --name "cont2" -it --rm -p 8082:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
docker run --name "cont2" -it --rm -p 8083:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
观察 1
由于以下消息,集群成员似乎看到了彼此:
Members [3] {
Member [172.17.0.2]:5701 - 1d50394c-cf11-4bd7-877e-7e06e2959940 this
Member [172.17.0.3]:5701 - 3fa2cff4-ba9e-431b-9c4e-7b1fd8de9437
Member [172.17.0.4]:5701 - b9a3114a-7c15-4992-b609-63c0f22ed388
}
我们也可以跨越 AppContainer
:
curl -d "spawn -={Application-1}=-" -XPOST http://localhost:8083
消息总线似乎工作正常,因为我们看到生成消息以循环方式传送到 ContainerVerticle
。
观察2-问题
现在让我们尝试杀死 Verticle(假设它在 cont2 中 运行s):
docker kill --signal=SIGKILL cont2
其余的容器似乎对那个事件有反应,日志文件是这样的:
Aug 14, 2018 8:18:45 AM com.hazelcast.internal.cluster.ClusterService
INFO: [172.17.0.4]:5701 [dev] [3.8.2] Removing Member [172.17.0.2]:5701 - fbe67a02-80a3-4207-aa10-110fc09e0607
Aug 14, 2018 8:18:45 AM com.hazelcast.internal.cluster.ClusterService
INFO: [172.17.0.4]:5701 [dev] [3.8.2]
Members [2] {
Member [172.17.0.3]:5701 - 8b93a822-aa7f-460d-aa3e-568e0d85067c
Member [172.17.0.4]:5701 - b0ecea8e-59f1-440c-82ca-45a086842004 this
}
但是 AppVerticle
没有重新部署。
完整的源代码可在 github 上获得:
https://github.com/conceptacid/vertx-ha-eval
我花了几个小时调试这个,但终于找到了。
所以这里是解决方案:
你的verticle启动方法header是:
override fun start(startFuture: Future<Void>?)
您正在重写开始方法,这为您提供了在 Verticle 开始后等待的未来。 Vert.x 永远等待这个未来的完成,因为你不打电话
startFuture.complete()
方法结束时。
因此 Verticle 永远不会添加到 HAManager 的 verticle-list 中,因此不会重新部署。
或者,您可以使用
override fun start()
as 方法 header 如果你的 Verticle 做一个简单的同步 start-up.
希望这对您有所帮助。
简要说明
我刚刚开始使用 VertX,我想通过一个小玩具示例来尝试高可用性功能。在我的设置中,我有一个部署到多个 docker 容器的 fatjar 应用程序。该应用程序以编程方式创建一个 VertX 实例并启动一个名为 ContainerVerticle
的 Verticle。这个 运行 是一个 HTTP 服务器并充当 "launcher" - 当收到 "SPAWN" 命令时,它会在高可用性模式下部署另一个名为 AppVerticle
的 Verticle。我的想法是,我想在 3 个容器上 运行,然后在其中一个容器上终止 JVM,这应该将 AppVerticle 重新部署到另一个 docker 容器。
实际结果:Verticle 可以使用事件总线相互通信,集群似乎也正常工作:根据日志文件,成员可以看到彼此。但是,当我杀死一个 Verticle 时,它并没有被重新部署。
更多详情
(所有源代码均使用 Kotlin 编写) 顶点初始化:
val hzConfig = Config()
val mgr = HazelcastClusterManager(hzConfig) // empty config -> use default
val hostAddress = getAddress() // get the local ip address (not localhost!)
val options = VertxOptions()
.setClustered(true)
.setClusterHost(hostAddress)
.setClusterPort(18001)
.setClusterManager(mgr)
//.setQuorumSize(2)
.setHAEnabled(true)
val eventBusOptions = EventBusOptions()
eventBusOptions
.setClustered(true)
.setHost(hostAddress)
.setPort(18002)
options.setEventBusOptions(eventBusOptions)
Vertx.clusteredVertx(options) { res ->
if (res.succeeded()) {
val vertx = res.result()
vertx.deployVerticle(ContainerVerticle::class.java.name,
DeploymentOptions()
.setHa(false)) // ContainerVerticle should not restart
}
}
ContainerVerticle(我们的'launcher')
class ContainerVerticle : AbstractVerticle() {
...
override fun start(startFuture: Future<Void>?) {
val router = createRouter()
val port = config().getInteger("http.port", 8080)
vertx.eventBus().consumer<Any>("mynamspace.container.spawn") { message ->
val appVerticleID = message.body()
log.info(" - HANDLE SPAWN message \"${appVerticleID}\"")
val appVerticleConfig = JsonObject().put("ID", appVerticleID)
vertx.deployVerticle(AppVerticle::class.java.name, // Deploy the APP!!!
DeploymentOptions()
.setConfig(appVerticleConfig)
.setInstances(1)
.setHa(true))
}
vertx.createHttpServer()... // omitted (see github link)
}
private fun createRouter(): Router { ... } // omitted (see github link)
val handlerRoot = Handler<RoutingContext> { routingContext ->
val cmd = routingContext.bodyAsString
val tokens = cmd.split(" ")
if (tokens[0] == "spawn") {
vertx.eventBus().send("mynamspace.container.spawn", tokens[1]) // round-robin
routingContext.response().end("Successfully handled command ${cmd}\n")
} else if (tokens[0] == "send") {
vertx.eventBus().send("mynamspace.app.${tokens[1]}", tokens[2])
routingContext.response().end("success\n")
} else {
routingContext.response().end("ERROR: Unknown command ${cmd}\n")
}
}
}
最后一部分:AppVerticle:
class AppVerticle : AbstractVerticle() {
var timerID = 0L
override fun start(startFuture: Future<Void>?) {
val id = config().getString("ID")
log.info(" SPAWNED app verticle \"${id}\"")
vertx.eventBus().consumer<Any>("mynamspace.app.${id}") { message ->
val cmd = message.body()
log.info(" - app verticle \"${id}\" handled message ${cmd}")
}
timerID = vertx.setPeriodic(1000) {
log.info(" - app verticle \"${id}\" is alive")
}
}
}
运行
打开 3 个终端和 运行 3 docker 个实例。次要细节:这里我们将端口 8080 重新映射到三个不同的端口 8081、8082、8083,我们还为容器指定了唯一的名称:cont1、cont2、cont3
docker run --name "cont1" -it --rm -p 8081:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
docker run --name "cont2" -it --rm -p 8082:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
docker run --name "cont2" -it --rm -p 8083:8080 -v $PWD/build/libs:/app anapsix/alpine-java java -jar /app/vertxhaeval-1.0-SNAPSHOT-all.jar
观察 1
由于以下消息,集群成员似乎看到了彼此:
Members [3] {
Member [172.17.0.2]:5701 - 1d50394c-cf11-4bd7-877e-7e06e2959940 this
Member [172.17.0.3]:5701 - 3fa2cff4-ba9e-431b-9c4e-7b1fd8de9437
Member [172.17.0.4]:5701 - b9a3114a-7c15-4992-b609-63c0f22ed388
}
我们也可以跨越 AppContainer
:
curl -d "spawn -={Application-1}=-" -XPOST http://localhost:8083
消息总线似乎工作正常,因为我们看到生成消息以循环方式传送到 ContainerVerticle
。
观察2-问题
现在让我们尝试杀死 Verticle(假设它在 cont2 中 运行s):
docker kill --signal=SIGKILL cont2
其余的容器似乎对那个事件有反应,日志文件是这样的:
Aug 14, 2018 8:18:45 AM com.hazelcast.internal.cluster.ClusterService
INFO: [172.17.0.4]:5701 [dev] [3.8.2] Removing Member [172.17.0.2]:5701 - fbe67a02-80a3-4207-aa10-110fc09e0607
Aug 14, 2018 8:18:45 AM com.hazelcast.internal.cluster.ClusterService
INFO: [172.17.0.4]:5701 [dev] [3.8.2]
Members [2] {
Member [172.17.0.3]:5701 - 8b93a822-aa7f-460d-aa3e-568e0d85067c
Member [172.17.0.4]:5701 - b0ecea8e-59f1-440c-82ca-45a086842004 this
}
但是 AppVerticle
没有重新部署。
完整的源代码可在 github 上获得: https://github.com/conceptacid/vertx-ha-eval
我花了几个小时调试这个,但终于找到了。
所以这里是解决方案:
你的verticle启动方法header是:
override fun start(startFuture: Future<Void>?)
您正在重写开始方法,这为您提供了在 Verticle 开始后等待的未来。 Vert.x 永远等待这个未来的完成,因为你不打电话
startFuture.complete()
方法结束时。
因此 Verticle 永远不会添加到 HAManager 的 verticle-list 中,因此不会重新部署。
或者,您可以使用
override fun start()
as 方法 header 如果你的 Verticle 做一个简单的同步 start-up.
希望这对您有所帮助。