SlideShare a Scribd company logo
1 of 100
Download to read offline
#BSGTHEPLAN #BlendWebMix
The Plan
#BSGTHEPLAN #BlendWebMix
bonjour
Philippe Charrière
🕺@clever_cloud
! @k33g_org 👈
G @k33g
I ❤JavaScript, I 💔 Java, I 💚 JVM, I 💖 Golo, I 💙 Scala (depuis peu…)
#BSGTHEPLAN #BlendWebMix
avertissements
Je ne suis pas un développeur Java … Enfin je ne crois pas
#BSGTHEPLAN #BlendWebMix
pourquoi ce talk?
Trouver un sujet de talk
Le préparer pendant les heures de travail (démos)
monter en compétence sur les microservices (et vert.x)
partager ce que j’ai appris
#BSGTHEPLAN #BlendWebMix
Plan de “the plan”
Intro - microservices
anatomie du plan
#BSGTHEPLAN #BlendWebMix
microservices?
Une application à base de microservices =
ensemble de services autonomes
Ces services collaborent pour exécuter des
tâches complexes
Protocoles de messages standard
Mode direct ou asynchrone
#BSGTHEPLAN #BlendWebMix
micro, m’ouais …
Distribuer les fonctionnalités et les données
Identifier les microservices, les trouver, …
Gérer la cohérence et la fiabilité du réseau pour chacun
Composants distribués + Technologies différentes = “new modes of failure”
Quand ça ne marche pas … 😡 Complexité d’investigation
#BSGTHEPLAN #BlendWebMix
microservices vert.x 😍
Simplicité, Polyglotte
C’est un framework simple: Maîtrise de ce que vous
faites
BackendDiscovery / Service Discovery (ex: Hazelcast,
Redis, …)
Stabilité & Résilience
Circuit Breakers
Health Checks
#BSGTHEPLAN #BlendWebMix
1 microservice REST Vert.x
public class BaseStar extends AbstractVerticle {
public void start() {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.get("/api/hello").handler(context -> {
context.response()
.putHeader("content-type", "application/json;charset=UTF-8")
.end(new JsonObject().put("message", "hello").encodePrettily());
});
Integer httpPort = 8080;
HttpServer server = vertx.createHttpServer();
server.requestHandler(router::accept).listen(httpPort, result -> {
System.out.println("🌍 Listening on " + httpPort);
});
}
}
#BSGTHEPLAN #BlendWebMix
Le Plan
Redis + http
#BSGTHEPLAN #BlendWebMix
#BSGTHEPLAN #BlendWebMix
objectifs
#BSGTHEPLAN #BlendWebMix
#BSGTHEPLAN #BlendWebMix
Plan de “the plan”
Intro - microservices
anatomie du plan
BaseStars
WebApp
Raiders
Conclusion(s)
#BSGTHEPLAN #BlendWebMix
BaseStar
Java
#BSGTHEPLAN #BlendWebMix
BaseStar
Service Discovery & Backend
#BSGTHEPLAN #BlendWebMix
s’enregistrer
BaseStar
JAVA
BaseStar
JAVA
BaseStar
JAVA
BaseStar
JAVA
REDIS
BaseStar
JAVA
#BSGTHEPLAN #BlendWebMix
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)
));
}
}
#BSGTHEPLAN #BlendWebMix
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)
));
}
}
#BSGTHEPLAN #BlendWebMix
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
);
}
}
#BSGTHEPLAN #BlendWebMix
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
);
}
}
#BSGTHEPLAN #BlendWebMix
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")))
);
}
}
#BSGTHEPLAN #BlendWebMix
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());
}
});
}
}
#BSGTHEPLAN #BlendWebMix
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());
}
});
}
}
#BSGTHEPLAN #BlendWebMix
BaseStar
Routes | Router
#BSGTHEPLAN #BlendWebMix
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 -> { });
}
}
#BSGTHEPLAN #BlendWebMix
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 -> { });
}
}
#BSGTHEPLAN #BlendWebMix
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 -> { });
}
}
#BSGTHEPLAN #BlendWebMix
“Enregistrer” les raiders
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
👉
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
👉
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
👉
👉
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
👉
#BSGTHEPLAN #BlendWebMix
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());
}
});
});
}
}
👉
#BSGTHEPLAN #BlendWebMix
BaseStar
HealthCheck ❤
#BSGTHEPLAN #BlendWebMix
HealthCheck ❤
👋 Hey, toujours là ?
#BSGTHEPLAN #BlendWebMix
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);
}
}
👉
#BSGTHEPLAN #BlendWebMix
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);
}
}
👉
👉
#BSGTHEPLAN #BlendWebMix
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);
}
}
👉
#BSGTHEPLAN #BlendWebMix
BaseStar
Worker 🤖
this.raiderWorker(raiderRecord,raiderClient); // 👷 this is a worker
#BSGTHEPLAN #BlendWebMix
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)
);
//…
}
👉
#BSGTHEPLAN #BlendWebMix
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);});
}
}
);
});
});
}
👉
#BSGTHEPLAN #BlendWebMix
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);});
}
}
);
});
});
}
👉
#BSGTHEPLAN #BlendWebMix
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);});
}
}
);
});
});
}
👉
#BSGTHEPLAN #BlendWebMix
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);});
}
}
);
});
});
}
👉
#BSGTHEPLAN #BlendWebMix
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
#BSGTHEPLAN #BlendWebMix
checklist
✅ Démarrer les BaseStars
✅ cf Base Redis - MEDIS & health
✅ Démarrer Redis
#BSGTHEPLAN #BlendWebMix
🌍 WebApp
Scala + JavaScript
#BSGTHEPLAN #BlendWebMix
🌍 WebApp
BaseStar
JAVA
BaseStar
JAVA
BaseStar
JAVA
REDIS
BaseStar
JAVA
GET http://localhost:808N/api/raiders
POST http://localhost:808N/api/raiders
GET http://localhost:808N/health
BSG
monitor
SCALA
GET http://localhost:8080/api/raiders
BSG
map
JS
http://localhost:7070
BaseStar
JAVA
Raider
KOTLIN
Raider
KOTLIN
Raider
GROOVY
Raider
GROOVY
POST http://localhost:909N/api/coordinates
#BSGTHEPLAN #BlendWebMix
🌍 WebApp | Backend Scala
val server = vertx.createHttpServer()
val router = Router.router(vertx)
// Settings for the Redis backend
val redisHost = sys.env.getOrElse("REDIS_HOST", "127.0.0.1")
val redisPort = sys.env.getOrElse("REDIS_PORT", "6379").toInt
val redisAuth = sys.env.getOrElse("REDIS_PASSWORD", null)
val redisRecordsKey = sys.env.getOrElse("REDIS_RECORDS_KEY", "vert.x.ms")
// Mount the service discovery backend (Redis)
val discovery = ServiceDiscovery.create(vertx, ServiceDiscoveryOptions()
.setBackendConfiguration(
new JsonObject()
.put("host", redisHost)
.put("port", redisPort)
.put("auth", redisAuth)
.put("key", redisRecordsKey)
)
)
#BSGTHEPLAN #BlendWebMix
🌍 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)
#BSGTHEPLAN #BlendWebMix
🌍 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)
#BSGTHEPLAN #BlendWebMix
pour la forme, la version node
rien à voir avec vert.x
#BSGTHEPLAN #BlendWebMix
🌍 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)
#BSGTHEPLAN #BlendWebMix
🌍 WebApp | Frontend HTML/JS
😳🤢
#BSGTHEPLAN #BlendWebMix
checklist
✅ Démarrer les BaseStars
✅ cf Base Redis - MEDIS
✅ Démarrer Redis
✅ Démarrer BSG Monitor
✅ Démarrer BSG Map
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
#BSGTHEPLAN #BlendWebMix
🚀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
#BSGTHEPLAN #BlendWebMix
🚀Raider(s) | main + stop
kotlin
#BSGTHEPLAN #BlendWebMix
🚀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() { … }
}
#BSGTHEPLAN #BlendWebMix
🚀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() { … }
}
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s) | start / discovery
kotlin
#BSGTHEPLAN #BlendWebMix
🚀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
)
}
))
}
}
#BSGTHEPLAN #BlendWebMix
🚀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
)
}
}
#BSGTHEPLAN #BlendWebMix
🚀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"))
)
}
}
}
#BSGTHEPLAN #BlendWebMix
🚀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 ===
}
}
}
}
}
#BSGTHEPLAN #BlendWebMix
🚀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 ===
}
}
}
}
}
👉
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider
Routes | Router
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)// 👋 ⚠ call by a basestar
router.post("/api/coordinates").handler { context ->
// check data -> if null, don't move
val computedX = context.bodyAsJson.getDouble("x") ?: x
val computedY = context.bodyAsJson.getDouble("y") ?: y
val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity
val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity
/* 💾 === updating record of the service === */
record?.metadata?.getJsonObject("coordinates")
?.put("x", computedX)
?.put("y",computedY)
?.put("xVelocity",computedXVelocity)
?.put("yVelocity",computedYVelocity)
record?.metadata?.put("basestar", json {obj(
"name:" to baseStar?.record?.name,
"color" to baseStar?.record?.metadata?.get("color")
)})
discovery?.update(record, {asyncUpdateResult -> })
context
.response().putHeader("content-type", "application/json;charset=UTF-8")
.end(json {obj(
"message" to "👍", "x" to computedX, "y" to computedY
)}.toString())
}
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)// 👋 ⚠ call by a basestar
router.post("/api/coordinates").handler { context ->
// check data -> if null, don't move
val computedX = context.bodyAsJson.getDouble("x") ?: x
val computedY = context.bodyAsJson.getDouble("y") ?: y
val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity
val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity
/* 💾 === updating record of the service === */
record?.metadata?.getJsonObject("coordinates")
?.put("x", computedX)
?.put("y",computedY)
?.put("xVelocity",computedXVelocity)
?.put("yVelocity",computedYVelocity)
record?.metadata?.put("basestar", json {obj(
"name:" to baseStar?.record?.name,
"color" to baseStar?.record?.metadata?.get("color")
)})
discovery?.update(record, {asyncUpdateResult -> })
context
.response().putHeader("content-type", "application/json;charset=UTF-8")
.end(json {obj(
"message" to "👍", "x" to computedX, "y" to computedY
)}.toString())
}
#BSGTHEPLAN #BlendWebMix
🚀Raider
HealthCheck ❤
#BSGTHEPLAN #BlendWebMix
🚀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())
}
})
})
#BSGTHEPLAN #BlendWebMix
🚀Raider
CircuitBreaker 💔
#BSGTHEPLAN #BlendWebMix
CircuitBreaker 💔
👋 toc toc Penny ?
👋 toc toc Penny ?
👋 toc toc Penny ?
ok j’arrête
#BSGTHEPLAN #BlendWebMix
🚀Raider(s) | circuitbreaker
/* 🚦 === Define a circuit breaker === */
breaker = CircuitBreaker.create("bsg-circuit-breaker", vertx, CircuitBreakerOptions(
maxFailures = 5,
timeout = 20000,
fallbackOnFailure = true,
resetTimeout = 100000))
#BSGTHEPLAN #BlendWebMix
🚀Raider
/* 🤖 === search for a baseStar === */
searchAndSelectOneBaseStar()
#BSGTHEPLAN #BlendWebMix
🚀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
}
👉
#BSGTHEPLAN #BlendWebMix
🚀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
}
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider
subscribeToBaseStar(selectedRecord) // 🎉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun subscribeToBaseStar(selectedRecord: Record) {
val serviceReference = discovery?.getReference(selectedRecord)
val webClient = serviceReference?.getAs(WebClient::class.java)
// 👋 === CIRCUIT BREAKER === try to register to the basestar
breaker?.execute<String>({ future ->
webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, {
baseStarResponse ->
when {
baseStarResponse.failed() -> {
this.baseStar = null // 😧 remove the basestar
future.fail("😡 ouch something bad happened")
}
baseStarResponse.succeeded() -> {
println("👏 you found a basestar")
val selectedBaseStar = BaseStar(selectedRecord, webClient)
this.baseStar = selectedBaseStar
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
future.complete("😃 yesss!")
}
}
})
})?.setHandler({ breakerResult -> })
}
👉
⚠⚠⚠
#BSGTHEPLAN #BlendWebMix
🚀Raider
// 👀 time to check the health of my basestar
watchingMyBaseStar(selectedBaseStar)
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
👉
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
fun watchingMyBaseStar(baseStar: BaseStar) {
// oh oh a B
vertx.setPeriodic(1000, { timerId ->
baseStar.client.get("/health").send { asyncGetRes ->
when {
asyncGetRes.failed() -> {
record?.metadata?.getJsonObject("coordinates")
?.put("xVelocity",0)
?.put("yVelocity",0)
// btw, you never stop in space 👾
discovery?.update(record, {asyncUpdateResult ->
println("${record?.name} 😡 I'm alone ?")
// time to search a new basestar
searchAndSelectOneBaseStar()
vertx.setTimer(3000, { id ->
vertx.cancelTimer(timerId)
})
})
}
asyncGetRes.succeeded() -> {
// 😃 === all is fine ===
}
}
}
})
}
👉
👉
#BSGTHEPLAN #BlendWebMix
🚀Raider(s)
groovy
#BSGTHEPLAN #BlendWebMix
🚀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
]))
}
#BSGTHEPLAN #BlendWebMix
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
#BSGTHEPLAN #BlendWebMix
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
#BSGTHEPLAN #BlendWebMix
Faker Raider(s)
#BSGTHEPLAN #BlendWebMix
ServiceDiscoveryRestEndpoint.java
// use me with other microservices
ServiceDiscoveryRestEndpoint.create(router, discovery);
http://localhost:8081/discovery
#BSGTHEPLAN #BlendWebMix
#BSGTHEPLAN #BlendWebMix
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
#BSGTHEPLAN #BlendWebMix
Créer son Backend
#BSGTHEPLAN #BlendWebMix
#BSGTHEPLAN #BlendWebMix
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
#BSGTHEPLAN #BlendWebMix
Conclusion(s) - Remarques -
Perspectives
#BSGTHEPLAN #BlendWebMix
#BSGTHEPLAN #BlendWebMix
Perspectives
• Déploiement sur le cloud, j’ai eu des surprises
• Remettre à plat mon code | Refactoring
• Ecrire un autre ServiceDiscoveryBackend (Redis PubSub, MQTT, …)
• Utilisation d’un reverse proxy (peut-être)
• Ajouter les 12 colonies dans la démo
https://github.com/botsgarden/vertx-service-discovery-backend-redisson
#BSGTHEPLAN #BlendWebMix
Merci D
• Vous
• BlendWebMix
• Clément Escoffier | Julien Viet | Julien Ponge

More Related Content

What's hot

Commencer avec le TDD
Commencer avec le TDDCommencer avec le TDD
Commencer avec le TDDEric Hogue
 
Continuous testing In PHP
Continuous testing In PHPContinuous testing In PHP
Continuous testing In PHPEric Hogue
 
Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Eric Hogue
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统yiditushe
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)James Titcumb
 
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...zznate
 
Application Logging in the 21st century - 2014.key
Application Logging in the 21st century - 2014.keyApplication Logging in the 21st century - 2014.key
Application Logging in the 21st century - 2014.keyTim Bunce
 
Perlmania_Study - CPAN
Perlmania_Study - CPANPerlmania_Study - CPAN
Perlmania_Study - CPANJeen Lee
 
Redis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your applicationRedis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your applicationrjsmelo
 
2016年のPerl (Long version)
2016年のPerl (Long version)2016年のPerl (Long version)
2016年のPerl (Long version)charsbar
 
Static Typing in Vault
Static Typing in VaultStatic Typing in Vault
Static Typing in VaultGlynnForrest
 
RestMQ - HTTP/Redis based Message Queue
RestMQ - HTTP/Redis based Message QueueRestMQ - HTTP/Redis based Message Queue
RestMQ - HTTP/Redis based Message QueueGleicon Moraes
 
Devinsampa nginx-scripting
Devinsampa nginx-scriptingDevinsampa nginx-scripting
Devinsampa nginx-scriptingTony Fabeen
 
PostgreSQL High-Availability and Geographic Locality using consul
PostgreSQL High-Availability and Geographic Locality using consulPostgreSQL High-Availability and Geographic Locality using consul
PostgreSQL High-Availability and Geographic Locality using consulSean Chittenden
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)James Titcumb
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaJon Moore
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebookguoqing75
 
Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupGreg DeKoenigsberg
 

What's hot (20)

Commencer avec le TDD
Commencer avec le TDDCommencer avec le TDD
Commencer avec le TDD
 
Continuous testing In PHP
Continuous testing In PHPContinuous testing In PHP
Continuous testing In PHP
 
Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014Getting started with TDD - Confoo 2014
Getting started with TDD - Confoo 2014
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
 
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
 
Application Logging in the 21st century - 2014.key
Application Logging in the 21st century - 2014.keyApplication Logging in the 21st century - 2014.key
Application Logging in the 21st century - 2014.key
 
Perlmania_Study - CPAN
Perlmania_Study - CPANPerlmania_Study - CPAN
Perlmania_Study - CPAN
 
Redis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your applicationRedis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your application
 
2016年のPerl (Long version)
2016年のPerl (Long version)2016年のPerl (Long version)
2016年のPerl (Long version)
 
Static Typing in Vault
Static Typing in VaultStatic Typing in Vault
Static Typing in Vault
 
RestMQ - HTTP/Redis based Message Queue
RestMQ - HTTP/Redis based Message QueueRestMQ - HTTP/Redis based Message Queue
RestMQ - HTTP/Redis based Message Queue
 
Devinsampa nginx-scripting
Devinsampa nginx-scriptingDevinsampa nginx-scripting
Devinsampa nginx-scripting
 
PostgreSQL High-Availability and Geographic Locality using consul
PostgreSQL High-Availability and Geographic Locality using consulPostgreSQL High-Availability and Geographic Locality using consul
PostgreSQL High-Availability and Geographic Locality using consul
 
RabbitMQ for Perl mongers
RabbitMQ for Perl mongersRabbitMQ for Perl mongers
RabbitMQ for Perl mongers
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
 
Learning Dtrace
Learning DtraceLearning Dtrace
Learning Dtrace
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and Lua
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetup
 

Similar to Microservices Plan with Vert.x and Redis

Hazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSHazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSuzquiano
 
Vert.x - Reactive & Distributed [Devoxx version]
Vert.x - Reactive & Distributed [Devoxx version]Vert.x - Reactive & Distributed [Devoxx version]
Vert.x - Reactive & Distributed [Devoxx version]Orkhan Gasimov
 
Quick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerQuick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerNic Raboy
 
Charla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo WebCharla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo WebMikel Torres Ugarte
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Testing multi outputformat based mapreduce
Testing multi outputformat based mapreduceTesting multi outputformat based mapreduce
Testing multi outputformat based mapreduceAshok Agarwal
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGMatthew McCullough
 
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 201910 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019Matt Raible
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETGianluca Carucci
 
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...GreeceJS
 
The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016Frank Lyaruu
 
Introduction to Vert.x
Introduction to Vert.xIntroduction to Vert.x
Introduction to Vert.xYiguang Hu
 
apidays LIVE Australia - Building distributed systems on the shoulders of gia...
apidays LIVE Australia - Building distributed systems on the shoulders of gia...apidays LIVE Australia - Building distributed systems on the shoulders of gia...
apidays LIVE Australia - Building distributed systems on the shoulders of gia...apidays
 
Is writing performant code too expensive?
Is writing performant code too expensive? Is writing performant code too expensive?
Is writing performant code too expensive? Tomasz Kowalczewski
 
Ingesting streaming data for analysis in apache ignite (stream sets theme)
Ingesting streaming data for analysis in apache ignite (stream sets theme)Ingesting streaming data for analysis in apache ignite (stream sets theme)
Ingesting streaming data for analysis in apache ignite (stream sets theme)Tom Diederich
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 

Similar to Microservices Plan with Vert.x and Redis (20)

Hazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSHazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMS
 
Vert.x - Reactive & Distributed [Devoxx version]
Vert.x - Reactive & Distributed [Devoxx version]Vert.x - Reactive & Distributed [Devoxx version]
Vert.x - Reactive & Distributed [Devoxx version]
 
Quick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerQuick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase Server
 
Charla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo WebCharla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo Web
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
Amazon elastic map reduce
Amazon elastic map reduceAmazon elastic map reduce
Amazon elastic map reduce
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Testing multi outputformat based mapreduce
Testing multi outputformat based mapreduceTesting multi outputformat based mapreduce
Testing multi outputformat based mapreduce
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUG
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 201910 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019
10 Excellent Ways to Secure Your Spring Boot Application - Devoxx Belgium 2019
 
NodeJS
NodeJSNodeJS
NodeJS
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
 
The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016
 
Introduction to Vert.x
Introduction to Vert.xIntroduction to Vert.x
Introduction to Vert.x
 
apidays LIVE Australia - Building distributed systems on the shoulders of gia...
apidays LIVE Australia - Building distributed systems on the shoulders of gia...apidays LIVE Australia - Building distributed systems on the shoulders of gia...
apidays LIVE Australia - Building distributed systems on the shoulders of gia...
 
Is writing performant code too expensive?
Is writing performant code too expensive? Is writing performant code too expensive?
Is writing performant code too expensive?
 
Ingesting streaming data for analysis in apache ignite (stream sets theme)
Ingesting streaming data for analysis in apache ignite (stream sets theme)Ingesting streaming data for analysis in apache ignite (stream sets theme)
Ingesting streaming data for analysis in apache ignite (stream sets theme)
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 

More from La Cuisine du Web

Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...
Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...
Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...La Cuisine du Web
 
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...La Cuisine du Web
 
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...La Cuisine du Web
 
Tour d’horizon de l’écosystème React-ien par Guillaume BESSON
Tour d’horizon de l’écosystème React-ien par Guillaume BESSONTour d’horizon de l’écosystème React-ien par Guillaume BESSON
Tour d’horizon de l’écosystème React-ien par Guillaume BESSONLa Cuisine du Web
 
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARD
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARDStream processing en mémoire avec Hazelcast Jet par Claire VILLARD
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARDLa Cuisine du Web
 
Programming is Fun par Francis BELLANGER
Programming is Fun par Francis BELLANGERProgramming is Fun par Francis BELLANGER
Programming is Fun par Francis BELLANGERLa Cuisine du Web
 
La démarche communautaire au coeur de la croissance de votre entreprise par H...
La démarche communautaire au coeur de la croissance de votre entreprise par H...La démarche communautaire au coeur de la croissance de votre entreprise par H...
La démarche communautaire au coeur de la croissance de votre entreprise par H...La Cuisine du Web
 
Qui est ton client ? par Audrey JULIENNE
Qui est ton client ? par Audrey JULIENNEQui est ton client ? par Audrey JULIENNE
Qui est ton client ? par Audrey JULIENNELa Cuisine du Web
 
Cloaking is not a crime par Patrick VALIBUS
Cloaking is not a crime par Patrick VALIBUSCloaking is not a crime par Patrick VALIBUS
Cloaking is not a crime par Patrick VALIBUSLa Cuisine du Web
 
Electron, une alternative intéressante ? par Florent MOIGNARD
Electron, une alternative intéressante ? par Florent MOIGNARDElectron, une alternative intéressante ? par Florent MOIGNARD
Electron, une alternative intéressante ? par Florent MOIGNARDLa Cuisine du Web
 
CSS orienté objet par Lenny ROUANET
CSS orienté objet par Lenny ROUANETCSS orienté objet par Lenny ROUANET
CSS orienté objet par Lenny ROUANETLa Cuisine du Web
 
Automatiser la mise en production d’un site web par Nicolas KANDEL
Automatiser la mise en production d’un site web par Nicolas KANDELAutomatiser la mise en production d’un site web par Nicolas KANDEL
Automatiser la mise en production d’un site web par Nicolas KANDELLa Cuisine du Web
 
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANN
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANNDesign Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANN
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANNLa Cuisine du Web
 
Fontes variables, la matrice typographique par Malou VERLOMME
Fontes variables, la matrice typographique par Malou VERLOMMEFontes variables, la matrice typographique par Malou VERLOMME
Fontes variables, la matrice typographique par Malou VERLOMMELa Cuisine du Web
 
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...La Cuisine du Web
 
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...La Cuisine du Web
 
Audio procédural : la révolution WebAssembly ! par Yann ORLAREY
Audio procédural : la révolution WebAssembly ! par Yann ORLAREYAudio procédural : la révolution WebAssembly ! par Yann ORLAREY
Audio procédural : la révolution WebAssembly ! par Yann ORLAREYLa Cuisine du Web
 
A la rencontre de Kafka, le log distribué par Florian GARCIA
A la rencontre de Kafka, le log distribué par Florian GARCIAA la rencontre de Kafka, le log distribué par Florian GARCIA
A la rencontre de Kafka, le log distribué par Florian GARCIALa Cuisine du Web
 
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...Voyage au centre du cerveau humain ou comment manipuler des données binaires ...
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...La Cuisine du Web
 
Ciel mon smartphone ! par Damien GOSSET
Ciel mon smartphone ! par Damien GOSSETCiel mon smartphone ! par Damien GOSSET
Ciel mon smartphone ! par Damien GOSSETLa Cuisine du Web
 

More from La Cuisine du Web (20)

Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...
Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...
Kit de survie du marketeur au bois dormant – Digitalisation du marketing par ...
 
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...
Objets connectés : adapter la technologie aux usages, et pas l’inverse ! par ...
 
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...
Manager l’innovation : est-ce contradictoire, pertinent, nécessaire ?… par Ma...
 
Tour d’horizon de l’écosystème React-ien par Guillaume BESSON
Tour d’horizon de l’écosystème React-ien par Guillaume BESSONTour d’horizon de l’écosystème React-ien par Guillaume BESSON
Tour d’horizon de l’écosystème React-ien par Guillaume BESSON
 
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARD
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARDStream processing en mémoire avec Hazelcast Jet par Claire VILLARD
Stream processing en mémoire avec Hazelcast Jet par Claire VILLARD
 
Programming is Fun par Francis BELLANGER
Programming is Fun par Francis BELLANGERProgramming is Fun par Francis BELLANGER
Programming is Fun par Francis BELLANGER
 
La démarche communautaire au coeur de la croissance de votre entreprise par H...
La démarche communautaire au coeur de la croissance de votre entreprise par H...La démarche communautaire au coeur de la croissance de votre entreprise par H...
La démarche communautaire au coeur de la croissance de votre entreprise par H...
 
Qui est ton client ? par Audrey JULIENNE
Qui est ton client ? par Audrey JULIENNEQui est ton client ? par Audrey JULIENNE
Qui est ton client ? par Audrey JULIENNE
 
Cloaking is not a crime par Patrick VALIBUS
Cloaking is not a crime par Patrick VALIBUSCloaking is not a crime par Patrick VALIBUS
Cloaking is not a crime par Patrick VALIBUS
 
Electron, une alternative intéressante ? par Florent MOIGNARD
Electron, une alternative intéressante ? par Florent MOIGNARDElectron, une alternative intéressante ? par Florent MOIGNARD
Electron, une alternative intéressante ? par Florent MOIGNARD
 
CSS orienté objet par Lenny ROUANET
CSS orienté objet par Lenny ROUANETCSS orienté objet par Lenny ROUANET
CSS orienté objet par Lenny ROUANET
 
Automatiser la mise en production d’un site web par Nicolas KANDEL
Automatiser la mise en production d’un site web par Nicolas KANDELAutomatiser la mise en production d’un site web par Nicolas KANDEL
Automatiser la mise en production d’un site web par Nicolas KANDEL
 
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANN
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANNDesign Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANN
Design Sprints : Créons un monde meilleur ! par Jelto VON SCHUCKMANN
 
Fontes variables, la matrice typographique par Malou VERLOMME
Fontes variables, la matrice typographique par Malou VERLOMMEFontes variables, la matrice typographique par Malou VERLOMME
Fontes variables, la matrice typographique par Malou VERLOMME
 
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...
Le design agile : 6 techniques pour designer de façon plus agile par Matthieu...
 
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...
Réalité virtuelle et augmentée, ces nouvelles technologies au service de la f...
 
Audio procédural : la révolution WebAssembly ! par Yann ORLAREY
Audio procédural : la révolution WebAssembly ! par Yann ORLAREYAudio procédural : la révolution WebAssembly ! par Yann ORLAREY
Audio procédural : la révolution WebAssembly ! par Yann ORLAREY
 
A la rencontre de Kafka, le log distribué par Florian GARCIA
A la rencontre de Kafka, le log distribué par Florian GARCIAA la rencontre de Kafka, le log distribué par Florian GARCIA
A la rencontre de Kafka, le log distribué par Florian GARCIA
 
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...Voyage au centre du cerveau humain ou comment manipuler des données binaires ...
Voyage au centre du cerveau humain ou comment manipuler des données binaires ...
 
Ciel mon smartphone ! par Damien GOSSET
Ciel mon smartphone ! par Damien GOSSETCiel mon smartphone ! par Damien GOSSET
Ciel mon smartphone ! par Damien GOSSET
 

Recently uploaded

Call Girls Pune Just Call 9907093804 Top Class Call Girl Service Available
Call Girls Pune Just Call 9907093804 Top Class Call Girl Service AvailableCall Girls Pune Just Call 9907093804 Top Class Call Girl Service Available
Call Girls Pune Just Call 9907093804 Top Class Call Girl Service AvailableDipal Arora
 
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service Pune
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service PuneVIP Call Girls Pune Kirti 8617697112 Independent Escort Service Pune
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service PuneCall girls in Ahmedabad High profile
 
7.pdf This presentation captures many uses and the significance of the number...
7.pdf This presentation captures many uses and the significance of the number...7.pdf This presentation captures many uses and the significance of the number...
7.pdf This presentation captures many uses and the significance of the number...Paul Menig
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...lizamodels9
 
DEPED Work From Home WORKWEEK-PLAN.docx
DEPED Work From Home  WORKWEEK-PLAN.docxDEPED Work From Home  WORKWEEK-PLAN.docx
DEPED Work From Home WORKWEEK-PLAN.docxRodelinaLaud
 
Grateful 7 speech thanking everyone that has helped.pdf
Grateful 7 speech thanking everyone that has helped.pdfGrateful 7 speech thanking everyone that has helped.pdf
Grateful 7 speech thanking everyone that has helped.pdfPaul Menig
 
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature Set
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature SetCreating Low-Code Loan Applications using the Trisotech Mortgage Feature Set
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature SetDenis Gagné
 
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
Keppel Ltd. 1Q 2024 Business Update  Presentation SlidesKeppel Ltd. 1Q 2024 Business Update  Presentation Slides
Keppel Ltd. 1Q 2024 Business Update Presentation SlidesKeppelCorporation
 
M.C Lodges -- Guest House in Jhang.
M.C Lodges --  Guest House in Jhang.M.C Lodges --  Guest House in Jhang.
M.C Lodges -- Guest House in Jhang.Aaiza Hassan
 
Call Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine ServiceCall Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine Serviceritikaroy0888
 
Call Girls in Gomti Nagar - 7388211116 - With room Service
Call Girls in Gomti Nagar - 7388211116  - With room ServiceCall Girls in Gomti Nagar - 7388211116  - With room Service
Call Girls in Gomti Nagar - 7388211116 - With room Servicediscovermytutordmt
 
Tech Startup Growth Hacking 101 - Basics on Growth Marketing
Tech Startup Growth Hacking 101  - Basics on Growth MarketingTech Startup Growth Hacking 101  - Basics on Growth Marketing
Tech Startup Growth Hacking 101 - Basics on Growth MarketingShawn Pang
 
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurVIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurSuhani Kapoor
 
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...Lviv Startup Club
 
Eni 2024 1Q Results - 24.04.24 business.
Eni 2024 1Q Results - 24.04.24 business.Eni 2024 1Q Results - 24.04.24 business.
Eni 2024 1Q Results - 24.04.24 business.Eni
 
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxSocio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxtrishalcan8
 
It will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayIt will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayNZSG
 
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best Services
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best ServicesMysore Call Girls 8617370543 WhatsApp Number 24x7 Best Services
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best ServicesDipal Arora
 
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Dipal Arora
 

Recently uploaded (20)

Call Girls Pune Just Call 9907093804 Top Class Call Girl Service Available
Call Girls Pune Just Call 9907093804 Top Class Call Girl Service AvailableCall Girls Pune Just Call 9907093804 Top Class Call Girl Service Available
Call Girls Pune Just Call 9907093804 Top Class Call Girl Service Available
 
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service Pune
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service PuneVIP Call Girls Pune Kirti 8617697112 Independent Escort Service Pune
VIP Call Girls Pune Kirti 8617697112 Independent Escort Service Pune
 
7.pdf This presentation captures many uses and the significance of the number...
7.pdf This presentation captures many uses and the significance of the number...7.pdf This presentation captures many uses and the significance of the number...
7.pdf This presentation captures many uses and the significance of the number...
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
 
DEPED Work From Home WORKWEEK-PLAN.docx
DEPED Work From Home  WORKWEEK-PLAN.docxDEPED Work From Home  WORKWEEK-PLAN.docx
DEPED Work From Home WORKWEEK-PLAN.docx
 
Grateful 7 speech thanking everyone that has helped.pdf
Grateful 7 speech thanking everyone that has helped.pdfGrateful 7 speech thanking everyone that has helped.pdf
Grateful 7 speech thanking everyone that has helped.pdf
 
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature Set
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature SetCreating Low-Code Loan Applications using the Trisotech Mortgage Feature Set
Creating Low-Code Loan Applications using the Trisotech Mortgage Feature Set
 
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
Keppel Ltd. 1Q 2024 Business Update  Presentation SlidesKeppel Ltd. 1Q 2024 Business Update  Presentation Slides
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
 
M.C Lodges -- Guest House in Jhang.
M.C Lodges --  Guest House in Jhang.M.C Lodges --  Guest House in Jhang.
M.C Lodges -- Guest House in Jhang.
 
Call Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine ServiceCall Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine Service
 
Call Girls in Gomti Nagar - 7388211116 - With room Service
Call Girls in Gomti Nagar - 7388211116  - With room ServiceCall Girls in Gomti Nagar - 7388211116  - With room Service
Call Girls in Gomti Nagar - 7388211116 - With room Service
 
Tech Startup Growth Hacking 101 - Basics on Growth Marketing
Tech Startup Growth Hacking 101  - Basics on Growth MarketingTech Startup Growth Hacking 101  - Basics on Growth Marketing
Tech Startup Growth Hacking 101 - Basics on Growth Marketing
 
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurVIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
 
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...
Yaroslav Rozhankivskyy: Три складові і три передумови максимальної продуктивн...
 
Eni 2024 1Q Results - 24.04.24 business.
Eni 2024 1Q Results - 24.04.24 business.Eni 2024 1Q Results - 24.04.24 business.
Eni 2024 1Q Results - 24.04.24 business.
 
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxSocio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
 
Forklift Operations: Safety through Cartoons
Forklift Operations: Safety through CartoonsForklift Operations: Safety through Cartoons
Forklift Operations: Safety through Cartoons
 
It will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayIt will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 May
 
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best Services
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best ServicesMysore Call Girls 8617370543 WhatsApp Number 24x7 Best Services
Mysore Call Girls 8617370543 WhatsApp Number 24x7 Best Services
 
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
 

Microservices Plan with Vert.x and Redis

  • 2. #BSGTHEPLAN #BlendWebMix bonjour Philippe Charrière 🕺@clever_cloud ! @k33g_org 👈 G @k33g I ❤JavaScript, I 💔 Java, I 💚 JVM, I 💖 Golo, I 💙 Scala (depuis peu…)
  • 3. #BSGTHEPLAN #BlendWebMix avertissements Je ne suis pas un développeur Java … Enfin je ne crois pas
  • 4. #BSGTHEPLAN #BlendWebMix pourquoi ce talk? Trouver un sujet de talk Le préparer pendant les heures de travail (démos) monter en compétence sur les microservices (et vert.x) partager ce que j’ai appris
  • 5. #BSGTHEPLAN #BlendWebMix Plan de “the plan” Intro - microservices anatomie du plan
  • 6. #BSGTHEPLAN #BlendWebMix microservices? Une application à base de microservices = ensemble de services autonomes Ces services collaborent pour exécuter des tâches complexes Protocoles de messages standard Mode direct ou asynchrone
  • 7. #BSGTHEPLAN #BlendWebMix micro, m’ouais … Distribuer les fonctionnalités et les données Identifier les microservices, les trouver, … Gérer la cohérence et la fiabilité du réseau pour chacun Composants distribués + Technologies différentes = “new modes of failure” Quand ça ne marche pas … 😡 Complexité d’investigation
  • 8. #BSGTHEPLAN #BlendWebMix microservices vert.x 😍 Simplicité, Polyglotte C’est un framework simple: Maîtrise de ce que vous faites BackendDiscovery / Service Discovery (ex: Hazelcast, Redis, …) Stabilité & Résilience Circuit Breakers Health Checks
  • 9. #BSGTHEPLAN #BlendWebMix 1 microservice REST Vert.x public class BaseStar extends AbstractVerticle { public void start() { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.get("/api/hello").handler(context -> { context.response() .putHeader("content-type", "application/json;charset=UTF-8") .end(new JsonObject().put("message", "hello").encodePrettily()); }); Integer httpPort = 8080; HttpServer server = vertx.createHttpServer(); server.requestHandler(router::accept).listen(httpPort, result -> { System.out.println("🌍 Listening on " + httpPort); }); } }
  • 10. #BSGTHEPLAN #BlendWebMix Le Plan Redis + http #BSGTHEPLAN #BlendWebMix
  • 12. #BSGTHEPLAN #BlendWebMix Plan de “the plan” Intro - microservices anatomie du plan BaseStars WebApp Raiders Conclusion(s)
  • 16. #BSGTHEPLAN #BlendWebMix 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) )); } }
  • 17. #BSGTHEPLAN #BlendWebMix 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) )); } }
  • 18. #BSGTHEPLAN #BlendWebMix 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 ); } }
  • 19. #BSGTHEPLAN #BlendWebMix 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 ); } }
  • 20. #BSGTHEPLAN #BlendWebMix 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"))) ); } }
  • 21. #BSGTHEPLAN #BlendWebMix 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()); } }); } }
  • 22. #BSGTHEPLAN #BlendWebMix 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()); } }); } }
  • 24. #BSGTHEPLAN #BlendWebMix 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 -> { }); } }
  • 25. #BSGTHEPLAN #BlendWebMix 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 -> { }); } }
  • 26. #BSGTHEPLAN #BlendWebMix 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 -> { }); } }
  • 28. #BSGTHEPLAN #BlendWebMix 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()); } }); }); } }
  • 29. #BSGTHEPLAN #BlendWebMix 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()); } }); }); } } 👉
  • 30. #BSGTHEPLAN #BlendWebMix 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()); } }); }); } } 👉
  • 31. #BSGTHEPLAN #BlendWebMix 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()); } }); }); } } 👉 👉
  • 32. #BSGTHEPLAN #BlendWebMix 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()); } }); }); } } 👉
  • 33. #BSGTHEPLAN #BlendWebMix 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 #BlendWebMix 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); } } 👉
  • 37. #BSGTHEPLAN #BlendWebMix 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); } } 👉 👉
  • 38. #BSGTHEPLAN #BlendWebMix 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); } } 👉
  • 40. #BSGTHEPLAN #BlendWebMix 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) ); //… } 👉
  • 41. #BSGTHEPLAN #BlendWebMix 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);}); } } ); }); }); } 👉
  • 42. #BSGTHEPLAN #BlendWebMix 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);}); } } ); }); }); } 👉
  • 43. #BSGTHEPLAN #BlendWebMix 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);}); } } ); }); }); } 👉
  • 44. #BSGTHEPLAN #BlendWebMix 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);}); } } ); }); }); } 👉
  • 45. #BSGTHEPLAN #BlendWebMix 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
  • 46. #BSGTHEPLAN #BlendWebMix checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS & health ✅ Démarrer Redis
  • 48. #BSGTHEPLAN #BlendWebMix 🌍 WebApp BaseStar JAVA BaseStar JAVA BaseStar JAVA REDIS BaseStar JAVA GET http://localhost:808N/api/raiders POST http://localhost:808N/api/raiders GET http://localhost:808N/health BSG monitor SCALA GET http://localhost:8080/api/raiders BSG map JS http://localhost:7070 BaseStar JAVA Raider KOTLIN Raider KOTLIN Raider GROOVY Raider GROOVY POST http://localhost:909N/api/coordinates
  • 49. #BSGTHEPLAN #BlendWebMix 🌍 WebApp | Backend Scala val server = vertx.createHttpServer() val router = Router.router(vertx) // Settings for the Redis backend val redisHost = sys.env.getOrElse("REDIS_HOST", "127.0.0.1") val redisPort = sys.env.getOrElse("REDIS_PORT", "6379").toInt val redisAuth = sys.env.getOrElse("REDIS_PASSWORD", null) val redisRecordsKey = sys.env.getOrElse("REDIS_RECORDS_KEY", "vert.x.ms") // Mount the service discovery backend (Redis) val discovery = ServiceDiscovery.create(vertx, ServiceDiscoveryOptions() .setBackendConfiguration( new JsonObject() .put("host", redisHost) .put("port", redisPort) .put("auth", redisAuth) .put("key", redisRecordsKey) ) )
  • 50. #BSGTHEPLAN #BlendWebMix 🌍 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)
  • 51. #BSGTHEPLAN #BlendWebMix 🌍 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 #BlendWebMix pour la forme, la version node rien à voir avec vert.x
  • 53. #BSGTHEPLAN #BlendWebMix 🌍 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)
  • 54. #BSGTHEPLAN #BlendWebMix 🌍 WebApp | Frontend HTML/JS 😳🤢
  • 55. #BSGTHEPLAN #BlendWebMix checklist ✅ Démarrer les BaseStars ✅ cf Base Redis - MEDIS ✅ Démarrer Redis ✅ Démarrer BSG Monitor ✅ Démarrer BSG Map
  • 57. #BSGTHEPLAN #BlendWebMix 🚀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
  • 59. #BSGTHEPLAN #BlendWebMix 🚀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() { … } }
  • 60. #BSGTHEPLAN #BlendWebMix 🚀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() { … } } 👉
  • 61. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) | start / discovery kotlin
  • 62. #BSGTHEPLAN #BlendWebMix 🚀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 ) } )) } }
  • 63. #BSGTHEPLAN #BlendWebMix 🚀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 ) } }
  • 64. #BSGTHEPLAN #BlendWebMix 🚀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")) ) } } }
  • 65. #BSGTHEPLAN #BlendWebMix 🚀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 === } } } } }
  • 66. #BSGTHEPLAN #BlendWebMix 🚀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 === } } } } } 👉 👉
  • 68. #BSGTHEPLAN #BlendWebMix 🚀Raider(s)// 👋 ⚠ call by a basestar router.post("/api/coordinates").handler { context -> // check data -> if null, don't move val computedX = context.bodyAsJson.getDouble("x") ?: x val computedY = context.bodyAsJson.getDouble("y") ?: y val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity /* 💾 === updating record of the service === */ record?.metadata?.getJsonObject("coordinates") ?.put("x", computedX) ?.put("y",computedY) ?.put("xVelocity",computedXVelocity) ?.put("yVelocity",computedYVelocity) record?.metadata?.put("basestar", json {obj( "name:" to baseStar?.record?.name, "color" to baseStar?.record?.metadata?.get("color") )}) discovery?.update(record, {asyncUpdateResult -> }) context .response().putHeader("content-type", "application/json;charset=UTF-8") .end(json {obj( "message" to "👍", "x" to computedX, "y" to computedY )}.toString()) }
  • 69. #BSGTHEPLAN #BlendWebMix 🚀Raider(s)// 👋 ⚠ call by a basestar router.post("/api/coordinates").handler { context -> // check data -> if null, don't move val computedX = context.bodyAsJson.getDouble("x") ?: x val computedY = context.bodyAsJson.getDouble("y") ?: y val computedXVelocity = context.bodyAsJson.getDouble("xVelocity") ?: xVelocity val computedYVelocity = context.bodyAsJson.getDouble("yVelocity") ?: yVelocity /* 💾 === updating record of the service === */ record?.metadata?.getJsonObject("coordinates") ?.put("x", computedX) ?.put("y",computedY) ?.put("xVelocity",computedXVelocity) ?.put("yVelocity",computedYVelocity) record?.metadata?.put("basestar", json {obj( "name:" to baseStar?.record?.name, "color" to baseStar?.record?.metadata?.get("color") )}) discovery?.update(record, {asyncUpdateResult -> }) context .response().putHeader("content-type", "application/json;charset=UTF-8") .end(json {obj( "message" to "👍", "x" to computedX, "y" to computedY )}.toString()) }
  • 71. #BSGTHEPLAN #BlendWebMix 🚀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()) } }) })
  • 73. #BSGTHEPLAN #BlendWebMix CircuitBreaker 💔 👋 toc toc Penny ? 👋 toc toc Penny ? 👋 toc toc Penny ? ok j’arrête
  • 74. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) | circuitbreaker /* 🚦 === Define a circuit breaker === */ breaker = CircuitBreaker.create("bsg-circuit-breaker", vertx, CircuitBreakerOptions( maxFailures = 5, timeout = 20000, fallbackOnFailure = true, resetTimeout = 100000))
  • 75. #BSGTHEPLAN #BlendWebMix 🚀Raider /* 🤖 === search for a baseStar === */ searchAndSelectOneBaseStar()
  • 76. #BSGTHEPLAN #BlendWebMix 🚀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 } 👉
  • 77. #BSGTHEPLAN #BlendWebMix 🚀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 } 👉
  • 79. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun subscribeToBaseStar(selectedRecord: Record) { val serviceReference = discovery?.getReference(selectedRecord) val webClient = serviceReference?.getAs(WebClient::class.java) // 👋 === CIRCUIT BREAKER === try to register to the basestar breaker?.execute<String>({ future -> webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, { baseStarResponse -> when { baseStarResponse.failed() -> { this.baseStar = null // 😧 remove the basestar future.fail("😡 ouch something bad happened") } baseStarResponse.succeeded() -> { println("👏 you found a basestar") val selectedBaseStar = BaseStar(selectedRecord, webClient) this.baseStar = selectedBaseStar // 👀 time to check the health of my basestar watchingMyBaseStar(selectedBaseStar) future.complete("😃 yesss!") } } }) })?.setHandler({ breakerResult -> }) } 👉
  • 80. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun subscribeToBaseStar(selectedRecord: Record) { val serviceReference = discovery?.getReference(selectedRecord) val webClient = serviceReference?.getAs(WebClient::class.java) // 👋 === CIRCUIT BREAKER === try to register to the basestar breaker?.execute<String>({ future -> webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, { baseStarResponse -> when { baseStarResponse.failed() -> { this.baseStar = null // 😧 remove the basestar future.fail("😡 ouch something bad happened") } baseStarResponse.succeeded() -> { println("👏 you found a basestar") val selectedBaseStar = BaseStar(selectedRecord, webClient) this.baseStar = selectedBaseStar // 👀 time to check the health of my basestar watchingMyBaseStar(selectedBaseStar) future.complete("😃 yesss!") } } }) })?.setHandler({ breakerResult -> }) } 👉
  • 81. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun subscribeToBaseStar(selectedRecord: Record) { val serviceReference = discovery?.getReference(selectedRecord) val webClient = serviceReference?.getAs(WebClient::class.java) // 👋 === CIRCUIT BREAKER === try to register to the basestar breaker?.execute<String>({ future -> webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, { baseStarResponse -> when { baseStarResponse.failed() -> { this.baseStar = null // 😧 remove the basestar future.fail("😡 ouch something bad happened") } baseStarResponse.succeeded() -> { println("👏 you found a basestar") val selectedBaseStar = BaseStar(selectedRecord, webClient) this.baseStar = selectedBaseStar // 👀 time to check the health of my basestar watchingMyBaseStar(selectedBaseStar) future.complete("😃 yesss!") } } }) })?.setHandler({ breakerResult -> }) } 👉
  • 82. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun subscribeToBaseStar(selectedRecord: Record) { val serviceReference = discovery?.getReference(selectedRecord) val webClient = serviceReference?.getAs(WebClient::class.java) // 👋 === CIRCUIT BREAKER === try to register to the basestar breaker?.execute<String>({ future -> webClient?.post("/api/raiders")?.sendJson(json { obj("registration" to record?.registration )}, { baseStarResponse -> when { baseStarResponse.failed() -> { this.baseStar = null // 😧 remove the basestar future.fail("😡 ouch something bad happened") } baseStarResponse.succeeded() -> { println("👏 you found a basestar") val selectedBaseStar = BaseStar(selectedRecord, webClient) this.baseStar = selectedBaseStar // 👀 time to check the health of my basestar watchingMyBaseStar(selectedBaseStar) future.complete("😃 yesss!") } } }) })?.setHandler({ breakerResult -> }) } 👉 ⚠⚠⚠
  • 83. #BSGTHEPLAN #BlendWebMix 🚀Raider // 👀 time to check the health of my basestar watchingMyBaseStar(selectedBaseStar)
  • 84. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun watchingMyBaseStar(baseStar: BaseStar) { // oh oh a B vertx.setPeriodic(1000, { timerId -> baseStar.client.get("/health").send { asyncGetRes -> when { asyncGetRes.failed() -> { record?.metadata?.getJsonObject("coordinates") ?.put("xVelocity",0) ?.put("yVelocity",0) // btw, you never stop in space 👾 discovery?.update(record, {asyncUpdateResult -> println("${record?.name} 😡 I'm alone ?") // time to search a new basestar searchAndSelectOneBaseStar() vertx.setTimer(3000, { id -> vertx.cancelTimer(timerId) }) }) } asyncGetRes.succeeded() -> { // 😃 === all is fine === } } } }) }
  • 85. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun watchingMyBaseStar(baseStar: BaseStar) { // oh oh a B vertx.setPeriodic(1000, { timerId -> baseStar.client.get("/health").send { asyncGetRes -> when { asyncGetRes.failed() -> { record?.metadata?.getJsonObject("coordinates") ?.put("xVelocity",0) ?.put("yVelocity",0) // btw, you never stop in space 👾 discovery?.update(record, {asyncUpdateResult -> println("${record?.name} 😡 I'm alone ?") // time to search a new basestar searchAndSelectOneBaseStar() vertx.setTimer(3000, { id -> vertx.cancelTimer(timerId) }) }) } asyncGetRes.succeeded() -> { // 😃 === all is fine === } } } }) }
  • 86. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun watchingMyBaseStar(baseStar: BaseStar) { // oh oh a B vertx.setPeriodic(1000, { timerId -> baseStar.client.get("/health").send { asyncGetRes -> when { asyncGetRes.failed() -> { record?.metadata?.getJsonObject("coordinates") ?.put("xVelocity",0) ?.put("yVelocity",0) // btw, you never stop in space 👾 discovery?.update(record, {asyncUpdateResult -> println("${record?.name} 😡 I'm alone ?") // time to search a new basestar searchAndSelectOneBaseStar() vertx.setTimer(3000, { id -> vertx.cancelTimer(timerId) }) }) } asyncGetRes.succeeded() -> { // 😃 === all is fine === } } } }) } 👉 👉
  • 87. #BSGTHEPLAN #BlendWebMix 🚀Raider(s) fun watchingMyBaseStar(baseStar: BaseStar) { // oh oh a B vertx.setPeriodic(1000, { timerId -> baseStar.client.get("/health").send { asyncGetRes -> when { asyncGetRes.failed() -> { record?.metadata?.getJsonObject("coordinates") ?.put("xVelocity",0) ?.put("yVelocity",0) // btw, you never stop in space 👾 discovery?.update(record, {asyncUpdateResult -> println("${record?.name} 😡 I'm alone ?") // time to search a new basestar searchAndSelectOneBaseStar() vertx.setTimer(3000, { id -> vertx.cancelTimer(timerId) }) }) } asyncGetRes.succeeded() -> { // 😃 === all is fine === } } } }) } 👉 👉
  • 89. #BSGTHEPLAN #BlendWebMix 🚀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 ])) }
  • 90. #BSGTHEPLAN #BlendWebMix 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
  • 91. #BSGTHEPLAN #BlendWebMix 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
  • 93. #BSGTHEPLAN #BlendWebMix ServiceDiscoveryRestEndpoint.java // use me with other microservices ServiceDiscoveryRestEndpoint.create(router, discovery); http://localhost:8081/discovery #BSGTHEPLAN #BlendWebMix
  • 94. #BSGTHEPLAN #BlendWebMix 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
  • 98. #BSGTHEPLAN #BlendWebMix Conclusion(s) - Remarques - Perspectives #BSGTHEPLAN #BlendWebMix
  • 99. #BSGTHEPLAN #BlendWebMix Perspectives • Déploiement sur le cloud, j’ai eu des surprises • Remettre à plat mon code | Refactoring • Ecrire un autre ServiceDiscoveryBackend (Redis PubSub, MQTT, …) • Utilisation d’un reverse proxy (peut-être) • Ajouter les 12 colonies dans la démo https://github.com/botsgarden/vertx-service-discovery-backend-redisson
  • 100. #BSGTHEPLAN #BlendWebMix Merci D • Vous • BlendWebMix • Clément Escoffier | Julien Viet | Julien Ponge