|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
The publish() method accepts a destination, which in this case is "com.geekcap.vertxexamples.Server.announcements". Recall that the naming
of this destination is arbitrary, but prefacing the type of notification (announcement in this example) with the fully-qualified
name of the class makes it clear where the message came from.
Listing 5 shows the source code for the AuditVerticle class.
package com.geekcap.vertxexamples;
import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;
public class AuditVerticle extends Verticle {
@Override
public void start() {
// Let's register ourselves as a listener to Server notifications
EventBus eb = vertx.eventBus();
Handler<Message> auditHandler = new Handler<Message>() {
@Override
public void handle(Message message) {
Logger logger = container.logger();
logger.info( "AuditVerticle here, someone requested resource: " + message.body() );
}
};
eb.registerHandler( "com.geekcap.vertxexamples.Server.announcements", auditHandler );
}
}
The AuditVerticle in Listing 5 acts much like a reporting engine: it listens for "announcements" from the Server class and then writes those out as informational log messages. When something of interest happens in the Server class, it can publish it to its announcements topic and different subscribers can do different things, such as logging the message or inserting it in a Hadoop cluster
for later analysis.
Listing 5 then creates a Handler in-line instance (an anonymous inner class is created and assigned to a variable without creating the class in a separate
file) that logs the message. Next, it registers a handler to the "com.geekcap.vertxexamples.Server.announcements" address by invoking the EventBus's registerHandler() method. Now, any time that the Server class publishes a message to this destination, the AuditHandler's handle() method will be invoked.
Point-to-point messaging is used either when you want a message to be processed only by a single consumer or as a mechanism
for components to communicate with one another asynchronously. In this section I demonstrate the latter by creating a new
class that relies on a worker verticle to do its work for it, and then that worker verticle communicates the result back to
the Server2.
Listing 6 shows the source code for the Server2 class.
package com.geekcap.vertxexamples;
import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.deploy.Verticle;
import java.util.concurrent.ConcurrentMap;
public class Server2 extends Verticle {
public void start() {
// Create our dependent verticles
container.deployWorkerVerticle("com.geekcap.vertxexamples.MyWorkerVerticle");
// Start a server that handles things with point-to-point messaging
vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
@Override
public void handle(final HttpServerRequest req) {
// Set a shared variable
ConcurrentMap<String, String> map = vertx.sharedData().getMap("mymap");
map.put("mykey", "myvalue");
// Let's send a message to a worker verticle and wait for it to respond
EventBus eb = vertx.eventBus();
eb.send("request.worker", req.path, new Handler<Message<String>>() {
@Override
public void handle(Message<String> message) {
Logger logger = container.getLogger();
logger.info( "Received a reply from our worker: " + message.body );
req.response.headers().put("Content-Length", Integer.toString(message.body.length()));
req.response.write(message.body);
}
});
}
}).listen(8080);
}
}
The Server2 class starts by deploying a worker verticle. Worker verticles are different from standard verticles in that they do not contain
an event look and are expected to be triggered by an event bus message. Worker verticles are deployed by obtaining access
to the Vert.x container and invoking its deployWorkerVerticle() method.
Next, the Server2 obtains access to the EventBus, again by invoking the eventBus() method on the vertx instance variable. This time the Server2 invokes the send() method, which is the gateway to sending messages in a point-to-point fashion. In this case it sends the request path to a
destination named "request.worker". The first parameter to the send() method is the destination, the second parameter is the data to send to the destination, and an optional third parameter is
a Handler that can be called back by the recipient of the message.
The MyWorkerVerticle, which is shown in Listing 7, is designed to construct a response for the specified request path and send that response back
to the Server2's Handler. The handler logs the response and then writes that response back to the HttpServerRequest that initiated the action. The only thing that is a little challenging is that before we're able to write back to the HttpServerRequest we need to specify the HTTP Content-Length of the response, which is just the length of the string we're returning.
Two additional Vert.x features are added to the Server2 class:
container variable has a method named getLogger() that provides access to Vert.x's logger. This logger is very similar to log4j and provides methods like debug(), info(), and fatal() to log messages at different logging levels. By default, logging information will be echoed to standard output and will be
written to a file named vertx.log located in the TMPDIR-defined directory.
sharedData() method on the vertx instance and then invoking one of the shared data accessor methods. In Listing 4, we store data in a Map that is retrieved by invoking getMap(); you can likewise retrieve shared data in a Set by invoking getSet(). All of the verticles in your Vert.x instance have access to the same shared data using the same paradigm, so it is a way
for you to share immutable data between verticles.
Listing 7 shows the source code for the MyWorkerVerticle class.
package com.geekcap.vertxexamples;
import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;
import java.util.concurrent.ConcurrentMap;
public class MyWorkerVerticle extends Verticle {
@Override
public void start() {
// Register a listener
EventBus eb = vertx.eventBus();
Handler<Message> workerHandler = new Handler<Message>() {
@Override
public void handle(Message message) {
Logger logger = container.logger();
logger.info( "MyWorkerVerticle just received a request for: " + message.body() );
// Examine our shared map
ConcurrentMap<String, String> map = vertx.sharedData().getMap("mymap");
logger.info( "Shared data: " + map.get( "mykey" ) );
message.reply( "<html><head><title>Worker Response</title></head><body>Hello, from the worker verticle</body></html>" );
}
};
eb.registerHandler( "request.worker", workerHandler );
}
}
The MyWorkerVerticle class creates a new Handler instance with a handle() method that processes messages from the Server2 class. Recall from Listing 6 that one of the arguments passed to the send() method was a Handler instance that could be invoked by the message recipient. Listing 7 invokes message.reply(), which sends a response back to the originator (which in this example is the Server2's Handler.)
The MyWorkerVerticle class obtains access to the EventBus and then registers its handler to listen to messages sent to the "request.worker" destination, to complete the loop.
As far as functionality, the MyWorkerVerticle simply constructs an HTML document and returns it back to the Server2 class. You could build upon this example by connecting to a database or reading data from another server to retrieve the
data with which to build the response.
And you'll notice that MyWorkerVerticle retrieves the shared data from the vertx's sharedData() map.
As enterprise systems evolve in complexity, integration has become one of the biggest programming challenges for software developers. Vert.x addresses the complexity of integration in a couple of ways: First, it is built around an event bus that loosely couples verticles while supporting multiple programming languages. Regardless of whether code is written in Java, Ruby, Python, or JavaScript, you can seamlessly integrate it inside the Vert.x event bus. Additionally, the event bus natively supports asynchronous messaging and an event-driven architecture, which yields high-scalability and loose-coupling.
This article has presented an overview of Vert.x, its unique vernacular, and the core components that it combines to build highly scalable enterprise solutions. I demonstrated both a web server and a messaging system written in Vert.x, using the latter example to develop a publish/subscribe messaging and a point-to-point messaging solution. In the latter example I also demonstrated event logging, shared data, and the difference between standard and worker verticles. While this article has been introductory, I've touched on some of the major features that illustrate the power of Vert.x, a solution similar to Node.js but built on the JVM. I hope that you are inspired to learn more about Vert.x and the types of programming challenge it resolves.
Steven Haines is a technical architect at Kit Digital, currently working onsite at Disney in Orlando. He is the founder of www.geekcap.com, an online education website, and has written hundreds of Java-related articles as well as three books: Java 2 From Scratch, Java 2 Primer Plus, and Pro Java EE Performance Management and Optimization. He lives with his wife and two children in Apopka, Florida.
Read more about Enterprise Java in JavaWorld's Enterprise Java section.