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

The Plan v3 pour BDX.io

The Plan v3 pour BDX.io

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