1. Background

spring cloud is the de facto standard for microservices in the java application world, it provides very rich and complete microservice components and is very easy to integrate with java applications. However, as many features of spring cloud are integrated into applications through java jar packages in the form of SDK calls (e.g. eureka client, spring cloud config client, etc.), applications developed in other languages (e.g. go, python, php, js, etc.) do not directly use These jar packages are integrated into the spring cloud microservices system.

However, due to various practical reasons, it is not very realistic to require all components of the business system to adopt java technology stack. At the same time, an important starting point for adopting microservices technology is that different components of the system can independently choose technology stacks for independent design, development, testing and deployment upgrades, and it would be against the original intention of microservices design thinking to constrain all components of the microservices system to adopt a single java technology stack.

If you want a non-java application to integrate into the spring cloud system, one way is to develop a series of corresponding client components for each language, embed them in the application, and still integrate them through SDK calls, the same way as the current java application takes, this way is invasive; the other way is to introduce a separate The other way is to introduce a separate intermediate component through sidecar, non-java applications can reuse most of the spring cloud’s microservices base capabilities with minimal adaptation, which is (basically) non-invasive.

Here is a look at how spring cloud can integrate non-java applications via sidecar by introducing a sidecar implementation, the spring cloud netflix sidecar provided by netflix.

2. spring cloud netflix sidecar working principle

2.1 Process of java application and non-java application calling each other via sidecar

2.1.1 java application calls non-java application service process

java application calls non-java application service process

  1. sidecar registers itself with the registry.
  2. java-app gets the information of sidecar’s instance list from the registry.
  3. java-app selects a specific instance from the list of instances of sidecar based on a certain load balancing algorithm and sends the request to the selected instance.
  4. After receiving the request, sidecar finds out that it is calling the non-java-app service it represents, and then forwards the request to the corresponding non-java-app.
  5. non-java-app processes the request and sends the response to sidecar.
  6. sidecar forwards the response to java-app.

2.1.2 Non-java application calls java application service process

Non-java application calls java application service process

  1. java-app registers itself with the registry.
  2. non-java-app sends the request to sidecar, the first value of the request url path is the service name of java-app.
  3. sidecar gets the instance list information of java-app from the registry.
  4. sidecar selects a specific instance from the java-app instance list based on a certain load balancing algorithm, and sends the request to the selected instance.
  5. java-app processes the request and sends the response to sidecar.
  6. sidecar forwards the response to non-java-app.

2.2 spring cloud netflix sidecar internal principles

The core work inside spring cloud netflix sidecar is provided by netflix open source microservice gateway component zuul, sidecar is actually a secondary development based on zuul.

  1. sidecar and non-java-app must be deployed on the same node, and a sidecar uniquely corresponds to a non-java-app.

  2. sidecar is equivalent to the proxy of non-java-app, the service is registered by sidecar to the registry, the service name of non-java-app seen by other microservices is registered by sidecar, the service port number is the port number of sidecar.

  3. How the registry detects the health status of non-java-app.

    Non-java applications that wish to integrate into the spring cloud system via sidecar need to provide a health detection interface. When the state of the non-java application is normal, this health interface returns a json string with the following content.

    1
    2
    3
    
    {
        "status":"UP"
    }
    

    When sidecar feeds its health status to the registry, it will call the health detection interface provided by the corresponding non-java application, so that only when the non-java application status is normal, the status of the corresponding service instance registered by sidecar in the registry is normal.

  4. How sidecar determines whether the request is from the proxied non-java-app or is sent to the non-java-app.

    When sidecar receives a request, it determines whether the first segment of the path path of the http url is a service name. If it is, it will select a specific instance by certain load balancing algorithm based on the instance information registered in the registry and forward the request to the selected instance. Otherwise, sidecar assumes that it is calling the service of the non-java application it proxies, changes the host of the request to 127.0.0.1 and the port to the port of the corresponding non-java application, and then forwards the request to the corresponding non-java application.

3. spring cloud netflix sidecar application example

The following is an example of how sidecar supports multilingual microservice integration through a java application and a go application calling each other via spring cloud microservices, in this case using eureka as the registry.

3.1 Example of a golang application

The code snippet for the golang application is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

// 提供的健康检测接口
func health(w http.ResponseWriter, r *http.Request)  {
    m := map[string]string{"status": "UP"}
    mjson, _ := json.Marshal(m)
    w.Header().Set("Content-Type", "application/json")
    w.Write(mjson)
}

func hellogo(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "hellogo")
}

func remoteHellojava(w http.ResponseWriter, r *http.Request) {
    // 通过sidecar调用其它微服务的接口,8004是sidecar的服务端口,java-app-service是服务名,/hellojava是服务路径
    resp, _ := http.Get("http://172.18.182.27:8004/java-app-service/hellojava")
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Fprintf(w, string(body))
}

func main() {

    http.HandleFunc("/health.json", health)
    http.HandleFunc("/hellogo", hellogo)
    http.HandleFunc("/remoteHellojava", remoteHellojava)
    // 监听的端口是8093
    err := http.ListenAndServe(":8093", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

The application listens to port 8093 and provides three http services.

  1. /health.json

    is used for sidecar to detect the health status of the application, and eureka to determine the health status of the application indirectly through sidecar.

  2. /hellogo

    The service interface provided by the application, which returns the string “hellogo” when it is called.

  3. /remoteHellojava

    This interface will indirectly call the interface of other microservices by calling the interface of sidecar. url format is as follows.

    1
    
    http://{sidecar-host}:{sidecar-port}/{目的服务的服务名}/{目的服务的path}
    

3.2 sidecar example

The sidecar code snippet is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@EnableSidecar
@EnableEurekaClient
@SpringBootApplication
public class SidecarApplication {

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

}

As you can see, the base code of sidecar is very simple, just add a few annotations. The @EnableSidecar annotation is used to enable sidecar functionality

The basic configuration is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 服务端口号
server.port: 8004

# 服务名
spring:
  application:
    name: go-app-service

# 服务注册和发现
eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://127.0.0.1:8002/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ipAddress}:${server.port}

# sidecar配置
sidecar:
  port: 8093
  health-uri: http://localhost:8093/health.json

As you can see from the above configuration, except for two configuration items related to sidecar: sidecar.port and sidecar.health-uri, the rest of the configuration is the same as a normal spring cloud application, or to be more precise, the same as configuring the microservice gateway zuul.

sidecar.port is used to tell the service port that the sidecar go application listens on (the reason for not providing go application host is that sidecar invokes the go application service with a fixed 127.0.0.1 address), and sidecar.health-uri is used to tell the health Health testing interface

Start the go application and sidecar separately, and you can see the service instances registered by sidecar in eureka, as follows.

1
GO-APP-SERVICE  n/a (1) (1) UP (1) - 172.18.182.27:8004

3.3 Example of a java application

The code snippet of the java application is as follows.

1
2
3
4
5
6
7
8
@FeignClient(value = "go-app-service")
@RequestMapping(value = "/")
public interface GoAppServiceFeignClient {

    @GetMapping(value = "/hellogo")
    String hello();

}

The remote microservice is invoked via feign. @FeignClient(value = "go-app-service") specifies the name of the microservice, which is the name of the service registered by sidecar with eureka as we know from the previous.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@RestController
@RequestMapping(value = "/")
public class Controller {

    @Autowired
    private GoAppServiceFeignClient feignClient;

    @GetMapping(value = "/hellojava")
    public String hellojava() {
        return "hellojava";
    }

    @GetMapping(value = "/remoteHellogo")
    public String hellogo() {
        return feignClient.hello();
    }

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {

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

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {

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

}

The configuration file reads as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
server:
  port: 8003

spring:
  application:
    name: java-app-service

eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://127.0.0.1:8002/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ipAddress}:${server.port}

Start the program and you can see the service instances registered by the application in eureka, as follows.

1
JAVA-APP-SERVICE    n/a (1) (1) UP (1) - 172.18.182.27:8003

3.4 Mutual call test

Calling the /remoteHellojava interface of the go application returns hellojava, which means the call was successful.

1
2
3
% curl http://localhost:8093/remoteHellojava

hellojava

Calling the /remoteHellogo interface of a java application returns hellogo, indicating that the call was successful.

1
2
3
% curl http://localhost:8003/remoteHellogo

hellogo

Reference https://www.jianshu.com/p/9754b1a9929b