Slide 1

Slide 1 text

#BSGTHEPLAN @DevFestToulouse The Plan

Slide 2

Slide 2 text

#BSGTHEPLAN @DevFestToulouse bonjour Philippe Charrière @clever_cloud ! @k33g_org G @k33g I ❤JavaScript, I Java, I JVM, I Golo, I Scala (depuis peu…)

Slide 3

Slide 3 text

#BSGTHEPLAN @DevFestToulouse avertissements Je ne suis pas un développeur Java … Enfin je ne crois pas

Slide 4

Slide 4 text

#BSGTHEPLAN @DevFestToulouse 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

Slide 5

Slide 5 text

#BSGTHEPLAN @DevFestToulouse Plan de “the plan” Intro - microservices anatomie du plan

Slide 6

Slide 6 text

#BSGTHEPLAN @DevFestToulouse microservices? A microservice application is a collection of 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.

Slide 7

Slide 7 text

#BSGTHEPLAN @DevFestToulouse 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

Slide 8

Slide 8 text

#BSGTHEPLAN @DevFestToulouse 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

Slide 9

Slide 9 text

#BSGTHEPLAN @DevFestToulouse 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); }); } }

Slide 10

Slide 10 text

#BSGTHEPLAN @DevFestToulouse Le Plan Redis + http #BSGTHEPLAN @DevFestToulouse

Slide 11

Slide 11 text

#BSGTHEPLAN @DevFestToulouse objectifs #BSGTHEPLAN @DevFestToulouse

Slide 12

Slide 12 text

#BSGTHEPLAN @DevFestToulouse Plan de “the plan” Intro - microservices anatomie du plan BaseStars WebApp Raiders Conclusion(s)

Slide 13

Slide 13 text

#BSGTHEPLAN @DevFestToulouse BaseStar Java

Slide 14

Slide 14 text

#BSGTHEPLAN @DevFestToulouse BaseStar Service Discovery & Backend

Slide 15

Slide 15 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer BaseStar JAVA BaseStar JAVA BaseStar JAVA BaseStar JAVA REDIS BaseStar JAVA

Slide 16

Slide 16 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer - ServiceDiscovery public class BaseStar extends AbstractVerticle { public void start() { // Discovery settings ServiceDiscoveryOptions serviceDiscoveryOptions = new ServiceDiscoveryOptions(); // Redis settings with the standard Redis Backend Integer redisPort = Integer.parseInt(Optional.ofNullable(System.getenv("REDIS_PORT")).orElse("6379")); String redisHost = Optional.ofNullable(System.getenv("REDIS_HOST")).orElse("127.0.0.1"); String redisAuth = Optional.ofNullable(System.getenv("REDIS_PASSWORD")).orElse(null); String redisRecordsKey = Optional.ofNullable(System.getenv("REDIS_RECORDS_KEY")).orElse("vert.x.ms"); discovery = ServiceDiscovery.create( vertx, serviceDiscoveryOptions.setBackendConfiguration( new JsonObject() .put("host", redisHost) .put("port", redisPort) .put("auth", redisAuth) .put("key", redisRecordsKey) )); } }

Slide 17

Slide 17 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer - ServiceDiscovery public class BaseStar extends AbstractVerticle { public void start() { // Discovery settings ServiceDiscoveryOptions serviceDiscoveryOptions = new ServiceDiscoveryOptions(); // Redis settings with the standard Redis Backend Integer redisPort = Integer.parseInt(Optional.ofNullable(System.getenv("REDIS_PORT")).orElse("6379")); String redisHost = Optional.ofNullable(System.getenv("REDIS_HOST")).orElse("127.0.0.1"); String redisAuth = Optional.ofNullable(System.getenv("REDIS_PASSWORD")).orElse(null); String redisRecordsKey = Optional.ofNullable(System.getenv("REDIS_RECORDS_KEY")).orElse("vert.x.ms"); discovery = ServiceDiscovery.create( vertx, serviceDiscoveryOptions.setBackendConfiguration( new JsonObject() .put("host", redisHost) .put("port", redisPort) .put("auth", redisAuth) .put("key", redisRecordsKey) )); } }

Slide 18

Slide 18 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer - Record public class BaseStar extends AbstractVerticle { public void start() { // microservice options Haikunator haikunator = new HaikunatorBuilder().setTokenLength(6).build(); String niceName = haikunator.haikunate(); String serviceName = Optional.ofNullable(System.getenv("SERVICE_NAME")).orElse("the-plan")+"-"+niceName; String serviceHost = Optional.ofNullable(System.getenv("SERVICE_HOST")).orElse("localhost"); Integer servicePort = Integer.parseInt(Optional.ofNullable(System.getenv("SERVICE_PORT")).orElse("80")); String serviceRoot = Optional.ofNullable(System.getenv("SERVICE_ROOT")).orElse("/api"); String color = Optional.ofNullable(System.getenv("COLOR")).orElse("FFD433"); // create the microservice record record = HttpEndpoint.createRecord( serviceName, serviceHost, servicePort, serviceRoot ); } }

Slide 19

Slide 19 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer - Record public class BaseStar extends AbstractVerticle { public void start() { // microservice options Haikunator haikunator = new HaikunatorBuilder().setTokenLength(6).build(); String niceName = haikunator.haikunate(); String serviceName = Optional.ofNullable(System.getenv("SERVICE_NAME")).orElse("the-plan")+"-"+niceName; String serviceHost = Optional.ofNullable(System.getenv("SERVICE_HOST")).orElse("localhost"); Integer servicePort = Integer.parseInt(Optional.ofNullable(System.getenv("SERVICE_PORT")).orElse("80")); String serviceRoot = Optional.ofNullable(System.getenv("SERVICE_ROOT")).orElse("/api"); String color = Optional.ofNullable(System.getenv("COLOR")).orElse("FFD433"); // create the microservice record record = HttpEndpoint.createRecord( serviceName, serviceHost, servicePort, serviceRoot ); } }

Slide 20

Slide 20 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer - MetaData public class BaseStar extends AbstractVerticle { public void start() { // add some metadata record.setMetadata(new JsonObject() .put("kind", "basestar") .put("message", "Hello ") .put("uri", "/coordinates") .put("raiders_counter", raidersCounter) .put("color", color) .put("app_id", Optional.ofNullable(System.getenv("APP_ID")).orElse("")) .put("instance_id", Optional.ofNullable(System.getenv("INSTANCE_ID")).orElse("")) .put("instance_type", Optional.ofNullable(System.getenv("INSTANCE_TYPE")).orElse("production")) .put("instance_number", Integer.parseInt(Optional.ofNullable(System.getenv("INSTANCE_NUMBER")).orElse("0"))) ); } }

Slide 21

Slide 21 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer & démarrer public class BaseStar extends AbstractVerticle { public void start() { Integer httpPort = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { if(result.succeeded()) { System.out.println(" Listening on " + httpPort); //publish the microservice to the discovery backend discovery.publish(record, asyncResult -> { if(asyncResult.succeeded()) { System.out.println("" + record.getRegistration()); } else { System.out.println("" + asyncResult.cause().getMessage()); } }); } else { System.out.println(" Houston, we have a problem: " + result.cause().getMessage()); } }); } }

Slide 22

Slide 22 text

#BSGTHEPLAN @DevFestToulouse s’enregistrer & démarrer public class BaseStar extends AbstractVerticle { public void start() { Integer httpPort = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { if(result.succeeded()) { System.out.println(" Listening on " + httpPort); //publish the microservice to the discovery backend discovery.publish(record, asyncResult -> { if(asyncResult.succeeded()) { System.out.println("" + record.getRegistration()); } else { System.out.println("" + asyncResult.cause().getMessage()); } }); } else { System.out.println(" Houston, we have a problem: " + result.cause().getMessage()); } }); } }

Slide 23

Slide 23 text

#BSGTHEPLAN @DevFestToulouse BaseStar Routes | Router

Slide 24

Slide 24 text

#BSGTHEPLAN @DevFestToulouse Routes | Router public class BaseStar extends AbstractVerticle { public void start() { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.get("/api/raiders").handler(context -> { discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if (ar.succeeded()) { context.response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonArray(ar.result()).encodePrettily()); } }); }); // serve static assets, see /resources/webroot directory router.route("/*").handler(StaticHandler.create()); Integer httpPort = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { }); } }

Slide 25

Slide 25 text

#BSGTHEPLAN @DevFestToulouse GET | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.get("/api/raiders").handler(context -> { discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if (ar.succeeded()) { context.response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonArray(ar.result()).encodePrettily()); } }); }); // serve static assets, see /resources/webroot directory router.route("/*").handler(StaticHandler.create()); Integer httpPort = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { }); } }

Slide 26

Slide 26 text

#BSGTHEPLAN @DevFestToulouse GET | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.get("/api/raiders").handler(context -> { discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if (ar.succeeded()) { context.response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonArray(ar.result()).encodePrettily()); } }); }); // serve static assets, see /resources/webroot directory router.route("/*").handler(StaticHandler.create()); Integer httpPort = Integer.parseInt(Optional.ofNullable(System.getenv("PORT")).orElse("8080")); HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { }); } }

Slide 27

Slide 27 text

#BSGTHEPLAN @DevFestToulouse “Enregistrer” les raiders

Slide 28

Slide 28 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 29

Slide 29 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 30

Slide 30 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 31

Slide 31 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 32

Slide 32 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 33

Slide 33 text

#BSGTHEPLAN @DevFestToulouse POST | /api/raiders public class BaseStar extends AbstractVerticle { public void start() { router.post("/api/raiders").handler(context -> { // I'm a new raider String registationId = Optional.ofNullable(context.getBodyAsJson().getString("registration")).orElse("unknown"); discovery.getRecord(r -> r.getRegistration().equals(registationId), asyncResRecord -> { // = raider's record if(asyncResRecord.succeeded()) { Record raiderRecord = asyncResRecord.result(); ServiceReference reference = discovery.getReference(raiderRecord); WebClient raiderClient = reference.getAs(WebClient.class); // ⚠ get a web client this.raidersCounter += 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, ar -> { }); this.raiderWorker(raiderRecord,raiderClient); // this is a worker context.response() // ✉ message to the raider .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "ok, registered").encodePrettily()); } }); }); } }

Slide 34

Slide 34 text

#BSGTHEPLAN @DevFestToulouse BaseStar HealthCheck ❤

Slide 35

Slide 35 text

#BSGTHEPLAN @DevFestToulouse HealthCheck ❤ Hey, toujours là ?

Slide 36

Slide 36 text

#BSGTHEPLAN @DevFestToulouse HealthCheck ❤ public class BaseStar extends AbstractVerticle { public void start() { // health check of existing basestars HealthCheckHandler hch = HealthCheckHandler.create(vertx); hch.register("iamok", future -> discovery.getRecord(r -> r.getRegistration().equals(record.getRegistration()), ar -> { if(ar.succeeded()) { future.complete(); } else { // future.fail(ar.cause()); } }) ); router.get("/health").handler(hch); } }

Slide 37

Slide 37 text

#BSGTHEPLAN @DevFestToulouse HealthCheck ❤ public class BaseStar extends AbstractVerticle { public void start() { // health check of existing basestars HealthCheckHandler hch = HealthCheckHandler.create(vertx); hch.register("iamok", future -> discovery.getRecord(r -> r.getRegistration().equals(record.getRegistration()), ar -> { if(ar.succeeded()) { future.complete(); } else { // future.fail(ar.cause()); } }) ); router.get("/health").handler(hch); } }

Slide 38

Slide 38 text

#BSGTHEPLAN @DevFestToulouse HealthCheck ❤ public class BaseStar extends AbstractVerticle { public void start() { // health check of existing basestars HealthCheckHandler hch = HealthCheckHandler.create(vertx); hch.register("iamok", future -> discovery.getRecord(r -> r.getRegistration().equals(record.getRegistration()), ar -> { if(ar.succeeded()) { future.complete(); } else { // future.fail(ar.cause()); } }) ); router.get("/health").handler(hch); } }

Slide 39

Slide 39 text

#BSGTHEPLAN @DevFestToulouse BaseStar Worker this.raiderWorker(raiderRecord,raiderClient); // this is a worker

Slide 40

Slide 40 text

#BSGTHEPLAN @DevFestToulouse Worker private void raiderWorker(Record raiderRecord, WebClient raiderClient) { Raider thatRaider = new Raider( raiderRecord.getRegistration(), raiderRecord.getName(), raiderRecord.getMetadata().getJsonObject("coordinates").getDouble("x"), raiderRecord.getMetadata().getJsonObject("coordinates").getDouble("y"), new Constraints(5.0, 600.0, 600.0, 5.0) ); //… }

Slide 41

Slide 41 text

#BSGTHEPLAN @DevFestToulouse Worker private void raiderWorker(Record raiderRecord, WebClient raiderClient) { //… vertx.setPeriodic(1000, timerID -> { // this is a worker // get the raiders list discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if(ar.succeeded()) { List raidersRecords = ar.result(); thatRaider.moveWith(raidersRecords, 300.0); thatRaider.moveCloser(raidersRecords, 300.0); thatRaider.moveAway(raidersRecords, 15.0); thatRaider.move(); // === try to contact the raider and post coordinates === raiderClient.post("/api/coordinates").sendJsonObject( new JsonObject() .put("x",thatRaider.x) .put("y",thatRaider.y) .put("xVelocity",thatRaider.xVelocity) .put("yVelocity",thatRaider.yVelocity), asyncPostRes -> { if(asyncPostRes.succeeded()) { System.out.println(" " + asyncPostRes.result().bodyAsJsonObject().encodePrettily()); } else { // ouch raidersCounter -= 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, asyncRecUpdateRes -> { vertx.cancelTimer(timerID);}); } } ); }); }); }

Slide 42

Slide 42 text

#BSGTHEPLAN @DevFestToulouse Worker private void raiderWorker(Record raiderRecord, WebClient raiderClient) { //… vertx.setPeriodic(1000, timerID -> { // this is a worker // get the raiders list discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if(ar.succeeded()) { List raidersRecords = ar.result(); thatRaider.moveWith(raidersRecords, 300.0); thatRaider.moveCloser(raidersRecords, 300.0); thatRaider.moveAway(raidersRecords, 15.0); thatRaider.move(); // === try to contact the raider and post coordinates === raiderClient.post("/api/coordinates").sendJsonObject( new JsonObject() .put("x",thatRaider.x) .put("y",thatRaider.y) .put("xVelocity",thatRaider.xVelocity) .put("yVelocity",thatRaider.yVelocity), asyncPostRes -> { if(asyncPostRes.succeeded()) { System.out.println(" " + asyncPostRes.result().bodyAsJsonObject().encodePrettily()); } else { // ouch raidersCounter -= 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, asyncRecUpdateRes -> { vertx.cancelTimer(timerID);}); } } ); }); }); }

Slide 43

Slide 43 text

#BSGTHEPLAN @DevFestToulouse Worker private void raiderWorker(Record raiderRecord, WebClient raiderClient) { //… vertx.setPeriodic(1000, timerID -> { // this is a worker // get the raiders list discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if(ar.succeeded()) { List raidersRecords = ar.result(); thatRaider.moveWith(raidersRecords, 300.0); thatRaider.moveCloser(raidersRecords, 300.0); thatRaider.moveAway(raidersRecords, 15.0); thatRaider.move(); // === try to contact the raider and post coordinates === raiderClient.post("/api/coordinates").sendJsonObject( new JsonObject() .put("x",thatRaider.x) .put("y",thatRaider.y) .put("xVelocity",thatRaider.xVelocity) .put("yVelocity",thatRaider.yVelocity), asyncPostRes -> { if(asyncPostRes.succeeded()) { System.out.println(" " + asyncPostRes.result().bodyAsJsonObject().encodePrettily()); } else { // ouch raidersCounter -= 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, asyncRecUpdateRes -> { vertx.cancelTimer(timerID);}); } } ); }); }); }

Slide 44

Slide 44 text

#BSGTHEPLAN @DevFestToulouse Worker private void raiderWorker(Record raiderRecord, WebClient raiderClient) { //… vertx.setPeriodic(1000, timerID -> { // this is a worker // get the raiders list discovery.getRecords(r -> r.getMetadata().getString("kind").equals("raider") , ar -> { if(ar.succeeded()) { List raidersRecords = ar.result(); thatRaider.moveWith(raidersRecords, 300.0); thatRaider.moveCloser(raidersRecords, 300.0); thatRaider.moveAway(raidersRecords, 15.0); thatRaider.move(); // === try to contact the raider and post coordinates === raiderClient.post("/api/coordinates").sendJsonObject( new JsonObject() .put("x",thatRaider.x) .put("y",thatRaider.y) .put("xVelocity",thatRaider.xVelocity) .put("yVelocity",thatRaider.yVelocity), asyncPostRes -> { if(asyncPostRes.succeeded()) { System.out.println(" " + asyncPostRes.result().bodyAsJsonObject().encodePrettily()); } else { // ouch raidersCounter -= 1; record.getMetadata().put("raiders_counter", raidersCounter); discovery.update(record, asyncRecUpdateRes -> { vertx.cancelTimer(timerID);}); } } ); }); }); }

Slide 45

Slide 45 text

#BSGTHEPLAN @DevFestToulouse Récapitulatif BaseStar JAVA 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 Raider KOTLIN Raider KOTLIN Raider GROOVY Raider GROOVY POST http://localhost:909N/api/coordinates

Slide 46

Slide 46 text

#BSGTHEPLAN @DevFestToulouse checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS & health ✅ Démarrer Redis

Slide 47

Slide 47 text

#BSGTHEPLAN @DevFestToulouse WebApp Scala + JavaScript

Slide 48

Slide 48 text

#BSGTHEPLAN @DevFestToulouse 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

Slide 49

Slide 49 text

#BSGTHEPLAN @DevFestToulouse 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) ) )

Slide 50

Slide 50 text

#BSGTHEPLAN @DevFestToulouse WebApp | Backend Scala val httpPort = sys.env.getOrElse("PORT", "8080").toInt router.get("/api/raiders").handler(context => { discovery .getRecordsFuture(record => record.getMetadata.getString("kind").equals("raider")) .onComplete { case Success(results) => { context .response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonArray(results.toList.asJava).encodePrettily()) } case Failure(cause) => { context .response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("error", cause.getMessage).encodePrettily()) } } }) router.route("/*").handler(StaticHandler.create()) println(s" Listening on $httpPort - Enjoy ") server.requestHandler(router.accept _).listen(httpPort)

Slide 51

Slide 51 text

#BSGTHEPLAN @DevFestToulouse WebApp | Backend Scala val httpPort = sys.env.getOrElse("PORT", "8080").toInt router.get("/api/raiders").handler(context => { discovery .getRecordsFuture(record => record.getMetadata.getString("kind").equals("raider")) .onComplete { case Success(results) => { context .response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonArray(results.toList.asJava).encodePrettily()) } case Failure(cause) => { context .response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("error", cause.getMessage).encodePrettily()) } } }) router.route("/*").handler(StaticHandler.create()) println(s" Listening on $httpPort - Enjoy ") server.requestHandler(router.accept _).listen(httpPort)

Slide 52

Slide 52 text

#BSGTHEPLAN @DevFestToulouse pour la forme, la version node rien à voir avec vert.x

Slide 53

Slide 53 text

#BSGTHEPLAN @DevFestToulouse WebApp | Backend Node const express = require("express"); const bodyParser = require("body-parser"); const fetch = require('node-fetch'); let port = process.env.PORT || 7070; let app = express(); app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: false})) app.use(express.static('public')); app.get('/raiders', (req, res) => { //http://localhost:8080/api/raiders fetch(`${process.env.RAIDERS_SERVICE}`, { method:'GET', headers: {"Content-Type": "application/json;charset=UTF-8"} }) .then(response => response.json()) .then(jsonData => res.send({raiders: jsonData})) .catch(error => res.send({error: error})) }) app.listen(port) console.log(" Discovery Server is started - listening on ", port)

Slide 54

Slide 54 text

#BSGTHEPLAN @DevFestToulouse WebApp | Frontend HTML/JS

Slide 55

Slide 55 text

#BSGTHEPLAN @DevFestToulouse checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map

Slide 56

Slide 56 text

#BSGTHEPLAN @DevFestToulouse Raider(s)

Slide 57

Slide 57 text

#BSGTHEPLAN @DevFestToulouse Raider(s) 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 Raider GROOVY Raider KOTLIN Raider GROOVY

Slide 58

Slide 58 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | main + stop kotlin

Slide 59

Slide 59 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | main + stop fun main(args: Array) { val vertx = Vertx.vertx() vertx.deployVerticle(Raider()) } class Raider : AbstractVerticle() { override fun stop(stopFuture: Future) { super.stop() println("Unregistration process is started (${record?.registration})...") discovery?.unpublish(record?.registration, { ar -> when { ar.failed() -> { println(" Unable to unpublish the microservice: ${ar.cause().message}") stopFuture.fail(ar.cause()) } ar.succeeded() -> { println(" bye bye ${record?.registration}") stopFuture.complete() } } }) } override fun start() { … } }

Slide 60

Slide 60 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | main + stop fun main(args: Array) { val vertx = Vertx.vertx() vertx.deployVerticle(Raider()) } class Raider : AbstractVerticle() { override fun stop(stopFuture: Future) { super.stop() println("Unregistration process is started (${record?.registration})...") discovery?.unpublish(record?.registration, { ar -> when { ar.failed() -> { println(" Unable to unpublish the microservice: ${ar.cause().message}") stopFuture.fail(ar.cause()) } ar.succeeded() -> { println(" bye bye ${record?.registration}") stopFuture.complete() } } }) } override fun start() { … } }

Slide 61

Slide 61 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | start / discovery kotlin

Slide 62

Slide 62 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | s’enregistrer / ServiceDiscovery class Raider : AbstractVerticle() { override fun start() { /* === Discovery part === */ val redisPort= System.getenv("REDIS_PORT")?.toInt() ?: 6379 val redisHost = System.getenv("REDIS_HOST") ?: "127.0.0.1" val redisAuth = System.getenv("REDIS_PASSWORD") ?: null val redisRecordsKey = System.getenv("REDIS_RECORDS_KEY") ?: "vert.x.ms" val serviceDiscoveryOptions = ServiceDiscoveryOptions() discovery = ServiceDiscovery.create(vertx, serviceDiscoveryOptions.setBackendConfiguration( json { obj( "host" to redisHost, "port" to redisPort, "auth" to redisAuth, "key" to redisRecordsKey ) } )) } }

Slide 63

Slide 63 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | s’enregistrer / Record class Raider : AbstractVerticle() { override fun start() { // microservice informations val haikunator = HaikunatorBuilder().setTokenLength(3).build() val niceName = haikunator.haikunate() val serviceName = "${System.getenv("SERVICE_NAME") ?: "the-plan"}-$niceName" val serviceHost = System.getenv("SERVICE_HOST") ?: "localhost" val servicePort = System.getenv("SERVICE_PORT")?.toInt() ?: 80 val serviceRoot = System.getenv("SERVICE_ROOT") ?: "/api" // create the microservice record record = HttpEndpoint.createRecord( serviceName, serviceHost, servicePort, serviceRoot ) } }

Slide 64

Slide 64 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | s’enregistrer / metadata class Raider : 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")) ) } } }

Slide 65

Slide 65 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | s’enregistrer / démarrer class Raider : AbstractVerticle() { override fun start() { val httpPort = System.getenv("PORT")?.toInt() ?: 8080 vertx.createHttpServer(HttpServerOptions(port = httpPort)) .requestHandler { router.accept(it) } .listen { ar -> when { ar.failed() -> println(" Houston?") ar.succeeded() -> { println(" Raider started on $httpPort") /* === publish the microservice record to the discovery backend === */ discovery?.publish(record, { asyncRes -> when { asyncRes.failed() -> println(" ${asyncRes.cause().message}") asyncRes.succeeded() -> { println(" ${asyncRes.result().registration}") /* === search for a baseStar === */ searchAndSelectOneBaseStar() } } }) /* === end of publish === } } } } }

Slide 66

Slide 66 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | s’enregistrer / démarrer class Raider : AbstractVerticle() { override fun start() { val httpPort = System.getenv("PORT")?.toInt() ?: 8080 vertx.createHttpServer(HttpServerOptions(port = httpPort)) .requestHandler { router.accept(it) } .listen { ar -> when { ar.failed() -> println(" Houston?") ar.succeeded() -> { println(" Raider started on $httpPort") /* === publish the microservice record to the discovery backend === */ discovery?.publish(record, { asyncRes -> when { asyncRes.failed() -> println(" ${asyncRes.cause().message}") asyncRes.succeeded() -> { println(" ${asyncRes.result().registration}") /* === search for a baseStar === */ searchAndSelectOneBaseStar() } } }) /* === end of publish === } } } } }

Slide 67

Slide 67 text

#BSGTHEPLAN @DevFestToulouse Raider Routes | Router

Slide 68

Slide 68 text

#BSGTHEPLAN @DevFestToulouse 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()) }

Slide 69

Slide 69 text

#BSGTHEPLAN @DevFestToulouse 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()) }

Slide 70

Slide 70 text

#BSGTHEPLAN @DevFestToulouse Raider HealthCheck ❤

Slide 71

Slide 71 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | healthcheck /* === health check === */ healthCheck = HealthCheckHandler.create(vertx) healthCheck?.register("iamok",{ future -> discovery?.getRecord({ r -> r.registration == record?.registration}, { asyncRes -> when { asyncRes.failed() -> future.fail(asyncRes.cause()) asyncRes.succeeded() -> future.complete(Status.OK()) } }) })

Slide 72

Slide 72 text

#BSGTHEPLAN @DevFestToulouse Raider CircuitBreaker

Slide 73

Slide 73 text

#BSGTHEPLAN @DevFestToulouse CircuitBreaker toc toc Penny ? toc toc Penny ? toc toc Penny ? ok j’arrête

Slide 74

Slide 74 text

#BSGTHEPLAN @DevFestToulouse Raider(s) | circuitbreaker /* === Define a circuit breaker === */ breaker = CircuitBreaker.create("bsg-circuit-breaker", vertx, CircuitBreakerOptions( maxFailures = 5, timeout = 20000, fallbackOnFailure = true, resetTimeout = 100000))

Slide 75

Slide 75 text

#BSGTHEPLAN @DevFestToulouse Raider /* === search for a baseStar === */ searchAndSelectOneBaseStar()

Slide 76

Slide 76 text

#BSGTHEPLAN @DevFestToulouse Raider(s) fun searchAndSelectOneBaseStar() { /* === search for a baseStar in the discovery backend === */ discovery?.getRecords( {r -> r.metadata.getString("kind") == "basestar" && r.status == io.vertx.servicediscovery.Status.UP }, { asyncResult -> when { asyncResult.failed() -> { } // asyncResult.succeeded() -> { // val baseStarsRecords = asyncResult.result() // === choose randomly a basestar === baseStarsRecords.size.let { when(it) { 0 -> { /* oh oh no basestar online ?!!! */ } else -> { val selectedRecord = baseStarsRecords.get(Random().nextInt(it)) subscribeToBaseStar(selectedRecord) // } } } } } } ) // ⬅ end of the discovery }

Slide 77

Slide 77 text

#BSGTHEPLAN @DevFestToulouse Raider(s) fun searchAndSelectOneBaseStar() { /* === search for a baseStar in the discovery backend === */ discovery?.getRecords( {r -> r.metadata.getString("kind") == "basestar" && r.status == io.vertx.servicediscovery.Status.UP }, { asyncResult -> when { asyncResult.failed() -> { } // asyncResult.succeeded() -> { // val baseStarsRecords = asyncResult.result() // === choose randomly a basestar === baseStarsRecords.size.let { when(it) { 0 -> { /* oh oh no basestar online ?!!! */ } else -> { val selectedRecord = baseStarsRecords.get(Random().nextInt(it)) subscribeToBaseStar(selectedRecord) // } } } } } } ) // ⬅ end of the discovery }

Slide 78

Slide 78 text

#BSGTHEPLAN @DevFestToulouse Raider subscribeToBaseStar(selectedRecord) //

Slide 79

Slide 79 text

#BSGTHEPLAN @DevFestToulouse 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({ 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 -> }) }

Slide 80

Slide 80 text

#BSGTHEPLAN @DevFestToulouse 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({ 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 -> }) }

Slide 81

Slide 81 text

#BSGTHEPLAN @DevFestToulouse 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({ 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 -> }) }

Slide 82

Slide 82 text

#BSGTHEPLAN @DevFestToulouse 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({ 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 -> }) } ⚠⚠⚠

Slide 83

Slide 83 text

#BSGTHEPLAN @DevFestToulouse Raider // time to check the health of my basestar watchingMyBaseStar(selectedBaseStar)

Slide 84

Slide 84 text

#BSGTHEPLAN @DevFestToulouse 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 === } } } }) }

Slide 85

Slide 85 text

#BSGTHEPLAN @DevFestToulouse 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 === } } } }) }

Slide 86

Slide 86 text

#BSGTHEPLAN @DevFestToulouse 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 === } } } }) }

Slide 87

Slide 87 text

#BSGTHEPLAN @DevFestToulouse 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 === } } } }) }

Slide 88

Slide 88 text

#BSGTHEPLAN @DevFestToulouse Raider(s) groovy

Slide 89

Slide 89 text

#BSGTHEPLAN @DevFestToulouse Raider(s) // call by a basestar router.post("/api/coordinates").handler { context -> def computedX = context.bodyAsJson.getDouble("x") ?: x def computedY = context.bodyAsJson.getDouble("y") ?: y def computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity def 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", [ "name:" : baseStar.record.name, "color" : baseStar.record.metadata.getString("color") ]) discovery.update(record, {asyncUpdateResult -> }) context .response() .putHeader("content-type", "application/json;charset=UTF-8") .end(JsonOutput.toJson([ "message" : "", "x" : computedX, "y" : computedY ])) }

Slide 90

Slide 90 text

#BSGTHEPLAN @DevFestToulouse Récapitulatif 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 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

Slide 91

Slide 91 text

#BSGTHEPLAN @DevFestToulouse checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map ✅ Démarrer Raiders (kotlin) ✅ Démarrer Raiders (groovy) ✅ Tuer des BaseStars ✅ Ajouter des BaseStars

Slide 92

Slide 92 text

#BSGTHEPLAN @DevFestToulouse Faker Raider(s)

Slide 93

Slide 93 text

#BSGTHEPLAN @DevFestToulouse ServiceDiscoveryRestEndpoint.java // use me with other microservices ServiceDiscoveryRestEndpoint.create(router, discovery); http://localhost:8081/discovery #BSGTHEPLAN @DevFestToulouse

Slide 94

Slide 94 text

#BSGTHEPLAN @DevFestToulouse checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map ✅ Démarrer Raiders (kotlin) ✅ Démarrer Raiders (groovy) ✅ Tuer des BaseStars ✅ Ajouter des BaseStars ✅ http://localhost:8080/discovery ✅ Démarrer Fake-Raider (Node) ✅ http://localhost:8083/discovery

Slide 95

Slide 95 text

#BSGTHEPLAN @DevFestToulouse Créer son Backend

Slide 96

Slide 96 text

#BSGTHEPLAN @DevFestToulouse

Slide 97

Slide 97 text

#BSGTHEPLAN @DevFestToulouse https://github.com/botsgarden/call-simple-microservice https://github.com/botsgarden/vertx-service-discovery-backend-http http://k33g.github.io/2017/06/29/HTTP-BACKEND-DISCOVERY.html

Slide 98

Slide 98 text

#BSGTHEPLAN @DevFestToulouse Conclusion(s) - Remarques - Perspectives #BSGTHEPLAN @DevFestToulouse

Slide 99

Slide 99 text

#BSGTHEPLAN @DevFestToulouse 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

Slide 100

Slide 100 text

#BSGTHEPLAN @DevFestToulouse Merci D • Vous • DevFest Toulouse • Clément Escoffier | Julien Viet | Julien Ponge