If you are new to QBit. It might make more sense to skim the overview. We suggest reading the landing page of the QBit Microservices Lib's wiki for a background on QBit. This will let you see the forrest while the tutorials are inspecting the trees. There is also a lot of documents linked to off of the wiki landing page as well as in the footer section of the tutorials.
QBit Microservices Java Lib RESTful and Swaggerific API Gateway Support- Part 3
QBit Microserivces Lib provides three way to remotely talk to microservices out of the box, WebSocket, HTTP REST and the QBit event bus. (Communication is also pluggable so it is easy to send QBit calls or events over a message bus, or other means.)
This tutorial is going to focus on QBit and its REST support. It covers QBit REST support and its support for runtime stats and API Gateway support with Swagger. It just works.
Here is an example program that we are going to examine.
Here is an example program that we are going to examine.
REST API for Microservices
package com.mammatustech.todo;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.annotation.RequestMethod;
import io.advantageous.qbit.annotation.RequestParam;
import java.util.*;
@RequestMapping(value = "/todo-service", description = "Todo service")
public class TodoService {
private final Map<String, Todo> todoMap = new TreeMap<>();
@RequestMapping(value = "/todo", method = RequestMethod.POST,
description = "add a todo item to the list", summary = "adds todo",
returnDescription = "returns true if successful")
public boolean add(final Todo todo) {
todoMap.put(todo.getId(), todo);
return true;
}
@RequestMapping(value = "/todo", method = RequestMethod.DELETE,
description = "Deletes an item by id", summary = "delete a todo item")
public void remove(@RequestParam(value = "id", description = "id of Todo item to delete")
final String id) {
todoMap.remove(id);
}
@RequestMapping(value = "/todo", method = RequestMethod.GET,
description = "List all items in the system", summary = "list items",
returnDescription = "return list of all items in system")
public List<Todo> list() {
return new ArrayList<>(todoMap.values());
}
}
Let's start with the
addTodo method
.Add TODO
@RequestMapping(value = "/todo", method = RequestMethod.POST,
description = "add a todo item to the list", summary = "adds todo",
returnDescription = "returns true if successful")
public boolean add(final Todo todo) {
todoMap.put(todo.getId(), todo);
return true;
}
The
RequestMapping
is inspired from Spring MVC's REST support. In fact, if you use Spring's annotation, QBit can use it as is.
RequestMapping
annotation
package io.advantageous.qbit.annotation;
...
/**
* Used to map Service method to URIs in an HTTP like protocol.
* @author Rick Hightower
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.TYPE})
public @interface RequestMapping {
/**
* Primary mapping expressed by this annotation.
* For HTTP, this would be the URI. Or part of the URI after the parent URI context
* be it ServletApp Context or some other parent context.
*
* @return a request mapping, URIs really
*/
String[] value() default {};
/**
* HTTP request methods must be:
* GET, or POST or WebSocket.
*
* @return or RequestMethods that are supported by this end point
*/
RequestMethod[] method() default {RequestMethod.GET};
/**
* Used to document endpoint
* @return description
*/
String description() default "no description";
/**
* Used to document endpoint
* @return description
*/
String returnDescription() default "no return description";
/**
* Used to document endpoint
* @return summary
*/
String summary() default "no summary";
}
Both Boon and QBit have the same philosophy on annotations. You don't have to use our annotations. You can create your own and as long as the class name and attributes match, we can use the annotations that you provide. This way if you decide to switch away from Boon or QBit, it is easier and you can keep your annotations.
QBit added
description
, summary
and returnDescription
attributes to ourRequestMapping
annotation. QBit does this to capture the meta-data and expose it for API-Gateway style client generation and documentation using tools like Swagger. QBit is not tied to Swagger. It has its own service meta-data format, but QBit converts its service meta-data to Swagger format to get access to the wealth of Swagger tools and clients. Swagger, and tools like Swagger, makes documenting your API easy and accessible. QBit fully embraces generating service meta-data and Swagger because it embraces the concepts behind building Microserivce API Gateways, which is essential part ofMicroservices Architecture to support web and mobile Microservices.
The add method specifies the HTTP method as POST, and the URI is mapped to
"/todo"
Add TODO
@RequestMapping(value = "/todo", method = RequestMethod.POST, ...)
public boolean add(final Todo todo) {
todoMap.put(todo.getId(), todo);
return true;
}
You can specify the HTTP methods
POST
, GET
, DELETE
, PUT
, etc. Generally speaking you use GET
to read, POST
to create new, PUT
to update or add to a list, and DELETE
to destroy, delete or remove. It is a bad idea to use a GET to modify something (just in case a tool crawls your web service).
The next method is a DELETE operation which removes a Todo item.
Remove TODO
@RequestMapping(value = "/todo", method = RequestMethod.DELETE ...)
public void remove(@RequestParam(value = "id", description = "id of Todo item to delete")
final String id) {
todoMap.remove(id);
}
Notice that the
remove
method also takes an id
of the Todo item, which we want to remove from the map. The @RequestParam
is also modeled after the one from Spring MVC as that is the one that most people are familiar with. It just pulls the id
from a request parameter.
Notice that the method returns
void
. Whenever you provide a method that provides avoid
, it does not wait for the method to return to send a HTTP response code 202
Accepted. If you want the service to capture any exceptions that might occur and send the exception message, then return anything but void
from a method call. Returningvoid
means fire and forget which is very fast, but does not have a way to notify the client of errors.
Let's show an example that would report errors.
Remove TODO with error handling and a return
@RequestMapping(value = "/todo", method = RequestMethod.DELETE)
public boolean remove(final @RequestParam("id") String id) {
Todo remove = todoMap.remove(id);
return remove!=null;
}
If this operation for any reason throws an exception, then the client with get the exception message and an HTTP response code of 500 Internal Server Error.
Briefly, as you know you might have to call a downstream service to save the actual Todo item in Cassandra or a relational database. In this case, you would use a
Callback
as follows:Callback version of remove
@RequestMapping(value = "/todo", method = RequestMethod.DELETE)
public void remove(final Callback<Boolean> callback,
final @RequestParam("id") String id) {
Todo remove = todoMap.remove(id);
callback.accept(remove!=null);
}
A
Callback
allows you to response async to a request so that the method call does not block the IO threads. We will cover callbacks more later in the tutorial series.
Side Note: To learn more about
Callback
s go to the QBit wiki and search for Callback
in the Pages sidebar, to see some Cassandra async examples and SOLR examples of Callback
s go to reactively handling async callbacks with QBit Reactive Microservices.
Lastly in this example we have list todos.
List Todos
@RequestMapping(value = "/todo", method = RequestMethod.GET ... )
public List<Todo> list() {
return new ArrayList<>(todoMap.values());
}
Since the
list
method is not modifying the Todo items but merely returning them, then we can return the entire list.Using Callbacks
In general you use a Callback to handle multiple downstream services that may be doing additional processing or IO but in separate services and then in a non-blocking way return the result to the original client.
This service could be rewritten using
Callback
s as follows:REST API with callback for Microservices
package com.mammatustech.todo;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.annotation.RequestMethod;
import io.advantageous.qbit.annotation.RequestParam;
import io.advantageous.qbit.reactive.Callback;
import java.util.*;
@RequestMapping("/todo-service")
public class TodoService {
private final Map<String, Todo> todoMap = new TreeMap<>();
@RequestMapping(value = "/todo", method = RequestMethod.POST)
public void add(final Callback<Boolean> callback, final Todo todo) {
todoMap.put(todo.getId(), todo);
callback.accept(true);
}
@RequestMapping(value = "/todo", method = RequestMethod.DELETE)
public void remove(final Callback<Boolean> callback,
final @RequestParam("id") String id) {
Todo remove = todoMap.remove(id);
callback.accept(remove!=null);
}
@RequestMapping(value = "/todo", method = RequestMethod.GET)
public void list(final Callback<ArrayList<Todo>> callback) {
callback.accept(new ArrayList<>(todoMap.values()));
}
}
Again callbacks would make more sense if we were talking to a downstream service that did additional IO as follows:
Callback
@RequestMapping(value = "/todo", method = RequestMethod.POST)
public void add(final Callback<Boolean> callback, final Todo todo) {
todoMap.put(todo.getId(), todo);
todoRepo.add(callback, todo);
}
Of course there are a lot more details to cover then this and we will cover them in due course.
*Remember: To learn more about
Callback
s go to the QBit wiki and search forCallback
in the Pages sidebar, to see some Cassandra async examples and SOLR examples of Callback
s go to reactively handling async callbacks with QBit Reactive Microservices. Or just continue reading this tutorial series.TODO Info how to run the example
The complete code for the microservice Todo service without Callbacks is in github.
You can find the complete code listing for the microservice Todo service with Callbacks.
Running the todo service with gradle is shown as follows:
Running the todo service
$ gradle run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Todo Server and Admin Server started
The TODO service has a gradle build file that uses the Gradle application plugin as follows:
group 'qbit-ex'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'application'
mainClassName = "com.mammatustech.todo.TodoServiceMain"
compileJava {
sourceCompatibility = 1.8
}
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
compile group: 'io.advantageous.qbit', name: 'qbit-admin', version: '0.9.0.M2'
compile group: 'io.advantageous.qbit', name: 'qbit-vertx', version: '0.9.0.M2'
}
In the main method that launches the app we setup some meta-data about the service so we can export it later via Swagger.
TodoServiceMain
package com.mammatustech.todo;
import io.advantageous.qbit.admin.ManagedServiceBuilder;
import io.advantageous.qbit.meta.builder.ContextMetaBuilder;
public class TodoServiceMain {
public static void main(final String... args) throws Exception {
/* Create the ManagedServiceBuilder which manages a clean shutdown, health, stats, etc. */
final ManagedServiceBuilder managedServiceBuilder =
ManagedServiceBuilder.managedServiceBuilder()
.setRootURI("/v1") //Defaults to services
.setPort(8888); //Defaults to 8080 or environment variable PORT
/* Context meta builder to document this endpoint. */
ContextMetaBuilder contextMetaBuilder = managedServiceBuilder.getContextMetaBuilder();
contextMetaBuilder.setContactEmail("lunati-not-real-email@gmail.com");
contextMetaBuilder.setDescription("A great service to show building a todo list");
contextMetaBuilder.setContactURL("http://www.bwbl.lunati/master/of/rodeo");
contextMetaBuilder.setContactName("Buffalo Wild Bill Lunati");
contextMetaBuilder.setLicenseName("Commercial");
contextMetaBuilder.setLicenseURL("http://www.canttouchthis.com");
contextMetaBuilder.setTitle("Todo Title");
contextMetaBuilder.setVersion("47.0");
managedServiceBuilder.getStatsDReplicatorBuilder().setHost("192.168.59.103");
managedServiceBuilder.setEnableStatsD(true);
/* Start the service. */
managedServiceBuilder.addEndpointService(new TodoService()) //Register TodoService
.getEndpointServerBuilder()
.build().startServer();
/* Start the admin builder which exposes health end-points and swagger meta data. */
managedServiceBuilder.getAdminBuilder().build().startServer();
System.out.println("Todo Server and Admin Server started");
}
}
The Todo class is just a POJO.
Todo class
package com.mammatustech.todo;
public class Todo {
private String id;
private final String name;
private final String description;
private final long createTime;
public Todo(String name, String description, long createTime) {
this.name = name;
this.description = description;
this.createTime = createTime;
this.id = name + "::" + createTime;
}
public String getId() {
if (id == null) {
this.id = name + "::" + createTime;
}
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public long getCreateTime() {
return createTime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Todo todo = (Todo) o;
if (createTime != todo.createTime) return false;
return !(name != null ? !name.equals(todo.name) : todo.name != null);
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (int) (createTime ^ (createTime >>> 32));
return result;
}
}
The
TodoService
is as follows.TodoService without callbacks
package com.mammatustech.todo;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.annotation.RequestMethod;
import io.advantageous.qbit.annotation.RequestParam;
import java.util.*;
/**
* Default port for admin is 7777.
* Default port for main endpoint is 8080.
*
* <pre>
* <code>
*
* Access the service:
*
* $ curl http://localhost:8888/v1/...
*
*
* To see swagger file for this service:
*
* $ curl http://localhost:7777/__admin/meta/
*
* To see health for this service:
*
* $ curl http://localhost:8888/__health
* Returns "ok" if all registered health systems are healthy.
*
* OR if same port endpoint health is disabled then:
*
* $ curl http://localhost:7777/__admin/ok
* Returns "true" if all registered health systems are healthy.
*
*
* A node is a service, service bundle, queue, or server endpoint that is being monitored.
*
* List all service nodes or endpoints
*
* $ curl http://localhost:7777/__admin/all-nodes/
*
*
* List healthy nodes by name:
*
* $ curl http://localhost:7777/__admin/healthy-nodes/
*
* List complete node information:
*
* $ curl http://localhost:7777/__admin/load-nodes/
*
*
* Show service stats and metrics
*
* $ curl http://localhost:8888/__stats/instance
* </code>
* </pre>
*/
@RequestMapping(value = "/todo-service", description = "Todo service")
public class TodoService {
private final Map<String, Todo> todoMap = new TreeMap<>();
@RequestMapping(value = "/todo", method = RequestMethod.POST,
description = "add a todo item to the list", summary = "adds todo",
returnDescription = "returns true if successful")
public boolean add(final Todo todo) {
todoMap.put(todo.getId(), todo);
return true;
}
@RequestMapping(value = "/todo", method = RequestMethod.DELETE,
description = "Deletes an item by id", summary = "delete a todo item")
public void remove(@RequestParam(value = "id", description = "id of Todo item to delete")
final String id) {
todoMap.remove(id);
}
@RequestMapping(value = "/todo", method = RequestMethod.GET,
description = "List all items in the system", summary = "list items",
returnDescription = "return list of all items in system")
public List<Todo> list() {
return new ArrayList<>(todoMap.values());
}
}
QBit comes with a lib to easily (and quickly) make REST calls.
Calling todo service from Java using QBit's http client support
package com.mammatustech.todo;
import io.advantageous.boon.json.JsonFactory;
import io.advantageous.qbit.http.HTTP;
public class HttpClient {
public static void main(final String... args) throws Exception {
for (int index = 0; index < 100; index++) {
HTTP.postJSON("http://localhost:8888/v1/todo-service/todo",
JsonFactory.toJson(new Todo("name" + index,
"desc" + index, System.currentTimeMillis() )));
System.out.print(".");
}
}
}
You can also make async calls with QBit's http client lib.
Swagger generation
You can import the JSON meta data into Swagger.
$ curl http://localhost:7777/__admin/meta/
{
"swagger": "2.0",
"info": {
"title": "application title goes here",
"description": "A great service to show building a todo list",
"contact": {
"name": "Buffalo Wild Bill Lunati",
"url": "http://www.bwbl.lunati/master/of/rodeo",
"email": "lunati-not-real-email@gmail.com"
},
"version": "47.0",
"license": {
"name": "Commercial",
"url": "http://www.canttouchthis.com"
}
},
"host": "localhost:8888",
"basePath": "/v1",
"schemes": [
"http",
"https",
"wss",
"ws"
],
"consumes": [
"application/json"
],
"definitions": {
"Todo": {
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"createTime": {
"type": "integer",
"format": "int64"
}
}
}
},
"produces": [
"application/json"
],
"paths": {
"/todo-service/todo": {
"get": {
"operationId": "list",
"summary": "list items",
"description": "List all items in the system",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "return list of all items in system",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Todo"
}
}
}
}
},
"post": {
"operationId": "add",
"summary": "adds todo",
"description": "add a todo item to the list",
"produces": [
"application/json"
],
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Todo"
}
}
],
"responses": {
"200": {
"description": "returns true if successful",
"schema": {
"type": "boolean"
}
}
}
},
"delete": {
"operationId": "remove",
"summary": "delete a todo item",
"description": "Deletes an item by id",
"parameters": [
{
"name": "id",
"in": "query",
"description": "id of Todo item to delete",
"type": "string"
}
],
"responses": {
"202": {
"description": "returns success",
"schema": {
"type": "string"
}
}
}
}
}
}
}
The Swagger JSON meta-data can be imported into the swagger editor.
swagger: '2.0'
info:
title: Todo Title
description: A great service to show building a todo list
contact:
name: Buffalo Wild Bill Lunati
url: 'http://www.bwbl.lunati/master/of/rodeo'
email: lunati-not-real-email@gmail.com
version: '47.0'
license:
name: Commercial
url: 'http://www.canttouchthis.com'
host: 'localhost:8888'
basePath: /v1
schemes:
- http
- https
- wss
- ws
consumes:
- application/json
definitions:
Todo:
properties:
id:
type: string
name:
type: string
description:
type: string
createTime:
type: integer
format: int64
produces:
- application/json
paths:
/todo-service/todo:
get:
operationId: list
summary: list items
description: List all items in the system
produces:
- application/json
responses:
'200':
description: return list of all items in system
schema:
type: array
items:
$ref: '#/definitions/Todo'
post:
operationId: add
summary: adds todo
description: add a todo item to the list
produces:
- application/json
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/Todo'
responses:
'200':
description: returns true if successful
schema:
type: boolean
delete:
operationId: remove
summary: delete a todo item
description: Deletes an item by id
parameters:
- name: id
in: query
description: id of Todo item to delete
type: string
responses:
'202':
description: returns success
schema:
type: string
You can see the
description
s, summary
and returnDescription
from the@RequestMapping
and @RequestParam
annotations are exposed in the Swagger generation for documentation.
You can also generate working client libs from Swagger using the JSON that QBit provides. We did this and it works for both the Callback version of the TODO list as well as the non-callback version. The source code for the Swagger REST TODO Client which was generated is in github.
Using Swagger generated client to talk to TODO service
DefaultApi defaultApi = new DefaultApi();
Todo todo = new Todo();
todo.setDescription("Show demo to group");
todo.setName("Show demo");
todo.setCreateTime(123L);
defaultApi.add(todo);
List<Todo> list = defaultApi.list();
list.forEach(new Consumer<Todo>() {
@Override
public void accept(Todo todo) {
System.out.println(todo);
}
});
Curl, Stats and health
You can access the service via curl commands.
Getting a list of TODO items using REST curl call
$ curl http://localhost:8888/v1/todo-service/todo
[{"id":"name0::1441040038414","name":"name0","description":"desc0",
"createTime":1441040038414}, ...
You can inquire about the health of its nodes using the admin port.
Using admin port to check Todo services health
$ curl http://localhost:7777/__admin/load-nodes/
[{"name":"TodoService","ttlInMS":10000,"lastCheckIn":1441040291968,"status":"PASS"}]
Remember that this will list all service actors (
ServiceQueue
s) andServiceServerEndpoint
s (REST and WebSocket services).
If you are looking for a yes/no answer to health for Nagios or Consul or some load balancer, then you can use.
Health status yes or no?
$ curl http://localhost:8888/__health
"ok"
Of the admin port version of this with:
Health status yes or no? on admin port
$ curl http://localhost:7777/__admin/ok
true
To get the stats (after I ran some load testing):
Getting runtime stats of the TODO microservice
$ curl http://localhost:8888/__stats/instance
Output of getting runtime stats of the TODO microservice
{
"MetricsKV": {
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free": 219638816,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.std": 0.5,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.count": 8,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.median": 21867208,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.min": 60,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.std": 1023408.06,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.median": 300,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.peak.count": 32,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.mean": 21437416,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.std": 18688268,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.max": 3817865216,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.mean": 61136300,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count": 31,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.count": 9,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.std": 1.1659224,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.min": 173839288,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level": 4,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.std": 2.5819888,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.min": 19162464,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes": 9,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.mean": 300,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.total": 257425408,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count": 35,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.median": 32,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.count": 2,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.max": 35,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.mean": 5,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.median": 4,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.median": 191758000,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.std": 154.91933,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.max": 83586120,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.count": 9,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.max": 32,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.mean": 4.125,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds": 540,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used": 37786592,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.median": 65667408,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used": 22026992,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.count": 10,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.median": 5,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.max": 22026992,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.mean": 31.5,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.count.min": 31,
"TodoService": 1,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.max": 238262944,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.min": 1,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.mean": 196289104,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.count": 10,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.median": 35,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.min": 34,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.used.min": 18501664,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.count": 2,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.max": 6,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.minutes.max": 9,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.daemon.count": 11,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.free.count": 10,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.std": 0.5,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.non.heap.max": -1,
"todo.title.millenniumfalcon.mammatustech.com.jvm.os.load.level.min": 2,
"todo.title.millenniumfalcon.mammatustech.com.jvm.up.time.seconds.max": 540,
"todo.title.millenniumfalcon.mammatustech.com.jvm.mem.heap.used.std": 18688268,
"todo.title.millenniumfalcon.mammatustech.com.jvm.thread.started.count.mean": 34.5
},
"MetricsMS": {
"TodoService.callTimeSample": [
167643,
9502
],
"todo.title.millenniumfalcon.mammatustech.com.jvm.gc.collector.ps.scavengecollection.time": [
11,
11
]
},
"MetricsC": {
"todo.title.millenniumfalcon.mammatustech.com.jvm.gc.collector.ps.scavengecollection.count": 2,
"TodoService.startBatchCount": 175,
"TodoService.receiveCount": 260
},
"version": 1
}
Remember that you can disable this local collection of stats (read the batteries included tutorial for more information).
Conclusion
We covered how to expose a service by mapping the service and its methods to URIs.
Next up we will show how to create a resourceful REST scheme.
Read more:
- QBit Microservice Hello World tutorial
- QBit Microservice Hello World Part 2
- QBit Microservice Hello World Health Checks
- QBit Microservice Hello World Stats, Metrics, and Monitoring
- QBit Microservice Reactive programming tutorial
QBit is the Java microservice lib. QBit is a reactive programming lib for building microservices and focuses on JSON, HTTP, WebSocket, and REST. QBit uses reactive programming to build elastic REST, and WebSockets based cloud friendly, web services. SOA evolved for mobile and cloud. ServiceDiscovery, Health, reactive StatService, events, Java idiomatic reactive programming for Microservices.