Spring Boot Sidecar

Upasana | July 31, 2020 | 6 min read | 1,937 views


In this tutorial we will integrate a non-JVM app (python Flask app in this case) into Spring Cloud seamlessly using polyglot sidecar support. We will use netflix eureka registry to manage service registrations and netflix zuul for API Gateway.

Roadmap

  1. Cover scalability patterns using Sidecar approach for non JVM apps

  2. Custom health endpoint where its not possible to modify the non JVM app (elasticsearch, for example)

  3. Provide Code link for the app shared on Github

  4. Add steps to create the Spring Boot Project

  5. Add screenshots for Eureka Service Registry

Any decent sizes non-trivial distributed application will have polyglot programming model to make best use of available technology stacks (libraries and frameworks). For example, if there is a IO intensive work (chat for example), then few microservices may be built using nodejs, or if there is something related to machine learning, then python may be the good choice for developing that recommendation service.

Important Usecase

  1. A machine learning program written in python/flask/django needs to communicate to & forth with rest of the JVM apps in spring cloud ecosystem.

  2. Few IO intensive services are developed in nodejs, but rest of the code is using Spring Cloud, effective communication b/w JVM & nodejs is required.

  3. We want to access elasticsearch using API gateway rather then using separate host/port.

  4. Access database using sidecar pattern.

The problem statement

There are multiple problems related to inter service communication when polyglot comes into picture. few of them are:

  1. How does non-JVM apps discover JVM microservices in Spring Cloud environment and vice-versa. In any cloud native application, we would never want to hard code host and port of apps for discovery purpose.

  2. How does non-JVM app communicates (i.e. calls REST endpoints) with JVM apps and vice versa. How do we utilize the client side load balancing features for non-JVM apps.

  3. How do we utilize the config server for non-JVM apps?

Sidecar for the Rescue

Sidecar is built to solve the exact problem at hand. It allows any non-JVM apps to take advantage of Eureka, Ribbon and ConfigServer.

sidecar implementation

Fig. Sidecar for non-jvm apps integration

It includes an HTTP API to get all of the instances (by host and port) for a given service. This API is accessible to the non-JVM application (if the sidecar is on port 5678) at

http://localhost:5678/hosts/{serviceId}.

For example, if we want to list the host and port details for sidecar-pdf itself, we can call the below API:

GET request for service details
http://localhost:5678/hosts/sidecar-pdf
Host details for sidecar-pdf service
[
    {
        "host": "localhost",
        "port": 8058,
        "instanceId": "10.65.18.178:sidecar-pdf:5678",
        "secure": false,
        "uri": "http://localhost:8058",
        "serviceId": "SIDECAR-PDF"
     }
]

Creating a Spring Boot Sidecar project

You can create a new Spring Boot 2.x starter project from https://start.spring.io with the Spring Cloud dependencies (webflux, actuator, and eureka).

spring initializer

Once project is created, import it into your favorite IDE (IntelliJ IDEA Ultimate in this tutorial) and manually add the sidecar dependencies.

build.gradle
plugins {
	id 'org.springframework.boot' version '2.1.5.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.cloud:spring-cloud-netflix-sidecar'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

To enable the Sidecar, create a Spring Boot application with @EnableSidecar. The main entry point for our application will look like below:

SidecarApp.java
@EnableSidecar  (1)
@SpringBootApplication
public class SidecarApp {

    public static void main(String[] args) {
        SpringApplication.run(SidecarApp.class, args);
    }

}

This annotation includes @EnableCircuitBreaker, @EnableDiscoveryClient, and @EnableZuulProxy.

We need to configure few properties for sidecar i.e. port of non jvm app, health-uri of non jvm app and hostname.

/src/main/resources/application.yml
server:
  port: 5678    (1)

spring:
  application:
    name: sidecar-pdf   (2)
  main:
    web-application-type: reactive

sidecar:
  port: 8058    (3)
  health-uri: http://localhost:8058/health.json (4)
  hostname: localhost   (5)
1 port of the sidecar app
2 app name that will be used for eureka registration
3 port of the non-JVM app
4 health check URI that must be exposed in non-jvm app
5 must be localhost, since sidecar and non-jvm apps run on the same host

Here in above configuration, sidecar app is running at port 5678 on the same host as that of non-JVM app. non JVM app is running on port 8058.

Sample Flask App

We need to expose a health endpoint in non JVM app that will return health of the app. This way Eureka can check if non JVM is actually in a healthy state or not.

In this sample usecase, we will develop a small python app that we will integrate into Spring Cloud using the sidecar approach.

app.py
from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/")
def home():
    return "Hello, World!"


@app.route("/health.json")
def health():
    return jsonify({"status": "UP"}), 200


if __name__ == "__main__":
    app.run(debug=True)

Run the python Flask app using inbuilt development server, using the below command:

$ python app.py

Test that health endpoint is working:

$ curl -X GET http://127.0.0.1:5000/health.json
Response
{
  "status": "UP"
}

That’s the exact format of JSON that spring boot sidecar app will expect from non JVM app.

Increasing the Hystrix timeout

We may want to set a different hystrix timeout value for non-JVM app, which can be done as follow:

application.yml
#Hystrix timeout configuration
hystrix:
  command.default.execution.timeout.enabled: false
  command.default.execution.isolation.thread.timeoutInMilliseconds: 60000

Starting the Eureka registry

We need to start the service registry now so that sidecar-pdf service can register there.

sidecar pdf eureka

Fig. Sidecar service entry in eureka registry

Calling non-JVM API using sidecar

Now we can invoke REST APIs defined in non-JVM through sidecar app, for example we can access health endpoint defined in flask app through API gateway with the help of sidecar app:

GET http://localhost:8080/sidecar-pdf/health.json

Where 8080 is the port of API Gateway. API Gateway converts the sidecar-pdf endpoint to actual host/port using service registry lookup and facilitates the call.

Scalability Patterns for Sidecar Approach

Often we need the non JVM app to scale as per the load of requests, with sidecar we have two approaches to handle the scalability.

  1. Using one sidecar per non JVM app (preferred approach)

  2. Using a load balancer (nginx) in-front of non JVM apps and then just use one Sidecar app for the entire set of non JVM apps.

One sidecar per non JVM app (preferred approach)

In this setup, we create and deploy one sidecar app for each instance of non-jvm app. This normally happens in the same docker container which hosts the non-jvm app. Each pair of sidecar and non-jvm app must run on the same host (different port of-course), otherwise the setup won’t work.

sidecar scaling

Fig. Sidecar for non-jvm apps integration with spring cloud

This is most preferable approach due to various reasons:

  1. All non-jvm apps get registered into Eureka Service Registry because each one of them is accompanied by a sidecar app.

  2. Eureka knows the health of each non-jvm app, because sidecar is monitoring each instance dedicated manner.

Single Sidecar with nginx load balancer

We can configure single sidecar app to talk to load balancer instead of individual non-jvm app. The architecture diagram for such kind of arrangement is shown below:

sidecar nginx load balance

Fig. Sidecar for non-jvm apps integration

There are certain pros and cons of this approach:

  1. Both sidecar and the load balancer shall run on the same host.

  2. If a non jvm app goes down, sidecar app may not know about it since it is talking only to load balancer not the real app instances.

  3. Individual non-jvm app’s metrics can not be collected using this approach.

Integrating non-jvm app that can not provide its own health check endpoint (elasticsearch)

In many cases its not feasible to add a custom health check endpoint to non-JVM app, and we will have to provide an alternate mechanism for health check. For example if we want to use sidecar pattern for elastic search/kafka or Postgres, which likely won’t provide a health check URL as spring boot’s requirements. In this case the sidecar app itself be used to implement such requirement.

SidecarHealthIndicator.java
public interface SidecarHealthIndicator extends HealthIndicator {

}

Top articles in this category:
  1. Sendgrid Dynamic Templates with Spring Boot
  2. Redis rate limiter in Spring Boot
  3. Setting a Random Port in Spring Boot Application at startup
  4. SendGrid emails in Spring Boot
  5. Basic Auth Security in Spring Boot 2
  6. Testing web layer in Spring Boot using WebMvcTest
  7. Run method on Spring Boot startup

Recommended books for interview preparation:

Find more on this topic: