Vxms is a modular micro-service framework, based 100% on Vert.x 3. While Vert.x is a totally unopinionated framework/toolkit, Vxms helps developers to create REST/event based (micro) services with a clear, uniform and easy to use fluent-API.
Since Vxms is extending Vert.x you can still use all capabilities of Vert.x and mix it with Vxms wherever you like/need.
The intention for Vxms was to create a framework on top of the powerful Vert.x framework, allowing developers quickly to create services with the focus on writeability, readability and resilience. Basically, most of todays service endpoints need to handle requests, process data and be aware of the error handling. This is the focus of Vxms.
Vert.x is very powerful, but many developers still struggling with the reactive style and the callback handling in Vert.x. This is the keypoint of Vxms, which provides an easy to use API for "everyday" endpoint development.
Currently vxms consists of 1 base module and 3 extension modules, helping the developer to write Jax-RS like REST services, EventBus endpoints and doing service discovery in Kubernetes. The core module is using a Java SPIs to include the REST and EventBus modules, so you can adopt the API easily for your needs. Vxms only uses Vert.x-core and Vert.x-web extension as dependencies and any other Vert.x extension will work in vxms out of the box.
vxms-core link
<dependency>
<groupId>org.jacpfx</groupId>
<artifactId>vxms-core</artifactId>
<version>1.1</version>
</dependency>
vxms-rest link
<dependency>
<groupId>org.jacpfx</groupId>
<artifactId>vxms-rest</artifactId>
<version>1.1</version>
</dependency>
vxms-event bus link
<dependency>
<groupId>org.jacpfx</groupId>
<artifactId>vxms-event</artifactId>
<version>1.1</version>
</dependency>
vxms-k8s-discovery link
<dependency>
<groupId>org.jacpfx</groupId>
<artifactId>vxms-k8sdiscovery</artifactId>
<version>1.1</version>
</dependency>
@ServiceEndpoint(port=8090)
public class RESTExample extends VxmsEndpoint {
@Path("/hello/:name")
@GET
public void simpleNonBlocking(RestHandler handler) {
String name = handler.request().param("name");
handler.
response().
stringResponse((response)->
response.complete("hello World "+name)). // complete non-blocking response
timeout(2000). // timeout for stringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
httpErrorCode(HttpResponseStatus.BAD_REQUEST). // http error code in case of onFailureRespond will be executed
retry(3). // amount of retries before onFailureRespond will be executed
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
@Path("/helloChain/:name")
@GET
public void simpleNonBlockingChain(RestHandler handler) {
String name = handler.request().param("name");
handler.
response().
<Integer>supply((future) -> future.complete(getAge())). // start the chain by supplying a value (an Integer)
<Customer>andThen((value, future) -> future.complete(new Customer(value + 1 + "", name))). // take the value (the Integer) from supply and return an other type (the Customer)
mapToStringResponse((customer, response)->
response.complete("hello World "+customer.getName())). // get the return-value from the last chain step and map it to a string-response and complete non-blocking response
timeout(2000). // timeout for stringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
httpErrorCode(HttpResponseStatus.BAD_REQUEST). // http error code in case of onFailureRespond will be executed
retry(3). // amount of retries before onFailureRespond will be executed
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
@Path("/helloBlocking/:name")
@GET
public void simpleBlocking(RestHandler handler) {
String name = handler.request().param("name");
handler.
response().
blocking().
stringResponse(()->{
String val = blockingCall();
return val+ "hello World "+name;
}). // complete blocking response
timeout(15000). // timeout for stringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
httpErrorCode(HttpResponseStatus.BAD_REQUEST). // http error code in case of onFailureRespond will be executed
retry(3). // amount of retries before onFailureRespond will be executed
delay(1000). // delay between retries
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
@Path("/helloEventbus/:name")
@GET
public void simpleEventbusCall(RestHandler handler) {
String name = handler.request().param("name");
handler.
eventBusRequest().
send("/hello", name). // send message to eventbus onSuccess
mapToStringResponse((message, response)->
response.complete(message.result().body()). // on message response, map message result value to the rest response ). // complete non-blocking response
timeout(5000). // timeout for mapToStringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
httpErrorCode(HttpResponseStatus.BAD_REQUEST). // http error code in case of onFailureRespond will be executed
retry(3). // amount of retries before onFailureRespond will be executed
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
private String blockingCall(){
// block
return "xyz";
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(RESTExample.class.getName());
}
}
@ServiceEndpoint
public class EventbusExample extends VxmsEndpoint {
@Consume("/hello")
public void simpleNonBlocking(EventbusHandler handler) {
String name = handler.request().body();
handler.
response().
stringResponse((response)->
response.complete("hello World "+name)). // complete non-blocking response
timeout(2000). // timeout for stringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
retry(3). // amount of retries before onFailureRespond will be executed
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
@Consume("/helloChain")
public void simpleNonBlocking(EventbusHandler handler) {
String name = handler.request().body();
handler.
response().
<Integer>supply((future) -> getAge()). // start the chain by supplying a value (an Integer)
<Customer>andThen((value, future) -> future.complete(new Customer(value + 1 + "", name))). // take the value (the Integer) from supply and return an other type (the Customer)
mapToStringResponse((cust, response)->
response.complete("hello World "+cust.getName())). // get the return-value from the last chain step and map it to a string-response and complete non-blocking response
timeout(2000). // timeout for stringResponse handling. If timeout is reached, error handling will be executed
onError(error -> LOG(error.getMessage())). // intermediate error handling, will be executed on each error
onFailureRespond((error, future) -> future.complete("error:"+error.getMessage())). // define final error response when (if no retry is defined or all retries are failing)
retry(3). // amount of retries before onFailureRespond will be executed
closeCircuitBreaker(2000). // time after circuit breaker will be closed again. While opened, onFailureRespond will be executed on request
execute(); // execute non blocking
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(EventbusExample.class.getName());
}
}
@ServiceEndpoint(port=8090)
@K8SDiscovery
public class RESTExample extends VxmsEndpoint {
@ServiceName()
@WithLabels({
@WithLabel(name = "name", value = "${read_name}"),
@WithLabel(name = "version", value = "${read_version}")
})
private String read;
@ServiceName()
@WithLabels({
@WithLabel(name = "name", value = "${write_name}"),
@WithLabel(name = "version", value = "${write_version}")
})
private String write;
...
public static void main(String[] args) {
// this is only for local discovery to bypass Kubernetes in local environments
DeploymentOptions options =
new DeploymentOptions()
.setInstances(1)
.setConfig(
new JsonObject()
.put("kube.offline", true)
.put("local", true)
.put("read_name", "vxms-k8s-read")
.put("read_version", "1.2-SNAPSHOT")
.put("write_name", "vxms-k8s-write")
.put("write_version", "1.2-SNAPSHOT")
.put("name.vxms-k8s-read.version.1.2-SNAPSHOT", "localhost:7070")
.put("name.vxms-k8s-write.version.1.2-SNAPSHOT", "localhost:9090"));
Vertx.vertx().deployVerticle(RESTExample.class.getName(), options);
}
}
@ServiceEndpoint
public class SimpleService extends VxmsEndpoint {
public void postConstruct(Router router, final Future<Void> startFuture){
router.get("/hello").handler(helloGet -> helloGet.response().end("simple response"));
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(SimpleREST.class.getName());
}
}
or using plain Verticles with static initializer
@ServiceEndpoint
public class SimpleService extends AbstractVerticle {
@Override
public void start(io.vertx.core.Future<Void> startFuture) throws Exception {
VxmsEndpoint.start(startFuture, this);
}
public void postConstruct(Router router, final Future<Void> startFuture){
router.get("/hello").handler(helloGet -> helloGet.response().end("simple response"));
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(SimpleREST.class.getName());
}
}