is there a Java SockJS client for Vert.x available?
Similar to the TCP/IP bridge, but based on SockJS.
Reason is that we want a unified protocol stack, connecting clients
to Vert.x. For JavaScript we can use vertx3-eventbus-client, which work great.
We are looking now for a similar solution for Java.
There isn't yet (work-in-progress). However you can write a basic client yourself using the Vert.x HttpClient:
open a websocket
send pings periodically to prevent the connection from being closed
register a handler
listen for messages
Here's an example:
client.websocket(HTTP_PORT, HTTP_HOST, "/eventbus/websocket", ws -> {
JsonObject msg = new JsonObject().put("type", "ping");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
// Send pings periodically to avoid the websocket connection being closed
vertx.setPeriodic(5000, id -> {
JsonObject msg = new JsonObject().put("type", "ping");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
});
// Register
JsonObject msg = new JsonObject().put("type", "register").put("address", "my-address");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
ws.handler(buff -> {
JsonObject json = new JsonObject(buff.toString()).getJsonObject("body");
// Do stuff with the body
});
});
If you need to work with different addresses then your handler will have to inspect the JSON object, not just get the body.
Related
I have a two verticle server written in vert.x + reactive extensions. HTTP server verticle uses event bus to send requests to the DB verticle. After receiving the response from the DB verticle (through event bus) I send the response to the http client using rxEnd. However clients does not seem to receive this response and times out eventually. If I were to use end() instead, things work fine. I use postman to test this REST API. Please see below for the code which forward results from the DB verticle to client.
routerFactory.addHandlerByOperationId("createChargePoints", routingContext -> {
RequestParameters params = routingContext.get("parsedParameters");
RequestParameter body = params.body();
JsonObject jsonBody = body.getJsonObject();
vertx.eventBus().rxRequest("dbin", jsonBody)
.map(message -> {
System.out.println(message.body());
return routingContext.response().setStatusCode(200).rxEnd(message.body().toString());
})
.subscribe(res -> {
System.out.println(res);
}, res -> {
System.out.println(res);
});
});
The rxEnd method is a variant of end that returns a Completable. The former is lazy, the latter is not.
In other words, if you invoke rxEnd you have to subscribe to the Completable otherwise nothing happens.
Looking at the code of your snippet, I don't believe using rxEnd is necessary. Indeed, it doesn't seem like you need to know if the request was sent succesfully.
What is the recommended way in vert.x to write an Asynchronous request handler?
In this service, a request processing typically involves calling DB, calling external services, etc. I do not want to block the request handling thread however. What is the recommended way to achieve this using vet.x? In a typical asynchronous processing chain, I would use the request handling thread to emit a message to the message bus with the request object. Another handler will pick this message and do some processing such as checking request params. This handler can then emit a new message to the bus which can be picked up by the next handler which will do a remote call. This handler emits a new message with the result of the call which can be picked up by the next handler which will do error checking etc. Next handler would be responsible for creating the response and sending it to the client.
How one can create a similar pipeline using vert.x?
Everything, comprising request handlers for HttpServer, is asynchronous, isn't it?
var server = vertx.createHttpServer(HttpServerOptions())
server.requestHandler { req ->
req.setExpectMultipart(true) // for handling forms
var totalBuffer = Buffer.buffer()
req.handler { buff -> b.appendBuffer(buff) }
.endHandler { // the body has now been fully read
var formAttributes = request.formAttributes()
req.response().putHeader("Content-type","text/html");
req.response().end("Hello HTTP!");
}
// the above is so common that Vertx provides: bodyHandler{totalbuff->..}
}.listen(8080, "127.0.0.1", { res -> if(res.succeeded()) ... });
You just need to (end) write on req.response() on your final handler of your pipeline.
For a more stream-like implementation (i.e., not callback-based), you may use Vert.x Rx/ReactiveStreams API. E.g., you may use Vert.x Web Client for making requests, possibly using its Rx-fied API.
I am following the vertx sockjs example to transfer data over the SockJS event bus bridge.
The sending code:
eventBus.publish(ebAddress, data);
The consumer code:
var eb = new EventBus("http://localhost:8088/eventbus");
eb.onopen = function () {
eb.registerHandler("/ebaddress", function (err, msg) {
var str = "<code>" + msg.body + "</code><br>";
console.log(str);
})
}
The first client works fine. However, for the second connected client, since it is subscribing the same eb address, it cannot get the most current data that has been sent to the first client. It won't be an issue if the data is coming in fast. But if the time interval between data points are long, the second client will have no data for a long time until the next new data point arrive.
So, is the event bus of Vert.x able to retain message so that whenever a new client connects, it can get the most recent data right away?
I am pretty new to Vert.x, so any comments will be greatly appreciated.
Simple answer: no, Vert.x EventBus doesn't persist messages. Nor does it able to replay them, for that reason. It just that: a bus to send events on. After all, when you write in JavaScript element.on("click", function() {}), you don't usually expect to receive all previous clicks, right?
But, it doesn't mean it's not possible.
In your JavaScript:
eb.onopen = function () {
// On connect your client asks on a different channel to get some previously stored messages
eb.send("/replay", {count: 10}, null, function(err, msg) {
// Populate your code
});
// Continue here as usual
eb.registerHandler("/ebaddress", function (err, msg) {
// Something happens here
})
}
Of course on your server side you'll need to
Persist some amount of messages, either in-memory or in some storage of your choice
Listen to this new /replay channel
Use .send() to reply to specific client with previous messages
I have got https server run by vertx http stack. Is there a way to get client IP addresses for all established connections (i.e. for audit, security, monitoring, QoS and other purposes). Ideally, I would like to have event (callback) driven API notifying about established and closed connections. How can I achieve it?
Current workaround is to poll a tool similar to netstat, but it is very inconvenient and not really real-time (i.e. short connections can be missed).
Github community responded with the answer: https://github.com/vert-x3/vertx-web/issues/685
You can use the HttpServer connection handler and manage it yourself easily:
server.connectionHandler(conn -> {
// Track conn.remoteAddress()
conn.closeHandler(v -> {
// Cleanup track of conn.remoteAddress()
});
});
In my example I'll write all IP addresses to a console. But of course you could write it to a log file, DB, or some other place of your choice.
Vertx vertx = Vertx.vertx();
// This your regular router
Router router = Router.router(vertx);
// We'll have two routes, just for fun
router.route("/a").handler((ctx) -> {
ctx.response().end("A");
});
router.route("/b").handler((ctx) -> {
ctx.response().end("B");
});
Router filterRouter = Router.router(vertx);
filterRouter.get().handler((ctx)->{
System.out.println("IP is " + ctx.request().remoteAddress());
// Forward to your actual router
ctx.next();
});
filterRouter.mountSubRouter("/", router);
vertx.createHttpServer().requestHandler(filterRouter::accept).listen(8080);
My Request handler
router.route("/clientController/*").handler(sockJSHandler);
and my event bus is
eb.consumer("chat.to.server",message->{
System.out.println("Getting Request in Client Controller Event Bus");
JsonObject data = (JsonObject) message.body();
String classifier = data.getString("classifier");
if(classifier.equals("loginUri")) {
System.out.println("Request for Login URI");
vertx.executeBlocking(future -> {
future.complete(OAuth.getOAuthParam());
}, res -> {
eb.publish("chat.to.client", res.result());
});
}
is it possible to access the session object inside event bus as we do the normal routing handler as routingContext.getSession()
Short answer, you can't. Routing context session is not related to the eventbus. The eventbus can be deployed without having a HTTP server while routing context is a very web specific thing.
If you need to have state on your eventbus then I'd say you need to pass the session id in your message headers, and only if the session is clustered then you might be able to retrieve it from the cluster store by id. By doing so you might also introduce inconsistency so i'd advice not to do it.
Alternative just put all your required session data in your message payload.