Rick

Rick
Rick

Wednesday, September 30, 2015

Vertx and QBit integration, the best of both worlds Microservices round 3


ManagedServiceBuilder vs. EndpointServerBuilder

ManagedServiceBuilder is in QBit admin. EndpointServerBuilder is in QBit core.ManagedServiceBuilder provides integration with statsD, consul, swagger.ManagedServiceBuilder is the glue to work well in Heroku-like environments, Swagger support, StatsD support, local stats support, health end point support, health system support, admin endpoint support, etc. EndpointServerBuilder builds a single endpoint.ManagedServiceBuilder builds a standard microservice app with health checks, metrics, and more to provide a batteries-included microservices architecture. There is some overlap with Vertx. But the plan is to build bridges from QBit health system over to Vertx health system, and from QBit metrics, stats, KPI over to Vertx stats system. (You just have to implement an interface and delegate some method calls to vertx for both QBit health system and QBit stats system).
Read all three rounds here if you missed the first two.
If you want to use QBit without statsD, consul, health checks, admin, managed shutdown and swagger support, then you just use EndpointServerBuilder. If you want statsD, consul, health checks, admin, or swagger then you use QBit Spring support or theManagedServiceBuilder. The Spring support is for another document page.ManagedServiceBuilder allows you to inject a custom health system, a custom service discovery, a custom stats system. It is an nice integration point to delegate to Vertx services.
Let's cover this in example that is like the REST example above but usesManagedServiceBuilder instead of EndpointServerBuilder.

You have a service like before

    @RequestMapping("/hello")
    public class MyRestService {

        @RequestMapping(value = "/world", method = RequestMethod.POST)
        public String hello(String body) {
            return body;
        }
    }
Note this is a simple example. QBit can do much more than this. To get a bit of an idea, please check out: QBit microservice tutorials. And be sure to check out QBit Reactive Programming.

The verticle now uses ManagedServiceBuilder instead of EndpointServerBuilder direct

    public class MyVerticle extends AbstractVerticle {

        private final int port;

        /** The systemManager can cleanly shut down anything started by the 
         * QBit ManagedServiceBuilder.
         */
        private  QBitSystemManager systemManager;

        public MyVerticle(int port) {
            this.port = port;
        }

        public void start() {

            try {


                /* Route one call to a vertx handler. */
                final Router router = Router.router(vertx); //Vertx router
                router.route("/svr/rout1/").handler(routingContext -> {
                    HttpServerResponse response = routingContext.response();
                    response.setStatusCode(202);
                    response.end("route1");
                });

                /* Route everything under /hello to QBit http server. */
                final Route qbitRoute = router.route().path("/hello/*");


                /* Vertx HTTP Server. */
                final io.vertx.core.http.HttpServer vertxHttpServer =
                        this.getVertx().createHttpServer();

                /*
                 * Use the VertxHttpServerBuilder which is a special builder for Vertx/Qbit integration.
                 */
                final HttpServer httpServer = VertxHttpServerBuilder.vertxHttpServerBuilder()
                        .setRoute(qbitRoute)
                        .setHttpServer(vertxHttpServer)
                        .setVertx(getVertx())
                        .build();


                /** Use a managed service builder. */
                final ManagedServiceBuilder managedServiceBuilder = 
                                ManagedServiceBuilder.managedServiceBuilder();

                systemManager = managedServiceBuilder.getSystemManager();

                /*
                 * Create a new service endpointServer.
                 */
                final ServiceEndpointServer endpointServer = managedServiceBuilder
                        .getEndpointServerBuilder().setUri("/")
                        .addService(new MyRestService())
                        .setHttpServer(httpServer).build();



                endpointServer.startServer();



                /*
                 * Associate the router as a request handler for the vertxHttpServer.
                 */
                vertxHttpServer.requestHandler(router::accept).listen(port);
            }catch (Exception ex) {
                ex.printStackTrace();
                throw new IllegalStateException(ex);
            }
        }

        public void stop() {

            if (systemManager!=null) {
                systemManager.shutDown();
            }
        }

    }
The important bits to see are that we are now using the ManagedServiceBuilder in the verticle start method

ManagedServiceBuilder

                /** Use a managed service builder. */
                final ManagedServiceBuilder managedServiceBuilder = 
                                ManagedServiceBuilder.managedServiceBuilder();

                systemManager = managedServiceBuilder.getSystemManager();
And that we are now using the EndpointServerBuilder that is managed by theManagedServiceBuilder (managedServiceBuilder.getEndpointServerBuilder).

ManagedServiceBuilder.getEndpointServerBuilder

                /*
                 * Create a new service endpointServer.
                 */
                final ServiceEndpointServer endpointServer = managedServiceBuilder
                        .getEndpointServerBuilder().setUri("/")
                        .addService(new MyRestService())
                        .setHttpServer(httpServer).build();
Note that the QBit systemManager ensures that all services that QBit started will get properly shutdown.

Proper shutdown

        public void stop() {

            if (systemManager!=null) {
                systemManager.shutDown();
            }
        }
This example is one of the unit tests for the admin package.

Complete example showing how to use ManagedServiceBuilder with Vertx to build microservices

package io.advantageous.qbit.vertx;

import io.advantageous.qbit.admin.ManagedServiceBuilder;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.annotation.RequestMethod;
import io.advantageous.qbit.http.client.HttpClient;
import io.advantageous.qbit.http.client.HttpClientBuilder;
import io.advantageous.qbit.http.request.HttpTextResponse;
import io.advantageous.qbit.http.server.HttpServer;
import io.advantageous.qbit.server.ServiceEndpointServer;
import io.advantageous.qbit.system.QBitSystemManager;
import io.advantageous.qbit.util.PortUtils;
import io.advantageous.qbit.vertx.http.VertxHttpServerBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

public class VertxManagedServiceBuilderIntegrationTest {

    private Vertx vertx;
    private TestVerticle testVerticle;
    private int port;

    @RequestMapping("/hello")
    public static class TestRestService {

        @RequestMapping(value = "/world", method = RequestMethod.POST)
        public String hello(String body) {
            return body;
        }
    }

    public static class TestVerticle extends AbstractVerticle {

        private final int port;

        private  QBitSystemManager systemManager;

        public TestVerticle(int port) {
            this.port = port;
        }

        public void start() {

            try {


                /* Route one call to a vertx handler. */
                final Router router = Router.router(vertx); //Vertx router
                router.route("/svr/rout1/").handler(routingContext -> {
                    HttpServerResponse response = routingContext.response();
                    response.setStatusCode(202);
                    response.end("route1");
                });

                /* Route everything under /hello to QBit http server. */
                final Route qbitRoute = router.route().path("/hello/*");


                /* Vertx HTTP Server. */
                final io.vertx.core.http.HttpServer vertxHttpServer =
                        this.getVertx().createHttpServer();

                /*
                 * Use the VertxHttpServerBuilder which is a special builder for Vertx/Qbit integration.
                 */
                final HttpServer httpServer = VertxHttpServerBuilder.vertxHttpServerBuilder()
                        .setRoute(qbitRoute)
                        .setHttpServer(vertxHttpServer)
                        .setVertx(getVertx())
                        .build();


                /** Use a managed service builder. */
                final ManagedServiceBuilder managedServiceBuilder = ManagedServiceBuilder.managedServiceBuilder();

                systemManager = managedServiceBuilder.getSystemManager();

                /*
                 * Create a new service endpointServer.
                 */
                final ServiceEndpointServer endpointServer = managedServiceBuilder
                        .getEndpointServerBuilder().setUri("/")
                        .addService(new TestRestService())
                        .setHttpServer(httpServer).build();



                endpointServer.startServer();



                /*
                 * Associate the router as a request handler for the vertxHttpServer.
                 */
                vertxHttpServer.requestHandler(router::accept).listen(port);
            }catch (Exception ex) {
                ex.printStackTrace();
                throw new IllegalStateException(ex);
            }
        }

        public void stop() {

            if (systemManager!=null) {
                systemManager.shutDown();
            }
        }

    }

    @Before
    public void setup() throws Exception{


        final CountDownLatch latch = new CountDownLatch(1);
        port = PortUtils.findOpenPortStartAt(9000);
        testVerticle = new TestVerticle(port);
        vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(5));
        vertx.deployVerticle(testVerticle, res -> {
            if (res.succeeded()) {
                System.out.println("Deployment id is: " + res.result());
            } else {
                System.out.println("Deployment failed!");
                res.cause().printStackTrace();
            }
            latch.countDown();
        });


        latch.await(5, TimeUnit.SECONDS);
    }

    @Test
    public void test() {

        final HttpClient client = HttpClientBuilder.httpClientBuilder().setHost("localhost").setPort(port).buildAndStart();
        final HttpTextResponse response = client.postJson("/svr/rout1/", "\"hi\"");
        assertEquals(202, response.code());
        assertEquals("route1", response.body());


        final HttpTextResponse response2 = client.postJson("/hello/world", "\"hi\"");
        assertEquals(200, response2.code());
        assertEquals("\"hi\"", response2.body());

    }


    @After
    public void tearDown() throws Exception {

        final CountDownLatch latch = new CountDownLatch(1);
        vertx.close(res -> {
            if (res.succeeded()) {
                System.out.println("Vertx is closed? " + res.result());
            } else {
                System.out.println("Vertx failed closing");
            }
            latch.countDown();
        });


        latch.await(5, TimeUnit.SECONDS);
        vertx = null;
        testVerticle = null;

    }
}
Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training