KNOTX.io
When Vert.x & RxJava meet
highly-efficient and scalable integration approach
for modern platforms
Maciej Laskowski
Tomasz Michalak
KNOTX.io
300 pageviews / second
Page available in less than 3 seconds
KNOTX.io
KNOTX.io
KNOTX.io
Ready?
> 300 views / s
< 0.05 s
KNOTX.io
Challenge
- The client is one of the biggest
perfume and cosmetics
retailer in Europe.
- Its sales concentrate on an
online platform.
- The client needs to connect
several incompatible systems.
KNOTX.io
Challenge
KNOTX.io
Traditional approach
KNOTX.io
Client-side approach
KNOTX.io
Nope!
KNOTX.io
Modern approach
● Scalable
● Transparent
● Reusable
● Each component
easy to replace
KNOTX.ioKNOTX.io
TEAMWORK
KNOTX.io
Modern approach
KNOTX.io
HACKATHON
OPEN SOURCE
KNOTX.io
Integration layer == Knot.x
KNOTX.io
Knot.x features: Data ingestion
<script
data-knotx-knots="services,handlebars"
data-knotx-service="welcome"
type="text/knotx-snippet">
<h1>{{message}}</h1>
<div>
<div>Message type - {{body.type}}</div>
<div>Statistics: {{statistics}}</div>
</div>
</script>
CMS
KNOTX.io
Knot.x features: Data ingestion
{
"message": "Hello from Knot.x!",
"body": {
"type": "greeting",
"priority": "1"
},
"statistics": 20
}
<script
data-knotx-knots="services,handlebars"
data-knotx-service="welcome"
type="text/knotx-snippet">
<h1>{{message}}</h1>
<div>
<div>Message type - {{body.type}}</div>
<div>Statistics: {{statistics}}</div>
</div>
</script>
CMS
Service
KNOTX.io
Knot.x features: Data ingestion
<h1>Hello from Knot.x!</h1>
<div>
<div>Message type - greeting</div>
<div>Statistics: 20</div>
</div>
{
"message": "Hello from Knot.x!",
"body": {
"type": "greeting",
"priority": "1"
},
"statistics": 20
}
<script
data-knotx-knots="services,handlebars"
data-knotx-service="welcome"
type="text/knotx-snippet">
<h1>{{message}}</h1>
<div>
<div>Message type - {{body.type}}</div>
<div>Statistics: {{statistics}}</div>
</div>
</script>
CMS
Service
Final page
KNOTX.io
Knot.x features: forms
Knot.x supports simple and multi-step
forms. Easily handles submission
errors, form validations and redirects.
Knot.x allows you to define a graph of
interconnected steps, responding to
user input or site visitor choices.
KNOTX.io
Knot.x features: prototyping
Knot.x gives you the ability to use
simple Mocks. This allows you to
expose your sample data directly to
pages even if the real service is not
available yet, and switch to the real
service without any further
development work.
KNOTX.io
Knot.x features: extensions
Knot.x is a fully modular platform with
very flexible extension points that we
call Knots and Adapters that
efficiently communicates with Knot.x
Core using event bus. Thanks to the
polyglot nature of Vert.x you can
implement extensions in languages
other than Java.
KNOTX.io
is
an event-driven, non-blocking, polyglot
application framework
that runs on the Java Virtual Machine
KNOTX.io
Actor-like modules in Vert.x
VerticleHandlers
HTTP?
KNOTX.io
Vert.x Event Bus
Machine
KNOTX.io
Vert.x Event Bus
heavy
load
KNOTX.io
Vert.x Event Bus
KNOTX.io
Vert.x Event Bus
Machine 2Machine 1
KNOTX.io
Knot.x Architecture
- CMS (Repository) responds
with HTML markup.
- REST services respond with
JSON.
Knot.x Core
Modules
KNOTX.io
Knot.x Architecture
KNOTX.io
Performance
What’s ?
- message
- notification,
- HTTP request
- command/instruction
- file
- result
- error
KNOTX.io
Handlers
void operation(param1, param2,
Handler< > handler) {
// …
handler.handle( );
// …
}
void handle( ) {
// do something with
}
Event Loop
The Golden Rule: don’t block the Event Loop
KNOTX.io
Multi-thread
KNOTX.io
Personalized Search
KNOTX.io
Personalized Search (Vert.x Callbacks)
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
vertx.eventBus().send("user-tracking", userId, reply1 -> {
String userTrackingData = (String) reply1.result().body();
});
}
}
KNOTX.io
Personalized Search (Vert.x Callbacks)
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
vertx.eventBus().send("user-tracking", userId, reply1 -> {
String userTrackingData = (String) reply1.result().body();
vertx.eventBus().send("search-engine", query, reply2 -> {
String searchEngineData = (String) reply2.result().body();
});
});
}
}
KNOTX.io
Personalized Search (Vert.x Callbacks)
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
vertx.eventBus().send("user-tracking", userId, reply1 -> {
String userTrackingData = (String) reply1.result().body();
vertx.eventBus().send("search-engine", query, reply2 -> {
String searchEngineData = (String) reply2.result().body();
String combinedData = userTrackingData + "-" + searchEngineData;
vertx.eventBus().send("personalize", combinedData, reply3 -> {
request.response().end("Personalized results are: " + reply3.result().body());
});
});
});
}
}
KNOTX.io
Personalized Search (Vert.x Callbacks)
KNOTX.io
Personalized Search (Vert.x Callbacks)
KNOTX.io
Personalized Search (Vert.x Callbacks)
KNOTX.io
Callback Hell
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
vertx.eventBus().send("user-tracking", userId, reply1 -> {
String userTrackingData = (String) reply1.result().body();
vertx.eventBus().send("search-engine", query, reply2 -> {
String searchEngineData = (String) reply2.result().body();
String combinedData = userTrackingData + "-" + searchEngineData;
vertx.eventBus().send("personalize", combinedData, reply3 -> {
request.response().end("Personalized results are: " + reply3.result().body());
});
});
});
}
}
KNOTX.io
KNOTX.io
RxJava - Observable
source: http://reactivex.io/intro.html
KNOTX.io
RxJava
Observable.subscribe(
onNext( ),
onError( ),
onCompleted()
);
X
Data stream #1:
Data stream #2: X
|
|
Each form the stream goes here.
Whenever any error happens, it triggers
onError and finishes processing.
X
When all were processed (no more
events: ), this is triggered|
KNOTX.io
RxJava
Observable.subscribe(
onNext( ),
onError( ),
onCompleted()
);
Data stream #1: |
x3
x1
KNOTX.io
RxJava
Observable.subscribe(
onNext( ),
onError( ),
onCompleted()
);
X
Data stream #2:
X |
x2
x1
KNOTX.io
Personalized Search (RxJava)
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
Observable<Message<String>> reply1 = vertx.eventBus()
.sendObservable("user-tracking", userId);
Observable<Message<String>> reply2 = vertx.eventBus()
.sendObservable("search-engine", query);
}
}
KNOTX.io
Personalized Search (RxJava)
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
Observable<Message<String>> reply1 = vertx.eventBus()
.sendObservable("user-tracking", userId);
Observable<Message<String>> reply2 = vertx.eventBus()
.sendObservable("search-engine", query);
Observable
.zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body())
.subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData)
.subscribe(reply3 ->
request.response().end("Personalized results are: " + reply3.body()))
);
}
}
KNOTX.io
Personalized Search (RxJava)
KNOTX.io
RxJava - abstraction over low-level threading
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
Observable<Message<String>> reply1 = vertx.eventBus()
.sendObservable("user-tracking", userId);
Observable<Message<String>> reply2 = vertx.eventBus()
.sendObservable("search-engine", query);
Observable
.zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body())
.subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData)
.subscribe(reply3 ->
request.response().end("Personalized results are: " + reply3.body()))
);
}
}
Nothing happens
here. Just
declarations.
KNOTX.io
RxJava - abstraction over low-level threading
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
Observable<Message<String>> reply1 = vertx.eventBus()
.sendObservable("user-tracking", userId);
Observable<Message<String>> reply2 = vertx.eventBus()
.sendObservable("search-engine", query);
Observable
.zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body())
.subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData)
.subscribe(reply3 ->
request.response().end("Personalized results are: " + reply3.body()))
);
}
}
Nothing happens
here. Just
declarations.
Also, no actions
here until we
subscribe.
KNOTX.io
RxJava - abstraction over low-level threading
public class RequestHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
Observable<Message<String>> reply1 = vertx.eventBus()
.sendObservable("user-tracking", userId);
Observable<Message<String>> reply2 = vertx.eventBus()
.sendObservable("search-engine", query);
Observable
.zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body())
.subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData)
.subscribe(reply3 ->
request.response().end("Personalized results are: " + reply3.body()))
);
}
}
Nothing happens
here. Just
declarations.
Also, no actions
here until we
subscribe.
This subscribe triggers
simultaneous calls to search-engine
and user-tracking.
This subscribe triggers calling
personalized service.
KNOTX.io
RxJava - Marble Diagrams
KNOTX.io
RxJava - Marble Diagrams
KNOTX.io
Live demo
https://goo.gl/lqG76x
KNOTX.io
Thank you
● Tutorials: www.knotx.io
● Sources & Wiki: https://github.com/Cognifide/knotx
● Twitter: @knotx_project
● Gitter / Google Groups
Maciej Laskowski
@skejven
https://github.com/Skejven
Tomasz Michalak
@tomichalak
https://github.com/tomaszmichalak

Knot.x: when Vert.x and RxJava meet

  • 1.
    KNOTX.io When Vert.x &RxJava meet highly-efficient and scalable integration approach for modern platforms Maciej Laskowski Tomasz Michalak
  • 2.
    KNOTX.io 300 pageviews /second Page available in less than 3 seconds
  • 3.
  • 4.
  • 5.
  • 6.
    KNOTX.io Challenge - The clientis one of the biggest perfume and cosmetics retailer in Europe. - Its sales concentrate on an online platform. - The client needs to connect several incompatible systems.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    KNOTX.io Modern approach ● Scalable ●Transparent ● Reusable ● Each component easy to replace
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
    KNOTX.io Knot.x features: Dataingestion <script data-knotx-knots="services,handlebars" data-knotx-service="welcome" type="text/knotx-snippet"> <h1>{{message}}</h1> <div> <div>Message type - {{body.type}}</div> <div>Statistics: {{statistics}}</div> </div> </script> CMS
  • 17.
    KNOTX.io Knot.x features: Dataingestion { "message": "Hello from Knot.x!", "body": { "type": "greeting", "priority": "1" }, "statistics": 20 } <script data-knotx-knots="services,handlebars" data-knotx-service="welcome" type="text/knotx-snippet"> <h1>{{message}}</h1> <div> <div>Message type - {{body.type}}</div> <div>Statistics: {{statistics}}</div> </div> </script> CMS Service
  • 18.
    KNOTX.io Knot.x features: Dataingestion <h1>Hello from Knot.x!</h1> <div> <div>Message type - greeting</div> <div>Statistics: 20</div> </div> { "message": "Hello from Knot.x!", "body": { "type": "greeting", "priority": "1" }, "statistics": 20 } <script data-knotx-knots="services,handlebars" data-knotx-service="welcome" type="text/knotx-snippet"> <h1>{{message}}</h1> <div> <div>Message type - {{body.type}}</div> <div>Statistics: {{statistics}}</div> </div> </script> CMS Service Final page
  • 19.
    KNOTX.io Knot.x features: forms Knot.xsupports simple and multi-step forms. Easily handles submission errors, form validations and redirects. Knot.x allows you to define a graph of interconnected steps, responding to user input or site visitor choices.
  • 20.
    KNOTX.io Knot.x features: prototyping Knot.xgives you the ability to use simple Mocks. This allows you to expose your sample data directly to pages even if the real service is not available yet, and switch to the real service without any further development work.
  • 21.
    KNOTX.io Knot.x features: extensions Knot.xis a fully modular platform with very flexible extension points that we call Knots and Adapters that efficiently communicates with Knot.x Core using event bus. Thanks to the polyglot nature of Vert.x you can implement extensions in languages other than Java.
  • 22.
    KNOTX.io is an event-driven, non-blocking,polyglot application framework that runs on the Java Virtual Machine
  • 23.
    KNOTX.io Actor-like modules inVert.x VerticleHandlers HTTP?
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
    KNOTX.io Knot.x Architecture - CMS(Repository) responds with HTML markup. - REST services respond with JSON. Knot.x Core Modules
  • 29.
  • 30.
    KNOTX.io Performance What’s ? - message -notification, - HTTP request - command/instruction - file - result - error
  • 31.
    KNOTX.io Handlers void operation(param1, param2, Handler<> handler) { // … handler.handle( ); // … } void handle( ) { // do something with } Event Loop The Golden Rule: don’t block the Event Loop
  • 32.
  • 33.
  • 34.
    KNOTX.io Personalized Search (Vert.xCallbacks) public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { vertx.eventBus().send("user-tracking", userId, reply1 -> { String userTrackingData = (String) reply1.result().body(); }); } }
  • 35.
    KNOTX.io Personalized Search (Vert.xCallbacks) public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { vertx.eventBus().send("user-tracking", userId, reply1 -> { String userTrackingData = (String) reply1.result().body(); vertx.eventBus().send("search-engine", query, reply2 -> { String searchEngineData = (String) reply2.result().body(); }); }); } }
  • 36.
    KNOTX.io Personalized Search (Vert.xCallbacks) public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { vertx.eventBus().send("user-tracking", userId, reply1 -> { String userTrackingData = (String) reply1.result().body(); vertx.eventBus().send("search-engine", query, reply2 -> { String searchEngineData = (String) reply2.result().body(); String combinedData = userTrackingData + "-" + searchEngineData; vertx.eventBus().send("personalize", combinedData, reply3 -> { request.response().end("Personalized results are: " + reply3.result().body()); }); }); }); } }
  • 37.
  • 38.
  • 39.
  • 40.
    KNOTX.io Callback Hell public classRequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { vertx.eventBus().send("user-tracking", userId, reply1 -> { String userTrackingData = (String) reply1.result().body(); vertx.eventBus().send("search-engine", query, reply2 -> { String searchEngineData = (String) reply2.result().body(); String combinedData = userTrackingData + "-" + searchEngineData; vertx.eventBus().send("personalize", combinedData, reply3 -> { request.response().end("Personalized results are: " + reply3.result().body()); }); }); }); } }
  • 41.
  • 42.
    KNOTX.io RxJava - Observable source:http://reactivex.io/intro.html
  • 43.
    KNOTX.io RxJava Observable.subscribe( onNext( ), onError( ), onCompleted() ); X Datastream #1: Data stream #2: X | | Each form the stream goes here. Whenever any error happens, it triggers onError and finishes processing. X When all were processed (no more events: ), this is triggered|
  • 44.
  • 45.
  • 46.
    KNOTX.io Personalized Search (RxJava) publicclass RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { Observable<Message<String>> reply1 = vertx.eventBus() .sendObservable("user-tracking", userId); Observable<Message<String>> reply2 = vertx.eventBus() .sendObservable("search-engine", query); } }
  • 47.
    KNOTX.io Personalized Search (RxJava) publicclass RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { Observable<Message<String>> reply1 = vertx.eventBus() .sendObservable("user-tracking", userId); Observable<Message<String>> reply2 = vertx.eventBus() .sendObservable("search-engine", query); Observable .zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body()) .subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData) .subscribe(reply3 -> request.response().end("Personalized results are: " + reply3.body())) ); } }
  • 48.
  • 49.
    KNOTX.io RxJava - abstractionover low-level threading public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { Observable<Message<String>> reply1 = vertx.eventBus() .sendObservable("user-tracking", userId); Observable<Message<String>> reply2 = vertx.eventBus() .sendObservable("search-engine", query); Observable .zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body()) .subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData) .subscribe(reply3 -> request.response().end("Personalized results are: " + reply3.body())) ); } } Nothing happens here. Just declarations.
  • 50.
    KNOTX.io RxJava - abstractionover low-level threading public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { Observable<Message<String>> reply1 = vertx.eventBus() .sendObservable("user-tracking", userId); Observable<Message<String>> reply2 = vertx.eventBus() .sendObservable("search-engine", query); Observable .zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body()) .subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData) .subscribe(reply3 -> request.response().end("Personalized results are: " + reply3.body())) ); } } Nothing happens here. Just declarations. Also, no actions here until we subscribe.
  • 51.
    KNOTX.io RxJava - abstractionover low-level threading public class RequestHandler implements Handler<HttpServerRequest> { @Override public void handle(HttpServerRequest request) { Observable<Message<String>> reply1 = vertx.eventBus() .sendObservable("user-tracking", userId); Observable<Message<String>> reply2 = vertx.eventBus() .sendObservable("search-engine", query); Observable .zip(reply1, reply2, (Message resp1, Message resp2) -> resp1.body() + "-" + resp2.body()) .subscribe(combinedData -> vertx.eventBus().sendObservable("personalize", combinedData) .subscribe(reply3 -> request.response().end("Personalized results are: " + reply3.body())) ); } } Nothing happens here. Just declarations. Also, no actions here until we subscribe. This subscribe triggers simultaneous calls to search-engine and user-tracking. This subscribe triggers calling personalized service.
  • 52.
  • 53.
  • 54.
  • 55.
    KNOTX.io Thank you ● Tutorials:www.knotx.io ● Sources & Wiki: https://github.com/Cognifide/knotx ● Twitter: @knotx_project ● Gitter / Google Groups Maciej Laskowski @skejven https://github.com/Skejven Tomasz Michalak @tomichalak https://github.com/tomaszmichalak