LIGHT

  • News
  • Docs
  • Community
  • Reddit
  • GitHub
  • About Light
    • Overview
    • Testimonials
    • What is Light
    • Features
    • Principles
    • Benefits
    • Roadmap
    • Community
    • Articles
    • Videos
    • License
    • Why Light Platform
  • Getting Started
    • Get Started Overview
    • Environment
    • Light Codegen Tool
    • Light Rest 4j
    • Light Tram 4j
    • Light Graphql 4j
    • Light Hybrid 4j
    • Light Eventuate 4j
    • Light Oauth2
    • Light Portal Service
    • Light Proxy Server
    • Light Router Server
    • Light Config Server
    • Light Saga 4j
    • Light Session 4j
    • Webserver
    • Websocket
    • Spring Boot Servlet
  • Architecture
    • Architecture Overview
    • API Category
    • API Gateway
    • Architecture Patterns
    • CQRS
    • Eco System
    • Event Sourcing
    • Fail Fast vs Fail Slow
    • Integration Patterns
    • JavaEE declining
    • Key Distribution
    • Microservices Architecture
    • Microservices Monitoring
    • Microservices Security
    • Microservices Traceability
    • Modular Monolith
    • Platform Ecosystem
    • Plugin Architecture
    • Scalability and Performance
    • Serverless
    • Service Collaboration
    • Service Mesh
    • SOA
    • Spring is bloated
    • Stages of API Adoption
    • Transaction Management
    • Microservices Cross-cutting Concerns Options
    • Service Mesh Plus
    • Service Discovery
  • Design
    • Design Overview
    • Design First vs Code First
    • Desgin Pattern
    • Service Evolution
    • Consumer Contract and Consumer Driven Contract
    • Handling Partial Failure
    • Idempotency
    • Server Life Cycle
    • Environment Segregation
    • Database
    • Decomposition Patterns
    • Http2
    • Test Driven
    • Multi-Tenancy
    • Why check token expiration
    • WebServices to Microservices
  • Cross-Cutting Concerns
    • Concerns Overview
  • API Styles
    • Light-4j for absolute performance
    • Style Overview
    • Distributed session on IMDG
    • Hybrid Serverless Modularized Monolithic
    • Kafka - Event Sourcing and CQRS
    • REST - Representational state transfer
    • Web Server with Light
    • Websocket with Light
    • Spring Boot Integration
    • Single Page Application
    • GraphQL - A query language for your API
    • Light IBM MQ
    • Light AWS Lambda
    • Chaos Monkey
  • Infrastructure Services
    • Service Overview
    • Light Proxy
    • Light Mesh
    • Light Router
    • Light Portal
    • Messaging Infrastructure
    • Centralized Logging
    • COVID-19
    • Light OAuth2
    • Metrics and Alerts
    • Config Server
    • Tokenization
    • Light Controller
  • Tool Chain
    • Tool Chain Overview
  • Utility Library
  • Service Consumer
    • Service Consumer
  • Development
    • Development Overview
  • Deployment
    • Deployment Overview
    • Frontend Backend
    • Linux Service
    • Windows Service
    • Install Eventuate on Windows
    • Secure API
    • Client vs light-router
    • Memory Limit
    • Deploy to Kubernetes
  • Benchmark
    • Benchmark Overview
  • Tutorial
    • Tutorial Overview
  • Troubleshooting
    • Troubleshoot
  • FAQ
    • FAQ Overview
  • Milestones
  • Contribute
    • Contribute to Light
    • Development
    • Documentation
    • Example
    • Tutorial

API servicemesher service call by async client module

Light-4j servicemesher client module call example

This example project used to show how to use light-4j client module and consumer component to call multi-service APIs in servicemesher environment.

It uses java async concurrent feature and processes the service call in parallel.

Introduction:

For microservice applications, the application will be split into a set of smaller, interconnected services instead of building a single monolithic application. Each microservice is a small application that has its own hexagonal architecture consisting of business logic along with various adapters. So there will be lots of service to service calls to get the required data for the business method. Microservices architecture adds complexity to the project just by the fact that a microservices application is a distributed system.

So massive service calls need to guarantee excellent performance to complete the business logic in a short time. If those service calls implement synchronously, that means the service calls will be invoked one by one, and any of them takes longer time will hang up the whole application.

Light-4j client module and light-consumer-4j component use Jave CompletableFuture asynchronous programming to implement non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion or failure;

We will introduce the detail steps for the implementation of this project as below.

diagram

Modules

In this example, we build two types of project setting for light-4j based microservice API.

First type: one project has multiple service APIs, for each service API, it has two maven modules: one module for API specification, one for API source code.

You can refer to sample server-side services

– server-side services

Inside the services folder, we build four simple server-side mock service APIs:


| ------------------ | -------------------------------------------|------------| ----------------------|
| Service API        | Service Id                                 |  Port      |  docker image name    |
| ------------------ | -------------------------------------------|------------| ----------------------|
| petstore   API     | com.networknt.petstore-service-api-3.0.1   | 8443       | petstore-service      |
| ------------------ | -------------------------------------------|------------| ----------------------|
| bookstore API      | com.networknt.bookstore-service-api-3.0.1  | 8444       | bookstore-service     |
| ------------------ | -------------------------------------------|------------| ----------------------|
| foodstore API      | com.networknt.foodstore-service-api-3.0.1  | 8447       | foodstore-service     |
| ------------------ | -------------------------------------------|------------| ----------------------|
| computerstore API  |com.networknt.computerstore-service-api-3.0.1| 8445       | computerstore-service |
| ------------------ | -------------------------------------------|------------| ----------------------|


Second type: one project for a single service API.

User can use command line to run light-codegen

Or use [light-api-template] (https://github.com/mservicetech/light-api-template) to start building the service API project

– client-side service

In the client folder, we build a client service API which use light-4j client module’s consumer component to call the service APIs parallel.

Structure diagram

diagram

Code implementation

service config

Since the client side service API will call multiple services, to avoid hardcode the service id in the code, we define it in the config file. By using the light-4j token injection feature, we set the detail value at values.yml:

values.yml

#--------------------------------------------------------------------------------
# service-define.yml
#--------------------------------------------------------------------------------
service-define.serviceMap:
  petstore:
    {  serviceId: "com.networknt.petstore-service-api-3.0.1", protocol: "https",   environment: null , requestKey: null}
  bookstore:
    {  serviceId: "com.networknt.bookstore-service-api-3.0.1", protocol: "https",   environment: null , requestKey: null}
  foodstore:
    {  serviceId: "com.networknt.foodstore-service-api-3.0.1", protocol: "https",   environment: null , requestKey: null}
  computerstore:
    {  serviceId: "com.networknt.computerstore-service-api-3.0.1", protocol: "https",   environment: null , requestKey: null}


The service config will be converted to ServiceMapper object by Config mapping:

    ServiceMapper serviceMapper = (ServiceMapper) Config.getInstance().getJsonObjectConfig(ServiceMapper.CONFIG_NAME, ServiceMapper.class);

ServiceMapper.java

package com.networknt.market.service;

import java.util.HashMap;
import java.util.Map;

public class ServiceMapper {
    public static final String CONFIG_NAME = "service-define";

    private Map<String, ServiceObject> serviceMap = new HashMap<>();


    public ServiceMapper() {
    }

    public Map<String, ServiceObject> getServiceMap() {
        return serviceMap;
    }

    public void setServiceMap(Map<String, ServiceObject> serviceMap) {
        this.serviceMap = serviceMap;
    }
}

Then in the MarketServiceImpl.java


public class MarketServiceImpl implements MarketService{
    private static final Logger logger = LoggerFactory.getLogger(MarketServiceImpl.class);
    private static final String PET_STORE = "petstore";
    private static final String FOOD_STORE = "foodstore";
    private static final String BOOK_STORE = "bookstore";
    private static final String COMPUTER_STORE = "computerstore";

    ServiceMapper serviceMapper = (ServiceMapper) Config.getInstance().getJsonObjectConfig(ServiceMapper.CONFIG_NAME, ServiceMapper.class);
    @Override
    public Market getMarket() throws Exception {
        Market market = new Market();
        Http2ServiceRequest listAllPets = getHttp2ServiceRequest(PET_STORE, "/v1/pets", "GET");
        Http2ServiceRequest listAllFood = getHttp2ServiceRequest(FOOD_STORE, "/v1/food", "GET");
        Http2ServiceRequest listAllComputers = getHttp2ServiceRequest(COMPUTER_STORE, "/v1/computers", "GET");
        Http2ServiceRequest listAllBooks = getHttp2ServiceRequest(BOOK_STORE, "/v1/books", "GET");

        CompletableFuture<List> petsFutureResponse = listAllPets.callForTypedObject(List.class);
        CompletableFuture<List> foodFutureResponse = listAllFood.callForTypedObject(List.class);
        CompletableFuture<List> computersFutureResponse = listAllComputers.callForTypedObject(List.class);
        CompletableFuture<List> booksFutureResponse = listAllBooks.callForTypedObject(List.class);

        Collection<CompletableFuture<?>> completableFutures = new HashSet<>();
        completableFutures.add(petsFutureResponse);
        completableFutures.add(foodFutureResponse);
        completableFutures.add(computersFutureResponse);
        completableFutures.add(booksFutureResponse);
        completableFutures.addAll(mapToMarket(market, petsFutureResponse, foodFutureResponse, computersFutureResponse, booksFutureResponse));
        try {
            CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(3, TimeUnit.SECONDS);
        } catch(Exception e) {
            logger.error("Some information was unavailable when assembling the market value.", e);
            System.out.println("error:" + e);
            throw new ApiException(new Status("ERR10010"));
        }
        return market;
    }


    public  Collection<CompletableFuture<?>> mapToMarket(Market market,CompletableFuture<List> petsFutureResponse, CompletableFuture<List> foodFutureResponse,
                                                              CompletableFuture<List> computersFutureResponse,CompletableFuture<List> booksFutureResponse) {
        Collection<CompletableFuture<?>> completableFutures = new HashSet<>();

        CompletableFuture<Void> setPets = petsFutureResponse.thenAccept(pets -> market.setPets(pets));
        CompletableFuture<Void> setFood = foodFutureResponse.thenAccept(food -> market.setFoodbox(food));
        CompletableFuture<Void> setBooks = booksFutureResponse.thenAccept(books -> market.setBooks(books));
        CompletableFuture<Void> setComputers = computersFutureResponse.thenAccept(computers -> market.setComputers(computers));

        completableFutures.add(setPets);
        completableFutures.add(setFood);
        completableFutures.add(setBooks);
        completableFutures.add(setComputers);
        return completableFutures;
    }

    private Http2ServiceRequest getHttp2ServiceRequest(String serviceName, String path, String method) throws Exception {
        ServiceObject serviceObject = serviceMapper.getServiceMap().get(serviceName);
        ServiceDef serviceDef = new ServiceDef(serviceObject.getProtocol(), serviceObject.getServiceId(), serviceObject.getEnvironment(), serviceObject.getRequestKey());

        if (serviceDef==null) throw new Exception("There is no service config in the service-define.yml file.");
        return  new Http2ServiceRequest(serviceDef, path, HttpVerb.valueOf(method));

    }
}

The getHttp2ServiceRequest method will get Http2ServiceRequest light-consumer-4j component based on service define. The Http2ServiceRequest class provides an abstraction for making parallel HTTP calls.

And we will send multi-service APIs call requests in parallel asynchronously which will return CompletableFuture. It can make sure the system won’t wait for each service call one by one.

Next step application adds the return futures and result process futures to a collection of CompletableFuture. All independent futures run in parallel and system set to return it less than 3 seconds (it is configurable based on the config value: in the client.yml file ).

CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(3, TimeUnit.SECONDS);

You can specify the JSON response type based on what class you expect it to deserialize to by calling

CompletableFuture<Pet> petFutureResponse = request.callForTypedObject(Pet.class);

CompletableFuture<List<Pet>> petListFutureResponse = request.callForTypedList(Pet.class);

If you do not have a class definition for the response, you can use a generic Map:

CompletableFuture<Map> futureResponse = request.callForTypedObject(Map.class);

The response received from making a request can be held in a CompletableFuture, which allows for further asynchronous chaining of operations. They will be asynchronous with respect to the original thread because a different thread was already assigned to execute the HTTP request and wait for its response, so either that thread or an additional thread will be responsible for completing each chained operation.

Additional references to the Java 8 CompletableFuture API:

  • Guide to CompletableFuture
  • Java 8: Definitive guide to CompletableFuture
  • When to use non-async methods of CompletableFuture?

Build and verify

Build and start server side services:

Step 1 (build server-side services and start local consul):

cd ~/networknt
git clone [email protected]:networknt/light-example-4j.git
cd ~/networknt/light-example-4j/servicemesher/services

mvn clean install

cd ~/networknt/light-docker
docker-compose -f docker-compose-consul.yml up -d

Step 2 (start server side services by open new terminal):

cd ~/networknt/light-example-4j/servicemesher/services
docker-compose  up

Build and start client side service:

In another terminal

cd ~/networknt
git clone [email protected]:networknt/light-example-4j.git
cd ~/networknt/light-example-4j/servicemesher/client

mvn clean install

docker-compose  up

verify service:

From browser or Postman (GET):

https://localhost:8448/v1/market

The client-side service will call all four server-side services parallel and get the result to build a market object. The sample result will look like below:

     {
         "name": "name": "light-4j sample market",
         "pets": [
             {
                 "id": 1,
                 "name": "catten",
                 "tag": "cat"
             },
             {
                 "id": 2,
                 "name": "doggy",
                 "tag": "dog"
             }
         ],
         "books": [
             {
                 "id": 1,
                 "name": "Educated",
                 "author": "James Bond"
             },
             {
                 "id": 2,
                 "name": "The Amazon Job",
                 "author": "Randy Grieser"
             }
         ],
         "foodbox": [
             {
                 "id": 1,
                 "name": "Noodle",
                 "tag": "food"
             },
             {
                 "id": 2,
                 "name": "Rice",
                 "tag": "food"
             }
         ],
         "computers": [
             {
                 "id": 1,
                 "brand": "HP",
                 "tag": "1112222-22"
             },
             {
                 "id": 2,
                 "brand": "IBM",
                 "tag": "111122255-55"
             }
         ]
     }

Prometheus metrics

Prometheus is an open-source systems monitoring and alerting toolkit originally built at SoundCloud.

The ServiceMesher sample implemented with Prometheus metrics middleware handler.

Enable Prometheus metrics middleware handler by prometheus.yml config file:

  • sample config

    ---
    # Prometheus Metrics handler configuration
    
    enabled: true
    
    enableHotspot: true
    
    

In the handler.yml config file for each services, we need add Prometheus middleware handler

   - com.networknt.metrics.prometheus.PrometheusHandler@prometheus
   - com.networknt.metrics.prometheus.PrometheusGetHandler@getprometheus

And add endpoint for Prometheus to pull the metrics data:

- path: '/v1/prometheus'
  method: 'get'
  exec:
    - getprometheus

To Verify, please following the below steps:

cd ~/networknt
git clone [email protected]:networknt/light-example-4j.git
cd ~/networknt/light-docker

 docker-compose -f docker-compose-prometheus.yml up

Then you can open Prometheus admin console by:

http://localhost:9090/

Light-4j framework middleware handler provide application defined monitor which include:

  public static final String REQUEST_TOTAL = "requests_total";
  public static final String SUCCESS_TOTAL = "success_total";
  public static final String AUTO_ERROR_TOTAL = "auth_error_total";
  public static final String REQUEST_ERROR_TOTAL = "request_error_total";
  public static final String SERVER_ERROR_TOTAL = "server_error_total";
  public static final String RESPONSE_TIME_SECOND = "response_time_seconds";

And for the mBeans based JVM hotspot monitor, it includes:

CPU and JVM process:

process_cpu_seconds_total
process_open_fds

JVM memory usage:

jvm_memory_pool_allocated_bytes_total
jvm_memory_pool_bytes_init
jvm_memory_pool_bytes_max
jvm_memory_pool_bytes_used

metrics about JVM thread areas

jvm_threads_peak
jvm_threads_daemon
jvm_threads_started_total
jvm_threads_deadlocked
jvm_threads_state

JVM garbage collectors:

jvm_gc_collection_seconds

Buffer:

jvm_buffer_pool_used_bytes
jvm_buffer_pool_capacity_bytes
jvm_buffer_pool_used_buffers
  • News
  • Docs
  • Community
  • Reddit
  • GitHub
  • About Light
  • Getting Started
  • Architecture
  • Design
  • Cross-Cutting Concerns
  • API Styles
  • Infrastructure Services
  • Tool Chain
  • Utility Library
  • Service Consumer
  • Development
  • Deployment
  • Benchmark
  • Tutorial
  • Troubleshooting
  • FAQ
  • Milestones
  • Contribute