Git Product home page Git Product logo

vxms's Introduction

Build Status

vxms

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.

maven dependencies

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>

vxms-rest example

@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());
    }
}

vxms-eventbus example

@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());
    }
}

vxms-k8s-discovery example

@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);
    }
}

vxms-core example

   @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());
       }
   } 

vxms's People

Contributors

amoahcp avatar dependabot[bot] avatar jacpfx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vxms's Issues

Orgainize packages around concerns not technical aspects

I saw packages like:

  • org.jacpfx.vertx.event.interfaces.blocking
  • org.jacpfx.vertx.event.interfaces.basic

This is IMO not a good idea. Name and organize artifacts along their concerns, not their technical implementation details (they may change). In the case above I think the event package itself is enough and allo subpackages can be ommitted.

Use short Apache headers in source files

It is sufficient to use the short ALv2 license header and add the full Apache license to LICENCE.txt on the repo top level (see how Apache projects do it):

`
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy current the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
`

Put everything in core into one common package.

This makes the package containing at least 2 classes (which is still very minimal). Rethink if some other functionality also can be added to core, so it has about 10-15 artifacts in it.

In vxms spring demo dependency should be changed

Hello,

I changed spring-ext dependency version and group id and it build success.

<dependency> <groupId>org.jacpfx.vertx.spring</groupId> <artifactId>vertx-spring-ext</artifactId> <version>2.2</version> </dependency>

Also changed repositories:


sonatype-nexus-staging

https://oss.sonatype.org/service/local/staging/deploy/maven2


and

sonatype-nexus-snapshots

https://oss.sonatype.org/content/repositories/snapshots


Old dependency failed during a build.

Improvement: abstracting interface calls over the eventbus in a comfortable service abstraction

This requires the following artifacts:

  • A custom service verticle factory, which registeres corresponding handlers on the events created by the proxies handed out and manages the services available in the system.
  • A @ServiceInterface annotation, which defines the exposed interfaces of a verticle.
  • An access point which allows to access proxies, which delegate calls to the eventbus, so they can be handled appropriately.

I can help providing this logic (done it already once).

Addition: Implementing this and also modelling ServiceCalls and CallState, would also help to find a better place (or rendering obsolote), code inorg.jacpfx.vertx.event.util and EventInitializer.java

vxms core module is not named core

In the docs the main module is mentioned as vxms-core, whereas in code the repository (and maybe also the aritfact) is named vxms-verticle. This is confusing, I propose to use core as name for the core repos.

Proposal: Make functionality in VxmsEndpoint also usable without inheritance.

To improve interoperability with other Vertx extensions/existing code I would appreciate to have the functionality implemented by VxmsEndpoint also usable without inheritance, e.g. by providing a instantiable form of VxmsEndpoint that can be added as ordinary Java field in an owning verticle, or that can be deployed as a verticle on its own by its owning verticle.

Add parameter objects to interfaces

                String targetId,
                 Object message,
                 ThrowableFutureBiConsumer<AsyncResult<Message<Object>>, T> function,
                 DeliveryOptions requestDeliveryOptions,
                 String methodId,
                 Vertx vertx,
                 Throwable failure,
                 Consumer<Throwable> errorMethodHandler,
                 Message<Object> requestMessage,
                 ThrowableFutureConsumer<T> consumer,
                 Encoder encoder,
                 Consumer<Throwable> errorHandler,
                 ThrowableErrorConsumer<Throwable, T> onFailureRespond,
                 DeliveryOptions responseDeliveryOptions,
                 int retryCount, long timeout, long circuitBreakerTimeout

Define thread-safe parameter objects for parameter lists, including a builder, basically longer than 5 params. This makes code much more easier to read and write. In the example above imagine the lamba parameter list required...

Addition: The same is valid for other packages like event.response etc. So I think we should identify and model common concerns such as a ServiceCall, CallState etc.

Run formatter over the whole codebase to ensure unified code formatting.

I saw code like this:

if (contextRootSet)
     topRouter.mountSubRouter(URIUtil.getCleanContextRoot(Optional.ofNullable(contexRoot).orElse("")), subRouter);

if(port!=0){
     initHTTPEndpoint(startFuture, port, host, server, topRouter);
}

Obviously (looking at the brackets) code formatting is not unified.

Check for TODOs

I encountered some todos, e.g.:

private void info(Message m) {
        // TODO create info message about service
}

So TODOs should be checked and implemented.

Add a readme to each repository top level.

Add a readme, which describes the repository's content, to each repository, so Github will show it one opens the repo. This greatly improves looking around and understanding what is around.

DiscoveryClient method name is confusing

The method name DiscoveryClientcreateClient IMO is confusing, since it may also return null:

/**
     * Creates a discovery client instance
     *
     * @param verticle the current verticle
     * @return the discover client implementation
     */
    static DiscoveryClient createClient(AbstractVerticle verticle) {
        ServiceLoader<DiscoveryClientSpi> loader = ServiceLoader.load(DiscoveryClientSpi.class);
        if (!loader.iterator().hasNext()) return null;
        return (DiscoveryClient) loader.iterator().next().getClient(verticle);
    }

SPI loading doesn't allow controlled overriding of instances to be used

Source: ServiceUtil.java:
The loader relies only on default ServiceLoader functionality. Depending on the classloading in place (which must not behave similar on each VM run), other instances of SPI may be returned. Furthermore there is no defined mechanism how one could override an SPI explicitly, if multiple implementation are registered.
A resolution how this can be solved can be found here:
ServiceContextManager.java,
ServiceContext.java

Node has several implementation flaws

The Node class (Node.java,ServiceDiscovery) has several implementation flaws IMO:

  • modifiedIndex, createdIndex are etcd specific implementation details. If other registries, such as consul are used these parameters maybe useless. I would recommend to think on an alternate method to store additional params as needed by the impl, e.g. a generic Map<String,String> userData property.
  • expiration is a String, I would expect a long UTC timestamp or similar.
  • the contract with instances using Node is very loose. No precondition checks, such as non nullable keys etc are performed.
  • the class may not be thread safe.
  • the list passed is not copied, making the class exposed to unexpected side effects by external changes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.