autonomous services that, individually, "do one thing well", but work together to perform more intricate operations. Instead of a single complex system, you build and manage a suite of relatively simple services that might interact in complex ways. These services collaborate with each other through technology-agnostic messaging protocols, either point-to-point or asynchronously.
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
JAVA REDIS BaseStar JAVA GET http://localhost:808N/api/raiders POST http://localhost:808N/api/raiders GET http://localhost:808N/health Raider KOTLIN Raider KOTLIN Raider GROOVY Raider GROOVY POST http://localhost:909N/api/coordinates
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) ) )
AbstractVerticle() { override fun start() { // add metadata record?.metadata = json { obj( "kind" to "raider", "message" to " ready to fight", "basestar" to null, "coordinates" to obj( "x" to random(0.0, 400.0), "y" to random(0.0, 400.0) ), "app_id" to (System.getenv("APP_ID") ?: ""), "instance_id" to (System.getenv("INSTANCE_ID") ?: ""), "instance_type" to (System.getenv("INSTANCE_TYPE") ?: "production"), "instance_number" to (Integer.parseInt(System.getenv("INSTANCE_NUMBER") ?: "0")) ) } } }
{ 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()) }
{ 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()) }
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 -> }) }
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 -> }) }
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 -> }) }
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 -> }) } ⚠⚠⚠
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 === } } } }) }
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 === } } } }) }
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 === } } } }) }
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 === } } } }) }
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 Raider GROOVY Raider KOTLIN Raider GROOVY GET http://localhost:909N/health POST http://localhost:808N/api/raiders POST http://localhost:909N/api/coordinates GET http://localhost:808N/health
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