Monthly Archives: March 2018

Service Discovery using ZooKeeper with Vert.x

Following on from my post Service discovery with Vert.x, let’s switch from using the Vert.x built-in Service Discovery to using ZooKeeper.

You can fire up a docker container running ZooKeeper using

docker run --name zookeeper --restart always -d zookeeper

Now we’ll need to add the following to our pom.xml

<dependency>
   <groupId>io.vertx</groupId>
   <artifactId>vertx-service-discovery-bridge-zookeeper</artifactId>
   <version>${vertx.version}</version>
</dependency>

Note: vertx.version is set to 3.5.1

The code to create the Service Discovery to ZooKeeper is, as follows

ServiceDiscovery discovery = ServiceDiscovery.create(vertx)
   .registerServiceImporter(new ZookeeperServiceImporter(),
      new JsonObject()
        .put("connection", "172.17.0.2:2181")
        .put("basePath", "/services/hello-service"));

Replace the ip address with the one create by Docker or if you’re running on localhost.

We register the ZooKeeperServiceImporter and supply at least the “connection”, the “basePath” is not required, a default will be supplied if none is explicitly supplied.

Don’t forget you’ll need the import

import io.vertx.servicediscovery.zookeeper.ZookeeperServiceImporter;

References

Docker ZooKeeper
Vert.x Service Discovery

Code

Source code relating to these posts on Vert.x can be found at VertxSamples. The code differs from the posts in that in some cases it’s been refactored to reduce some code duplication etc.

Configuration with Vert.x

Obviously you can use whatever configuration code you like to configure your Verticles and/or Vert.x applications, you might use standard configuration files or a Redis store or whatever.

Vert.x has a single library which allows us to abstract such things and offers different storage formats and code to access different storage mechanisms.

Add the following dependency to your pom.xml

<dependency>
   <groupId>io.vertx</groupId>
   <artifactId>vertx-config</artifactId>
   <version>${vertx.version}</version>
</dependency>

vertx.version in my version is 3.5.0

We can actually define multiple storage mechanisms for our configuration data and chain them. For example imagine we have some we have configuration on the localhost and other configuration on a remote HTTP server. We can set things up to get properties that are a combination of both locations.

Note: If location A has property port set to 8080 but location B (which appears after location A in the chain) has port set to 80 then the last property located in the chain is the one we’ll see when querying the configuration code for this property. For other properties the result is a combination of those from A and those from B.

We can also mark configuration sources as optional so, if they do not exist or are down, the chaining will not fail or exception.

Let’s start with a simple config.properties file stored in /src/main/resources. Here’s the file (nothing exciting here)

port=8080

We’ll now create the ConfigStoreOptions for a “properties” file, then add it to the store (we’ll just use a single store for this example) and finally we’ll retrieve the port property from the configuration retriever…

// create the options for a properties file store
ConfigStoreOptions propertyFile = new ConfigStoreOptions()
   .setType("file")
   .setFormat("properties")
   .setConfig(new JsonObject().put("path", "config.properties"));

// add the options to the chain
ConfigRetrieverOptions options = new ConfigRetrieverOptions()
   .addStore(propertyFile);
    // .addStore for other stores here

ConfigRetriever retriever = ConfigRetriever.create(vertx, options);
retriever.getConfig(ar ->
{
   if(ar.succeeded()) {
      JsonObject o  = ar.result();
      int port = o.getInteger("port");
      // use port config
   }
});

Note: Don’t forget to set the configuration store options “path” to the location and name of your file.

Service discovery with Vert.x

We may get to a point whereby we have multiple Vert.x applications running and we want one Verticle to communicate with another – this is easy enough if the IP address and port are fixed but not so easy in more scalable/real-world scenarios where we cannot guarantee these are fixed.

In such situations we can use service discovery to locate other services.

Before we get started with the code, we need to add the following to the pom.xml

<dependency>
   <groupId>io.vertx</groupId>
   <artifactId>vertx-service-discovery</artifactId>
   <version>${vertx.version}</version>
</dependency>

I’m using vertx.version 3.5.0 in my examples.

Publishing/Registering our Verticle with ServiceDiscovery

To register our Verticle with ServiceDisovery we create a Record object which tells the ServiceDiscovery how to access a Verticle, this includes it’s host/IP, port and service root along with a name for other code to use to locate the service. For example

Record record = HttpEndpoint.createRecord(
   "hello-service",
   "localhost",
   8080,
   "/hello");

So this basically says, create a Record named “hello-service” (the key or name of the service) and it’s IP/host is localhost, obviously this is just for my testing. Next we supply the exposed port and finally the root of the service.

We then publish this record to the ServiceDiscovery object like this

discovery.publish(record, ar ->
{
   if (ar.succeeded()) {
      // publication succeeded
      publishedRecord = ar.result();
   } else {
      // publication failed
   }
});

Upon success we store the Record (in this case we only do this if the call succeeded) so that we can unpublish the service if it’s shutdown.

Let’s look at the full code for a simplified HelloVerticle

public class HelloVerticle extends AbstractVerticle {

    private ServiceDiscovery discovery;
    private Record publishedRecord;

    @Override
    public void start() {
        discovery = new DiscoveryImpl(vertx, 
           new ServiceDiscoveryOptions());

        Router router = Router.router(vertx);
        router.get("/hello").handler(ctx -> {
            ctx.response()
                .putHeader("content-type", "text/plain")
                .end("hello");
        });

        Record record = HttpEndpoint.createRecord(
                "hello-service",
                "localhost",
                8080,
                "/hello");

        discovery.publish(record, ar ->
        {
            if (ar.succeeded()) {
                // publication success
                publishedRecord = ar.result();
            } else {
                // publication failure
            }
        });

        vertx
           .createHttpServer()
           .requestHandler(router::accept)
           .listen(8080, ar -> {
              // handle success/failure 
           });
    }

    @Override
    public void stop() {
        if(discovery != null) {
            discovery.unpublish(publishedRecord.getRegistration(), ar ->
            {
                if (ar.succeeded()) {
                    // Success
                } else {
                    // cannot unpublish the service, 
                    // may have already been removed, 
                    // or the record is not published
                }
            });

            discovery.close();
        }
    }
}

Locating a service via ServiceDiscovery

Let’s take a look at some “consumer” code which will use service discovery to locate our “HelloVerticle”. As expected we need to create access to the ServiceDiscovery object and then we try to locate the Record for a previously added Record.

In the example, below, we search for the “name”, “hello-service”, this is wrapped into a JsonObject and the result (if successful will contain a Record which matches the search criteria. Using HttpClient we can now simply get the reference to this service and interact with it without ever knowing it’s IP address or port.

ServiceDiscovery discovery = ServiceDiscovery.create(v);
discovery.getRecord(
   new JsonObject().put("name", "hello-service"), found -> {
   if(found.succeeded()) {
      Record match = found.result();
      ServiceReference reference = discovery.getReference(match);
      HttpClient client = reference.get();

      client.getNow("/hello", response ->
         response.bodyHandler(
            body -> 
               System.out.println(body.toString())));
   }
});

HttpClient in Vert.x

Vert.x includes an HttpClient and associated code for interacting with HTTP protocols, obviously this can be used to write client applications or in situations where we might use a reference from service discovery.

This is a very short post which is just mean’t to demonstrate the client capability which will be used in the next post (Service discovery with Vert.x).

HttpClient client = vertx.createHttpClient();
client.getNow(8080, "localhost", "/hello", response ->
{
   response.bodyHandler(
      body -> 
         System.out.println(body.toString()));
});

The HttpClient gives us get, post, head etc. the Now postfixed named methods tend to be simpler syntax including the supply of callback and are composable using fluent style syntax.

See also Creating an HTTP client for more information.

Benchmarking my Java code using JUnitBenchmarks

As part of building some integration tests for my Java service code, I wanted to get some (micro-)benchmarks run against the tests. In C# we have the likes of NBenchmark (see my post Using NBench for performance testing), so it comes as no surprise to find libraries such as JUnitBenchmarks in Java.

Note: the JUnitBenchmarks site states it’s now deprecated in favour of using JMH, but I will cover it here anyway as it’s very simple to use and get started with and fits nicely in with existing JUnit code.

JUnitBenchmarks

First off we need to add the required dependency to our pom.xml, so add the following

<dependency>
   <groupId>com.carrotsearch</groupId>
   <artifactId>junit-benchmarks</artifactId>
   <version>0.7.2</version>
   <scope>test</scope>
</dependency>

JUnitBenchmarks, as the name suggests, integrates with JUnit. To enable our tests within the test runner we simply add a rule to the unit test, like this

public class SampleVerticleIntegrationTests {
    @Rule
    public TestRule benchmarkRule = new BenchmarkRule();

   // tests
}

This will report information on the test, like this

[measured 10 out of 15 rounds, threads: 1 (sequential)]
round: 1.26 [+- 1.04], round.block: 0.00 [+- 0.00], 
round.gc: 0.00 [+- 0.00], 
GC.calls: 5, GC.time: 0.19, 
time.total: 25.10, time.warmup: 0.00, 
time.bench: 25.10

The first line tells us that the test was actually executed 15 times (or rounds), but only 10 times was it “measured” the other 5 times were warm-ups all on a single thread – this is obviously the default for benchmarking, however what if we want to change these parameters…

If we want to be more specific about the benchmarking of various test methods we add the annotation @BenchmarkOptions, for example

@BenchmarkOptions(benchmarkRounds = 20, warmupRounds = 0)
@Test
public void testSave() {
   // our code
}

As can be seen, this is a standard test but the annotation tells JUnitBenchmarks will run the test 20 times (with no warm-up runs) and then report the benchmark information, for example

[measured 20 out of 20 rounds, threads: 1 (sequential)]
round: 1.21 [+- 0.97], round.block: 0.00 [+- 0.00], 
round.gc: 0.00 [+- 0.00], 
GC.calls: 4, GC.time: 0.22, 
time.total: 24.27, time.warmup: 0.00, 
time.bench: 24.27

As you can see the first line tells us the code was measured 20 times on a single thread with no warm-ups (as we specified).

I’m not going to cover build integration here, but checkout JUnitBenchmarks: Build Integration for such information.

What do the results actually mean?

I’ll pretty much recreate what’s on Class Result here.

Let’s look at these results…

[measured 20 out of 20 rounds, threads: 1 (sequential)]
round: 1.21 [+- 0.97], round.block: 0.00 [+- 0.00], 
round.gc: 0.00 [+- 0.00], 
GC.calls: 4, GC.time: 0.22, 
time.total: 24.27, time.warmup: 0.00, 
time.bench: 24.27

We’ve already seen that the first line tells us how many times the test was run, and how many of those runs were warm-ups. It also tells us how many threads were used in this benchmark.

round tells us the average round time in seconds (hence the example took 1.21 seconds with a stddev of +/- 0.97 seconds).
round.block tells us the average (and stddev) of blocked threads, in this example there’s no concurrency hence 0.00.
round.gc tells us the average and stddev of the round’s GC time.
GC.calls tells us the number of times GC was invoked (in this example 4 times).
GC.time tels us the accumulated time take invoking the GC (0.22 seconds in this example).
time.total tells us the total benchmark time which includes benchmarking and GC overhead.
time.warmup tells us the total warmup time which includes benchmarking and GC overhead.

Caveats

Apart from the obvious caveat that this library has been marked as deprecated (but I feel it’s still useful), when benchmarking you have to be aware that, the results may be dependent upon outside factors, such as memory available, maybe hard disk/SSD speed if tests include any file I/O, network latency etc. So such figures are best seen as approximates of performance etc.

Also there’s seems to be no way to “fail” a test, for example if the test exceeds a specified time or more GC’s than x are seen, so treat these more as informational.

Starting out with Ansible

In my previous post How to log into a Linux server without a password I generated ssh keys to allow me to log into remote Linux servers without a password. The main intention was to allow me to use ansible on a controller machine to interact with my servers.

Now let’s get started writing/running Ansible commands and playbooks.

If you want to test things on localhost we first need to edit the file /etc/ansible/hosts and add the following

localhost ansible_connection=local

We also edit the same file to add any remote hosts that we intend to connect to. We can group remote servers into named groups, so for example interacting with London based servers separately to New York servers.

To group servers within the hosts file we use syntax similar to Window ini files, i.e.

[london]
ldn_server1
ldn_server2

[new_york]
ny_server1

Playbooks and Ad-hoc commands

A playbook is simply a YAML Ansible script that we can execute against one or more servers but in some cases we just want to execute ad-hoc commands (i.e. not bother creating a playbook).

Here’s an example of and ad-hoc command which will run the df, disk free command against all our servers.

ansible all -m command -a "df"

As you can see after the ansible command (in this example) we list the servers (or group(s)) we want to run the commands against, in this case we’re running the command against all servers. The -m switch tells Ansible to use the module, in this example the module command. As this is the default module we can omit this from the entered command to instead use

ansible all -a "df"

The -a switch switch denotes the arguments that we need to send to the command module. In this instance we’re sending the Linux command df.

Once run, this will display (for each server in our hosts file) the free disk space.

Creating a playbook

So ad-hoc commands are very useful, but we can take this to another level by creating scripts (known as playbooks in Ansible) to run our commands.

Let’s create the equivalent of the ad-hoc command we just ran.

Create a directory for your playbooks and then create the file df.yml, place the following code into it

---
- hosts: all
  tasks:
  - name: Run df across all servers
    command: df
    register: out
  - debug: msg={{out.stdout}}

Now from the folder containing the yml file run ansible-playbook df.yml

The – – – can be used optionally, to denote the start of a YAML file (and … can be used optionally to end one). For some reason most examples I’ve seen have the – – – but not the …, so I’ve included it in this script, but it’s not needed.

YAML files use -hosts: to denote which servers we want this playbook to interact with, followed by the list of tasks. We can optionally name the tasks, then list the commands for each task.

In the above we create a single task to run the command df, by default Ansible will simply tell us whether the command ran, but we’ll probably want to see the output from the server calls, hence the – debug section and the register: out.

How to log into a Linux server without a password

Let’s assume we have two Linux systems, client and server.

On the client, carry out the following steps

  • ssh-keygen
  • By default saves to /home/username/.ssh/id_rsa
  • Enter a passphrase (if you want to add one to the key)
  • ssh-copy-id root@server (replace root@server with your user name and server IP or host name)
  • You should get prompted for the server’s password, enter this and the key will be copied

To test everything worked, execute the following

ssh root@server

(obviously, replacing root@server with your user name and server IP or host name) and if all worked you should log into the server without having to enter a password.

Now for each server that you need ssh access without a password, simple run ssh-copy-id root@server with the username and server that you want to connect to.

Let’s create a Vert.x docker image of our application

As you’ve probably realised from previous posts, I’m very much into the idea of deploying my server applications via Docker, so let’s take the VertxTest application and create a Docker image with it.

Thankfully we do not need to start our Dockerfile by creating a base OS then adding Java etc. The people writing Vert.x have kindly implemented a base image for us.

See Deploying a Java verticle in a docker container.

If we take the example Dockerfile from their website and make the changes for our application (just replace VERTICAL_NAME and VERTICLE_FILE with our main verticle and the JAR) we get the following file

FROM vertx/vertx3

ENV VERTICLE_NAME com.putridparrot.MainVerticle
ENV VERTICLE_FILE VertxTest-1.0-SNAPSHOT-fat.jar

ENV VERTICLE_HOME .

EXPOSE 8080

COPY $VERTICLE_FILE $VERTICLE_HOME/

WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

Place this Dockerfile into a folder along with the compiled JAR and then execute

docker build -t vertx_test  .

This creates an image name vertx_test which we can now execute. If you want to watch the output from our System.out.println execute

docker run -it -p 8080:8080 vertx_test

Once you see the HTTP server started on port 8080 outputs, the Vert.x application will be ready for you to access via your preferred web browser etc.

Vert.x event bus communicating between Verticles

In my previous post on Vert.x, I demonstrated how to create multiple Verticle’s but also how a Verticle needn’t actually be a “service” but in some cases is just a worker process or in the case of the previous post a “main” or startup process.

This leads us to better see how Vert.x is actor-like with regards to Verticles.

In situations where we have multiple Verticles and possible some of those Verticles are simply workers for the other Verticles, we would need some way of communicating across those different parts of the application. Hence Vert.x also comes with an event bus for communicating across Verticles. This is a global event bus across all our Verticles.

Let’s, for sake of simplicity change our previous example so that whenever a service is successfully routed to, an event is sent out of the EventBus to say which services was called.

So, taken the example from my previous post, add the following to the MainVerticle start method

EventBus eventBus = vertx.eventBus();

eventBus.consumer("/svc", m -> {
   System.out.println(m.body());
});

The String /svc is the topic or an address if you prefer that we publish and consume message from. In the example above, we’re listening to the event bus for the anything sent (or published) to the address /svc. How you name you addresses is totally upto you.

Don’t forget to import io.vertx.core.eventbus.EventBus to use the event bus.

Next we’ll add code to the services to send a message on the /svc address when we wish to communicate something. Here’s the code for the HelloVerticle start method with the event bus added.

@Override
public void start() {

   EventBus evBus = vertx.eventBus();

   router.route("/hello").handler(ctx -> {
      evBus.send("/svc", "hello called");

      ctx.response()
         .putHeader("content-type", "text/plain")
         .end("Hello " + ctx.queryParam("name"));
      });

      vertx.createHttpServer()
         .requestHandler(router::accept)
         .listen(8080);

   System.out.println("HTTP server started on port 8080");
}

As you can see from the highlighted lines adding an event bus and sending messages out on it, is very simple. If we updated the WorldVerticle from the previous post also (but sending “world called”) then when run, the MainVerticle will print a line to the console every time the HelloVerticle or WorldVerticle routes are called.

Publish and Send

In the example (above) I used the event buss method send to send messages out on the event bus, but send will only be delivered to at most one of the handlers of the message.

Therefore is we added the following code to each of our service Verticles

evBus.consumer("/svc", m -> {
   System.out.println("Hello " + m.body());
});

After the declaration of the evBus variable.

we’ve now got a requirement that the message on address /svc needs to be handled by multiple consumers. Running up the application as it currently stands would result in only one of the consumers receiving the messages. In my case this was the MainVerticle, however there’s no guarantee which Verticle would receive the messages as this is determined by Vert.x in a round robin fashion.

So here’s the difference between send and publish. If you change the code from send to publish in the service Verticles. Then messages will be routed to all consumers.

Multiple Verticles

In my previous post I looked at creating my first Verticle/Vert.x application. This had a single Verticle acting as a service and running up a server against port 8080. If we therefore assume that by default a Verticle represents a single service then to create multiple service we simply create multi-Verticles.

Let’s create three Verticle’s. The first will act as the “main” verticle (or run as an application) and it will register/deploy the Verticle’s that we’re using to host our two services, the HelloVerticle and WorldVerticle.

Each service will itself create/host an HttpServer. Traditionally this would tend to mean we’d have two ports exposed, one for each service, but Vert.x allows us to create multiple HttpServer’s on the same port and uses something like “round robin” to try to locate a valid route.

Check my previous post for the required pom.xml.

Here’s MainVerticle.java

package com.putridparrot;

import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;

public class MainVerticle extends AbstractVerticle {
    @Override
    public void start() {
        Router router  = Router.router(vertx);

        vertx.deployVerticle(new HelloVerticle(router));
        vertx.deployVerticle(new WorldVerticle(router));
    }
}

Notice that this Verticle’s job is to both register the other Verticle’s and acts as the main entry point to our application.

You do not need to pass the Router around in this way unless we intend to have multiple routes on the same port. So in this case we’re creating the router that will be used by both services on the same port.

Let’s look at the HelloVerticle.java service (both HelloVerticle and WorldVerticle, in this simple example, are basically the same, but I’ll reproduce all code here anyway).

package com.putridparrot;

import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;

public class HelloVerticle extends AbstractVerticle {

    private Router router;

    public HelloVerticle(Router router) {
        this.router = router;
    }

    @Override
    public void start() {
        router.route("/hello").handler(ctx -> {
            ctx.response()
                    .putHeader("content-type", "text/plain")
                    .end("Hello " + ctx.queryParam("name"));
        });

        vertx.createHttpServer()
                .requestHandler(router::accept)
                .listen(8080);

        System.out.println("HTTP Hello server started on port 8080");
    }
}

There’s nothing particularly special or different here, compared to my previous posts code. So let’s look at the WorldVerticle.java

package com.putridparrot;

import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;

public class WorldVerticle extends AbstractVerticle {

    private Router router;

    public WorldVerticle(Router router) {
        this.router = router;
    }

    @Override
    public void start() {
        router.route("/world").handler(ctx -> {
            ctx.response()
                    .putHeader("content-type", "text/plain")
                    .end("World " + ctx.queryParam("name"));
        });

        vertx.createHttpServer()
                .requestHandler(router::accept)
                .listen(8080);

        System.out.println("HTTP World server started on port 8080");
    }
}

What’s interesting is that both HelloVerticle and WorldVerticle create the HTTP server on the same port. As stated earlier Vert.x simply handles this for us without a port conflict and because we’re using a single router across these two Verticle’s Vert.x can correctly located the service for each of the following URL’s

http://localhost:8080/hello?name=Hello
http://localhost:8080/world?name=World