1. The document describes a microservices application with BaseStar and Raider microservices.
2. The BaseStar service registers itself with the Redis service discovery and defines REST API routes including one to return a list of registered Raider services.
3. When a Raider service registers with the BaseStar via a POST call, BaseStar looks it up, starts a worker task to communicate with it, and returns a success message.
4. #BSGTHEPLAN #BlendWebMix
pourquoi ce talk?
Trouver un sujet de talk
Le préparer pendant les heures de travail (démos)
monter en compétence sur les microservices (et vert.x)
partager ce que j’ai appris
6. #BSGTHEPLAN #BlendWebMix
microservices?
Une application à base de microservices =
ensemble de services autonomes
Ces services collaborent pour exécuter des
tâches complexes
Protocoles de messages standard
Mode direct ou asynchrone
7. #BSGTHEPLAN #BlendWebMix
micro, m’ouais …
Distribuer les fonctionnalités et les données
Identifier les microservices, les trouver, …
Gérer la cohérence et la fiabilité du réseau pour chacun
Composants distribués + Technologies différentes = “new modes of failure”
Quand ça ne marche pas … 😡 Complexité d’investigation
8. #BSGTHEPLAN #BlendWebMix
microservices vert.x 😍
Simplicité, Polyglotte
C’est un framework simple: Maîtrise de ce que vous
faites
BackendDiscovery / Service Discovery (ex: Hazelcast,
Redis, …)
Stabilité & Résilience
Circuit Breakers
Health Checks
9. #BSGTHEPLAN #BlendWebMix
1 microservice REST Vert.x
public class BaseStar extends AbstractVerticle {
public void start() {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.get("/api/hello").handler(context -> {
context.response()
.putHeader("content-type", "application/json;charset=UTF-8")
.end(new JsonObject().put("message", "hello").encodePrettily());
});
Integer httpPort = 8080;
HttpServer server = vertx.createHttpServer();
server.requestHandler(router::accept).listen(httpPort, result -> {
System.out.println("🌍 Listening on " + httpPort);
});
}
}
48. #BSGTHEPLAN #BlendWebMix
🌍 WebApp
BaseStar
JAVA
BaseStar
JAVA
BaseStar
JAVA
REDIS
BaseStar
JAVA
GET http://localhost:808N/api/raiders
POST http://localhost:808N/api/raiders
GET http://localhost:808N/health
BSG
monitor
SCALA
GET http://localhost:8080/api/raiders
BSG
map
JS
http://localhost:7070
BaseStar
JAVA
Raider
KOTLIN
Raider
KOTLIN
Raider
GROOVY
Raider
GROOVY
POST http://localhost:909N/api/coordinates
49. #BSGTHEPLAN #BlendWebMix
🌍 WebApp | Backend Scala
val server = vertx.createHttpServer()
val router = Router.router(vertx)
// Settings for the Redis backend
val redisHost = sys.env.getOrElse("REDIS_HOST", "127.0.0.1")
val redisPort = sys.env.getOrElse("REDIS_PORT", "6379").toInt
val redisAuth = sys.env.getOrElse("REDIS_PASSWORD", null)
val redisRecordsKey = sys.env.getOrElse("REDIS_RECORDS_KEY", "vert.x.ms")
// Mount the service discovery backend (Redis)
val discovery = ServiceDiscovery.create(vertx, ServiceDiscoveryOptions()
.setBackendConfiguration(
new JsonObject()
.put("host", redisHost)
.put("port", redisPort)
.put("auth", redisAuth)
.put("key", redisRecordsKey)
)
)
68. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)// 👋 ⚠ call by a basestar
router.post("/api/coordinates").handler { context ->
// check data -> if null, don't move
val computedX = context.bodyAsJson.getDouble("x") ?: x
val computedY = context.bodyAsJson.getDouble("y") ?: y
val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity
val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity
/* 💾 === updating record of the service === */
record?.metadata?.getJsonObject("coordinates")
?.put("x", computedX)
?.put("y",computedY)
?.put("xVelocity",computedXVelocity)
?.put("yVelocity",computedYVelocity)
record?.metadata?.put("basestar", json {obj(
"name:" to baseStar?.record?.name,
"color" to baseStar?.record?.metadata?.get("color")
)})
discovery?.update(record, {asyncUpdateResult -> })
context
.response().putHeader("content-type", "application/json;charset=UTF-8")
.end(json {obj(
"message" to "👍", "x" to computedX, "y" to computedY
)}.toString())
}
69. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)// 👋 ⚠ call by a basestar
router.post("/api/coordinates").handler { context ->
// check data -> if null, don't move
val computedX = context.bodyAsJson.getDouble("x") ?: x
val computedY = context.bodyAsJson.getDouble("y") ?: y
val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity
val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity
/* 💾 === updating record of the service === */
record?.metadata?.getJsonObject("coordinates")
?.put("x", computedX)
?.put("y",computedY)
?.put("xVelocity",computedXVelocity)
?.put("yVelocity",computedYVelocity)
record?.metadata?.put("basestar", json {obj(
"name:" to baseStar?.record?.name,
"color" to baseStar?.record?.metadata?.get("color")
)})
discovery?.update(record, {asyncUpdateResult -> })
context
.response().putHeader("content-type", "application/json;charset=UTF-8")
.end(json {obj(
"message" to "👍", "x" to computedX, "y" to computedY
)}.toString())
}
79. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
80. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
81. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
82. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
⚠⚠⚠
84. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
85. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
86. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
👉
👉
87. #BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
👉
👉
99. #BSGTHEPLAN #BlendWebMix
Perspectives
• Déploiement sur le cloud, j’ai eu des surprises
• Remettre à plat mon code | Refactoring
• Ecrire un autre ServiceDiscoveryBackend (Redis PubSub, MQTT, …)
• Utilisation d’un reverse proxy (peut-être)
• Ajouter les 12 colonies dans la démo
https://github.com/botsgarden/vertx-service-discovery-backend-redisson