Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Plan v3 pour BDX.io

The Plan v3 pour BDX.io

Avatar for Philippe CHARRIERE

Philippe CHARRIERE

November 10, 2017
Tweet

More Decks by Philippe CHARRIERE

Other Decks in Programming

Transcript

  1. #BSGTHEPLAN #BDXIO17 bonjour Philippe Charrière @clever_cloud ! @k33g_org G @k33g

    I ❤JavaScript, I Java, I JVM, I Scala (depuis peu…)
  2. #BSGTHEPLAN #BDXIO17 Les origines de ce talk? Démos clients Monter

    en compétence sur les microservices (et vert.x) partager ce que j’ai appris (~ tuto) https://github.com/the-plan
  3. #BSGTHEPLAN #BDXIO17 –Philippe Charrière “Les microservices sont des morceaux d’application(s)

    développés de manière indépendante, déployés de manière indépendante et gérés par de petites équipes ” “Un microservice est petit, car il résout un seul problème”
  4. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  5. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  6. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  7. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  8. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  9. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  10. #BSGTHEPLAN #BDXIO17 Les qualités d’un microservice ? Avoir un seul

    objectif Encapsulation Appropriation par l’équipe Autonomie de l’équipe Versions multiples Chorégraphie Eventual consistency (cohérence à terme)
  11. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertise élevés par équipe Externalisation plus facile Securité
  12. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertise élevés par équipe Externalisation plus facile Securité
  13. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertise élevés par équipe Externalisation plus facile Securité
  14. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertises élevés par équipe Externalisation plus facile Securité
  15. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertises élevés par équipe Externalisation plus facile Securité
  16. #BSGTHEPLAN #BDXIO17 Pourquoi c’est bien ? Time to Market plus

    rapide Code moins compliqué et meilleur Responsabilité Des niveaux d’expertises élevés par équipe Externalisation plus facile Sécurité
  17. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  18. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  19. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  20. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  21. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  22. #BSGTHEPLAN #BDXIO17 Pourquoi ce n’est pas toujours simple ? Complexité

    externe Maturité “organisationnelle” Duplication (multiplication) Eventual consistency (cohérence à terme) Testing, simple … ou pas Monitoring
  23. #BSGTHEPLAN #BDXIO17 Pourquoi Vert.x Pas besoin de serveur d’application, de

    servlet Simple (à utiliser, à coder, à builder, à déployer) Polyglotte Comme à la maison, on dirait du #JavaScript Asynchrone Une dream team
  24. #BSGTHEPLAN #BDXIO17 microservices Vert.x En ce qui concerne la partie

    Microservices Agnostique sur la partie Discovery Léger Des composants pour une architecture stable & résilience: Health Checks Circuit Breakers …
  25. #BSGTHEPLAN #BDXIO17 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); }); } }
  26. #BSGTHEPLAN #BDXIO17 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
  27. #BSGTHEPLAN #BDXIO17 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) )); } }
  28. #BSGTHEPLAN #BDXIO17 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) )); } }
  29. #BSGTHEPLAN #BDXIO17 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 ); } }
  30. #BSGTHEPLAN #BDXIO17 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"))) ); } }
  31. #BSGTHEPLAN #BDXIO17 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()); } }); } }
  32. #BSGTHEPLAN #BDXIO17 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()); } }); } }
  33. #BSGTHEPLAN #BDXIO17 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 -> { }); } }
  34. #BSGTHEPLAN #BDXIO17 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 -> { }); } }
  35. #BSGTHEPLAN #BDXIO17 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()); } }); }); } }
  36. #BSGTHEPLAN #BDXIO17 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()); } }); }); } }
  37. #BSGTHEPLAN #BDXIO17 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()); } }); }); } }
  38. #BSGTHEPLAN #BDXIO17 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()); } }); }); } }
  39. #BSGTHEPLAN #BDXIO17 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()); } }); }); } }
  40. #BSGTHEPLAN #BDXIO17 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); } }
  41. #BSGTHEPLAN #BDXIO17 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); } }
  42. #BSGTHEPLAN #BDXIO17 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); } }
  43. #BSGTHEPLAN #BDXIO17 BaseStar Worker this.raiderWorker(raiderRecord,raiderClient); // this is a worker

    router.post("/api/raiders").handler(context -> { // I'm a new raider
  44. #BSGTHEPLAN #BDXIO17 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) ); //… }
  45. #BSGTHEPLAN #BDXIO17 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<Record> 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);}); } } ); }); }); }
  46. #BSGTHEPLAN #BDXIO17 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<Record> 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);}); } } ); }); }); }
  47. #BSGTHEPLAN #BDXIO17 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<Record> 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);}); } } ); }); }); }
  48. #BSGTHEPLAN #BDXIO17 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<Record> 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);}); } } ); }); }); }
  49. #BSGTHEPLAN #BDXIO17 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 Hey pilotage
  50. #BSGTHEPLAN #BDXIO17 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
  51. #BSGTHEPLAN #BDXIO17 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)
  52. #BSGTHEPLAN #BDXIO17 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)
  53. #BSGTHEPLAN #BDXIO17 checklist ✅ Démarrer les BaseStars ✅ cf Base

    Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map
  54. #BSGTHEPLAN #BDXIO17 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
  55. #BSGTHEPLAN #BDXIO17 Raider(s) | main + stop fun main(args: Array<String>)

    { val vertx = Vertx.vertx() vertx.deployVerticle(Raider()) } class Raider : AbstractVerticle() { override fun stop(stopFuture: Future<Void>) { 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() { … } }
  56. #BSGTHEPLAN #BDXIO17 Raider(s) | main + stop fun main(args: Array<String>)

    { val vertx = Vertx.vertx() vertx.deployVerticle(Raider()) } class Raider : AbstractVerticle() { override fun stop(stopFuture: Future<Void>) { 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() { … } }
  57. #BSGTHEPLAN #BDXIO17 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 ) } )) } }
  58. #BSGTHEPLAN #BDXIO17 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 ) } }
  59. #BSGTHEPLAN #BDXIO17 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")) ) } } }
  60. #BSGTHEPLAN #BDXIO17 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 === } } } } }
  61. #BSGTHEPLAN #BDXIO17 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 === } } } } }
  62. #BSGTHEPLAN #BDXIO17 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()) }
  63. #BSGTHEPLAN #BDXIO17 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()) }
  64. #BSGTHEPLAN #BDXIO17 Raider(s) | circuitbreaker /* === Define a circuit

    breaker === */ breaker = CircuitBreaker.create("bsg-circuit-breaker", vertx, CircuitBreakerOptions( maxFailures = 5, timeout = 20000, fallbackOnFailure = true, resetTimeout = 100000))
  65. #BSGTHEPLAN #BDXIO17 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 -> { searchAndSelectOneBaseStar()/* oh oh no basestar online ?!!! */ } else -> { val selectedRecord = baseStarsRecords.get(Random().nextInt(it)) subscribeToBaseStar(selectedRecord) // } } } } } } ) // ⬅ end of the discovery }
  66. #BSGTHEPLAN #BDXIO17 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 -> { searchAndSelectOneBaseStar() /* oh oh no basestar online ?!!! */ } else -> { val selectedRecord = baseStarsRecords.get(Random().nextInt(it)) subscribeToBaseStar(selectedRecord) // } } } } } } ) // ⬅ end of the discovery }
  67. #BSGTHEPLAN #BDXIO17 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 -> }) }
  68. #BSGTHEPLAN #BDXIO17 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 -> }) }
  69. #BSGTHEPLAN #BDXIO17 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 -> }) } ⚠⚠⚠
  70. #BSGTHEPLAN #BDXIO17 Raider // time to check the health of

    my basestar watchingMyBaseStar(selectedBaseStar)
  71. #BSGTHEPLAN #BDXIO17 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 === } } } }) }
  72. #BSGTHEPLAN #BDXIO17 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 === } } } }) }
  73. #BSGTHEPLAN #BDXIO17 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 === } } } }) }
  74. #BSGTHEPLAN #BDXIO17 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 === } } } }) }
  75. #BSGTHEPLAN #BDXIO17 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
  76. #BSGTHEPLAN #BDXIO17 checklist ✅ Démarrer les BaseStars ✅ cf Base

    Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map ✅ Démarrer Raiders (kotlin) ✅ Tuer des BaseStars
  77. #BSGTHEPLAN #BDXIO17 checklist ✅ Démarrer les BaseStars ✅ cf Base

    Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map ✅ Démarrer Raiders (kotlin) ✅ Tuer des BaseStars ✅ http://localhost:8080/discovery ✅ Démarrer Fake-Raider (Node) ✅ Démarrer Raiders (groovy) ✅ Ajouter des BaseStars
  78. #BSGTHEPLAN #BDXIO17 Perspectives • Déploiement sur le cloud, j’ai eu

    des surprises / API Management • 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 • Sécurité - Authentification https://github.com/botsgarden/vertx-service-discovery-backend-redisson
  79. #BSGTHEPLAN #BDXIO17 Merci D • Vous • BDX.io • Clément

    Escoffier | Julien Viet | Julien Ponge