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.
Join the DZone community and get the full member experience.
Join For Freephilosophy 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:
public static final string new_hire_channel = "com.mycompnay.employee.new";
public static final string payroll_adjustment_channel = "com.mycompnay.employee.payroll";
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 +
'}';
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());
}
}
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();
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));
flushserviceproxy(employeehiringserviceclientproxy);
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());
}
}
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:
Opinions expressed by DZone contributors are their own.
Comments