Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Introduction to QBit—A Powerful Microservice Library for Java

DZone's Guide to

Introduction to QBit—A Powerful Microservice Library for Java

A queuing library is a powerful component for microservice deployement. Learn more about QBit, a Java based option for this functionality.

· Integration Zone
Free Resource

Learn how API management supports better integration in Achieving Enterprise Agility with Microservices and API Management, brought to you in partnership with 3scale

QBit is a queuing library for  microservices. It is similar to many other projects like  AkkaSpring Reactor , etc. QBit is just a library not a platform. QBit has libraries to put a service behind a  queue. You can use QBit queues directly or you can create a service. QBit services can be exposed by  WebSocketHTTPHTTP pipeline, and other types of remoting. A service in QBit is a Java class whose methods are executed behind service queues. QBit implements  apartment model threading and is similar to the  Actor model or a better description would be  Active Objects. QBit does not use a disruptor. It uses regular Java Queues. QBit can do north of 100 million ping pong calls per second which is an amazing speed (seen as high as 200M). QBit also supports calling services via  REST, and WebSocket. QBit is microservices in the pure Web sense:  JSON, HTTP, WebSocket, etc.

Philosophy behind QBit - why is QBit so fast?

When you hear the word fast, the first thing that comes to mind is a racing car/driver. how does a driver get the best out of his car and win the race? it is simply by having a great understanding of how his car works. He does not need to know how to rebuild the engine or the transmission etc... he just needs to know how it works, and work in harmony with his car.

I believe it is very similar in computer science, developers can write very fast code if they have a good understanding of how the hardware used in computing (CPU, L1 cache, L2, L3, main memory, etc...) works. Take a look at this picture of a Formula one steering wheel:


It looks scary compared to an ordinary steeling wheel huh, don't be intimidated by all the nobs you see; for example one of them is used to make the car go faster but at the same time it will wear the engine down and consume more fuel, another one is to adjust the front and rear brake balance. so all these nobs are just tools for the driver to get the most speed out of his car, this explains "mechanical sympathy" and it is explained this way by Martin Thompson.

QBit uses this philosophy to deliver a very fast code/service execution, a good example to show you some of QBit's methodology is the event bus example. The event bus is loosely modeled after the Vertx event bus, except with QBit you can send strongly typed objects, JSON, Maps, etc. the event bus was written with the QBit library so it is not only a fast event bus, but it is an example of how to use QBit to write a fast service. 

Event Bus Example:

Let's build a set of services that handles when a new employee is hired. We want to add the employee to the payroll system, enroll the employee into the benefits system and we want to invite them to our community outreach program.
We will define four services but the first service will not know about the other service and so on. Then we can add more services in the future which can listen to events and participate in the new employee being hired.
This example uses two channels:  
  public static final String NEW_HIRE_CHANNEL = "com.mycompnay.employee.new";

    public static final String PAYROLL_ADJUSTMENT_CHANNEL = "com.mycompnay.employee.payroll";
The first channel  NEW_HIRE_CHANNEL is where we send new employee objects when they are hired. A whole slew of services could be listening to this channel.
An employee object looks like this:
  public Employee(String firstName, int employeeId) {
            this.firstName = firstName;
            this.employeeId = employeeId;
        }
Here are the getters for this example:
 public String getFirstName() {
            return firstName;
        }

        public int getEmployeeId() {
            return employeeId;
        }

        @Override
        public String toString() {
            return "Employee{" +
                    "firstName='" + firstName + '\'' +
                    ", employeeId=" + employeeId +
                    '}';
This example has three services:  EmployeeHiringServiceBenefitsService, and  PayrollService.
These services are inproc services. QBit supports WebSocket, HTTP and REST remote services as well, but for now, let's focus on inproc services. If you understand inproc then you will understand remote.

The EmployeeHiringService actually fires off the events to the other two services:

public class EmployeeHiringService {



        public void hireEmployee(final Employee employee) {

            int salary = 100;
            System.out.printf("Hired employee %s\n", employee);

            //Does stuff to hire employee

            //Sends events
            final EventManager eventManager = 
                              serviceContext().eventManager();

            eventManager.send(NEW_HIRE_CHANNEL, employee);

            eventManager.sendArray(PAYROLL_ADJUSTMENT_CHANNEL, 
                                                employee, salary);


        }

    }

In this example we are using the services within QBit doing that will give us a alot of advantages; the events come into the same queue that handles the method calls so the events method calls are thread safe. Everything comes in on the same thread, events and methods. To wire off the services into qbit, it is very simple you just do the following:

EmployeeHiringService employeeHiring = new EmployeeHiringService();
Service employeeHiringService = serviceBuilder()
                .setServiceObject(employeeHiring)
                .setInvokeDynamic(false).build();

When working inside of a QBit Service, you can access the event manager using serviceContext().eventManager(). If you access it this way, the flushing is taken care of for you. Flushing messages to other services in batches helps with the performance. You have to flush after you use a client proxy. The eventManager() method returns a client proxy. When running inside of QBit, you do not have to flush, it is done for you at the time when/where you will get the most performance out of the system. This is what allows the event manager to send so many messages in such a short period of time. Not only send the messages but en-queue them onto other service queues.

Notice for the EmployeeHiringService that we call sendArray so we can send the employee and his salary. The listener for PAYROLL_ADJUSTMENT_CHANNEL will have to handle both an employee and an int that represents the new employee salary.

The BenefitsService listens for new employees being hired so it can enroll them into the benefits system. The OnEvent annotation is an alternative to @Listen, you can also use the later:

public class BenefitsService {

        @OnEvent(NEW_HIRE_CHANNEL)
        public void enroll(final Employee employee) {

            System.out.printf("Employee enrolled into benefits system employee %s %d\n",
                    employee.getFirstName(), employee.getEmployeeId());

        }

    }
The employee is the Employee object from the  EmployeeHiringService. so you can get your benefits.

As i showed you before; here we create the objects and  wire all the services into the QBit queuing apparatus.

  EmployeeHiringService employeeHiring = new EmployeeHiringService();
        PayrollService payroll = new PayrollService();
        BenefitsService benefits = new BenefitsService();


        Service employeeHiringService = serviceBuilder()
                .setServiceObject(employeeHiring)
                .setInvokeDynamic(false).build();

        Sys.sleep(100);


        Service payrollService = serviceBuilder()
                .setServiceObject(payroll)
                .setInvokeDynamic(false).build();

        Sys.sleep(100);

        Service employeeBenefitsService = serviceBuilder()
                .setServiceObject(benefits)
                .setInvokeDynamic(false).build();







The objects  employeeHiringServicepayrollServiceemployeeBenefitsService are QBit services.
To start things off, you need to get a client proxy to the  EmployeeHiringService using the  employeeHiringService.
Also to invoke a method on a QBit service, you want to get a client proxy. A client proxy will send messages to a service. The service will get those messages as method calls.
Every call is sent over a high-speed internal inproc queue. You can also use a client proxy to talk to QBit over WebSockets.

To create a proxy you use the createProxy method of Service:

 EmployeeHiringServiceClient employeeHiringServiceClientProxy = 

                   employeeHiringService.createProxy(EmployeeHiringServiceClient.class);

Now that we created our proxy, we can send messages to it. Here the client proxy is made and used to hire Fadi with an ID number of 1.

employeeHiringServiceClientProxy.hireEmployee(new Employee("Fadi", 1));
Every so often, we have to flush calls to the client proxy.
The client proxy will flush calls every time the queue batch size is met. So if the queue batch size was set to 5, then it would flush every five calls. But no matter, when you are done making calls, you should flush the calls as follows:
  flushServiceProxy(employeeHiringServiceClientProxy);
If you were making calls to a service in a tight loop, you may want to flush every ten calls or every 100 calls. Or you may want to flush related calls.
If you set the batch size to 1, then every method calls is flushed, but this hampers performance.
If you use the event manager service, it will get auto flushed for you but in an extremely performant way. We may provide similar support for injected client proxies into a service.
Now we are all done. But you know how apps are. Later someone decides to create a community outreach program. Now what?
Simple enough, we just add another service to listen to the new hire event channel.
First we define our POJO:
 public class VolunteerService {

        @OnEvent(NEW_HIRE_CHANNEL)
        public void invite(final Employee employee) {

            System.out.printf("Employee will be invited to the community outreach program %s %d\n",
                    employee.getFirstName(), employee.getEmployeeId());

        }

    }
So now we have our new service.
Let's wire it in:
VolunteerService volunteering = new VolunteerService(); 
Service volunteeringService = serviceBuilder()
                .setServiceObject(volunteering)
                .setInvokeDynamic(false).build();

This is the output from this app:

Hired employee Employee{firstName='Fadi', employeeId=1}
Employee added to payroll  Fadi 1 100
Employee enrolled into benefits system employee Fadi 1
Employee will be invited to the community outreach program Fadi 1

Now our new employee is hired, added to the payroll system, enrolled into benefits and are invited to our community outreach program. Here is the full code listing:

package io.advantageous.qbit.example.events;

import io.advantageous.qbit.annotation.OnEvent;
import io.advantageous.qbit.events.EventManager;
import io.advantageous.qbit.service.Service;
import io.advantageous.qbit.service.ServiceProxyUtils;
import org.boon.Lists;
import org.boon.core.Sys;

import static io.advantageous.qbit.service.ServiceBuilder.serviceBuilder;
import static io.advantageous.qbit.service.ServiceContext.serviceContext;
import static io.advantageous.qbit.service.ServiceProxyUtils.flushServiceProxy;


public class EmployeeEventExample {

    public static final String NEW_HIRE_CHANNEL = "com.mycompnay.employee.new";

    public static final String PAYROLL_ADJUSTMENT_CHANNEL = "com.mycompnay.employee.payroll";

    public class Employee {
        final String firstName;
        final int employeeId;

        public Employee(String firstName, int employeeId) {
            this.firstName = firstName;
            this.employeeId = employeeId;
        }

        public String getFirstName() {
            return firstName;
        }

        public int getEmployeeId() {
            return employeeId;
        }

        @Override
        public String toString() {
            return "Employee{" +
                    "firstName='" + firstName + '\'' +
                    ", employeeId=" + employeeId +
                    '}';
        }
    }

    interface EmployeeHiringServiceClient {
        void hireEmployee(final Employee employee);

    }

    public class EmployeeHiringService {



        public void hireEmployee(final Employee employee) {

            int salary = 100;
            System.out.printf("Hired employee %s\n", employee);

            //Does stuff to hire employee

            //Sends events
            final EventManager eventManager = 
                              serviceContext().eventManager();

            eventManager.send(NEW_HIRE_CHANNEL, employee);

            eventManager.sendArray(PAYROLL_ADJUSTMENT_CHANNEL, 
                                                employee, salary);


        }

    }



    public class BenefitsService {

        @OnEvent(NEW_HIRE_CHANNEL)
        public void enroll(final Employee employee) {

            System.out.printf("Employee enrolled into benefits system employee %s %d\n",
                    employee.getFirstName(), employee.getEmployeeId());

        }

    }

    public class VolunteerService {

        @OnEvent(NEW_HIRE_CHANNEL)
        public void invite(final Employee employee) {

            System.out.printf("Employee will be invited to the community outreach program %s %d\n",
                    employee.getFirstName(), employee.getEmployeeId());

        }

    }



    public class PayrollService {

        @OnEvent(PAYROLL_ADJUSTMENT_CHANNEL)
        public void addEmployeeToPayroll(final Employee employee, int salary) {

            System.out.printf("Employee added to payroll  %s %d %d\n",
                    employee.getFirstName(), employee.getEmployeeId(), salary);

        }

    }

    public static void main(String... args) {


        EmployeeHiringService employeeHiring = new EmployeeHiringService();
        PayrollService payroll = new PayrollService();
        BenefitsService benefits = new BenefitsService();
        VolunteerService volunteering = new VolunteerService();


        Service employeeHiringService = serviceBuilder()
                .setServiceObject(employeeHiring)
                .setInvokeDynamic(false).build();

        Sys.sleep(100);


        Service payrollService = serviceBuilder()
                .setServiceObject(payroll)
                .setInvokeDynamic(false).build();

        Sys.sleep(100);

        Service employeeBenefitsService = serviceBuilder()
                .setServiceObject(benefits)
                .setInvokeDynamic(false).build();


        Service volunteeringService = serviceBuilder()
                .setServiceObject(volunteering)
                .setInvokeDynamic(false).build();

        EmployeeHiringServiceClient employeeHiringServiceClientProxy = 

                   employeeHiringService.createProxy(EmployeeHiringServiceClient.class);

        employeeHiringServiceClientProxy.hireEmployee(new Employee("Fadi", 1));

        flushServiceProxy(employeeHiringServiceClientProxy);

        Sys.sleep(5_000);

    }
}

Summary:

Comparing the Formula one steering wheel with the event bus example, you can see that the driver utilized the nobs to get the most speed out of his car and the developer sets the queue batch, so that the client proxy will flush accordingly this will enable the developer to get the most speed out of his computing hardware.  hope you enjoyed this article, we appreciate your comments and feedback, stay tuned for more articles. thanks!


QBit Lingo

QBit is a Java microservice lib supporting REST, JSON and WebSocket. It is written in Java but I might one day write a version in Rust or Go or C# (but that would require a large payday).

  • Service POJO (plain old Java object) behind a queue that can receive method calls via proxy calls or events (May have one thread managing events, method calls, and responses or two one for method calls and events and the other for responses so response handlers do not block service. One is faster unless responses block). Services can use Spring MVC style REST annotations to expose themselves to the outside world via REST and WebSocket.
  • ServiceBundle Many POJOs behind one response queue and many receive queues. There may be one thread for all responses or not. They also can be one receive queue.
  • Queue A thread managing a queue. It supports batching. It has events for empty, reachedLimit, startedBatch, idle. You can listen to these events from services that sit behind a queue. You don't have to use Services. You can use Queue's direct.
  • ServiceServer ServiceBundle that is exposed to REST and WebSocket communication
  • EventBus EventBus is a way to send a lot of messages to services that may be loosely coupled
  • ClientProxy Way to invoke service through async interface, service can be inproc (same process) or remoted over WebSocket.
  • Non-blocking QBit is a non-blocking lib. You use CallBacks via Java 8 Lambdas. You can also send event messages and get replies. Messaging is built into the system so you can easily coordinate complex tasks.
  • Speed There is a lot of room for improvement with Speed. But already QBit is VERY fast. 200M+ TPS inproc ping pong, 10M-20M+ TPS event bus, 500K TPS RPC calls over WebSocket/JSON, etc. More work needs to be done to improve speed, but now it is fast enough where I am working more with usability.

Related articles:

  • [Detailed Tutorial] QBit microservice example
  • [Doc] Queue Callbacks for QBit queue based services
  • [Quick Start] Building a simple Rest web microservice server with QBit
  • [Quick Start] Building a TODO web microservice client with QBit
  • [Quick Start] Building a TODO web microservice server with QBit
  • [Quick Start] Building boon for the QBit microservice engine
  • [Quick Start] Building QBit the microservice lib for Java
  • [Rough Cut] Delivering up Single Page Applications from QBit Java JSON Microservice lib
  • [Rough Cut] Working with event bus for QBit the microservice engine
  • [Rough Cut] Working with inproc MicroServices
  • [Rough Cut] Working with private event bus for inproc microservices
  • [Rough Cut] Working with strongly typed event bus proxies for QBit Java Microservice lib
  • [Rough Cut] Working with System Manager for QBit Mircoservice lib
  • [Z Notebook] More benchmarking internal
  • [Z Notebook] Performance testing for REST
  • [Z Notebook] Roadmap
  • Home
  • Introduction to QBit
  • Local Service Proxies
  • QBit Boon New Wave of JSON HTTP and Websocket
  • QBit Docs
  • Unleash the power of your APIs with future-proof API management - Create your account and start your free trial today, brought to you in partnership with 3scale.

    Topics:
    java ,queuing ,microservices ,rest ,json ,websocket

    Opinions expressed by DZone contributors are their own.

    The best of DZone straight to your inbox.

    SEE AN EXAMPLE
    Please provide a valid email address.

    Thanks for subscribing!

    Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
    Subscribe

    {{ parent.title || parent.header.title}}

    {{ parent.tldr }}

    {{ parent.urlSource.name }}