1. 程式人生 > >Spring Clould Sidecar整合異構服務(Finchley版本)

Spring Clould Sidecar整合異構服務(Finchley版本)

本節我們主要討論一下異構平臺(比如,nodejs、python、php等提供的Rest介面服務)的服務,怎麼通過spring cloud元件對這些服務註冊到eureka中心以及與在微服務中怎麼和異構平臺的服務進行通訊。這裡主要是通過spring cloud的sidecar來構建異構平臺的服務註冊與通訊。

sidecar靈感來自Netflix Prana。它可以獲取註冊中心的所有微服務例項的資訊(例如host,埠等)的http api。也可以通過嵌入的Zuul代理來代理服務呼叫,該代理從Eureka獲取其路由條目。 Spring Cloud配置伺服器可以通過主機查詢或通過嵌入的Zuul直接訪問。

涉及到的服務如下:

服務名 用途
eureka-server 8100 服務註冊與發現
nodeSidecar 8130 異構服務對接服務
node 3000 nodejs服務
consumer 8106 消費者服務,與node服務存在宣告式呼叫

1.1 Sidecar

1.1.1 Sidecar服務

先來看一下引入Sidecar需要做一些什麼。

1. 新增 Maven 依賴,很簡單,甚至不需要eureka-client的依賴,因為它已經整合至 Sidecar 的依賴中
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-netflix-sidecar</artifactId>
    </dependency>
</dependencies>
2. 接下來是註解,在 Sidecar 主類上新增@EnableSidecar註解,我們來看看這個註解包含些什麼
@EnableCircuitBreaker
@EnableZuulProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(SidecarConfiguration.class)
public @interface EnableSidecar {

}

包含了閘道器 Zuul 以及微服務結構中不可或缺的熔斷器 Hystrix

3. 最後是配置檔案,在application.yml中新增如下配置
server:
  port: 8130
spring:
  application:
    name: nodeSidecar
eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:9090/eureka/
sidecar:
  port: 3000
  health-uri: http://localhost:${sidecar.port}/health

宣告服務名和註冊中心地址都沒什麼問題,最核心的就是 sidecar 的幾個配置,包括

  • sidecar.port 監聽的 Node 應用的埠號,
  • sidecar.health-uri Node 應用的健康檢查介面的 uri

1.1.2 健康檢查介面

需要注意的是:Node.js 的微服務應用必須實現一個/health健康檢查介面,Sidecar 應用會每隔幾秒訪問一次該介面,並將該服務的健康狀態返回給 Eureka,該介面只需要返回{ status: 'UP' }這樣一串Json即可。


var http = require('http');
var url = require("url");
var path = require('path');

// 建立server
var server = http.createServer(function(req, res) {
    // 獲得請求的路徑
    var pathname = url.parse(req.url).pathname;
    res.writeHead(200, { 'Content-Type' : 'application/json; charset=utf-8' });
    if (pathname === '/index') {
        res.end(JSON.stringify({ "index" : "歡迎來到首頁" }));
    }
    else if (pathname === '/health') {
        res.end(JSON.stringify({ "status" : "UP" }));
    }
    // 其他情況返回404
    else {
        res.end("404");
    }
});
// 建立監聽,並列印日誌
server.listen(3000, function() {
    console.log('listening on localhost:3000');
});

1.2 服務註冊

準備好eureka,nodeSidecar,node服務。按順序啟動eureka->node服務->nodeSidecar

  • 訪問Eureka的WebUIhttp://localhost:8100/eureka-server,nodeSidecar服務以被註冊到Eureka上,並且狀態為UP。
    eureka webui
  • 訪問 Sidecar 的首頁http://localhost:8130/,提供了三個介面
    eureka webui
  • 訪問hosts/nodeSidecar可以的到 nodeSidecar 例項的一些資訊
    [
        {
            "host": "192.168.216.1",
            "port": 3000,
            "uri": "http://192.168.216.1:3000",
            "metadata": {
                "management.port": "8130"
            },
            "serviceId": "NODESIDECAR",
            "secure": false,
            "instanceInfo": {
                "instanceId": "localhost:nodeSidecar:8130",
                "app": "NODESIDECAR",
                "appGroupName": null,
                "ipAddr": "192.168.216.1",
                "sid": "na",
                "homePageUrl": "http://192.168.216.1:3000/",
                "statusPageUrl": "http://192.168.216.1:8130/actuator/info",
                "healthCheckUrl": "http://192.168.216.1:8130/actuator/health",
                "secureHealthCheckUrl": null,
                "vipAddress": "nodeSidecar",
                "secureVipAddress": "nodeSidecar",
                "countryId": 1,
                "dataCenterInfo": {
                    "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                    "name": "MyOwn"
                },
                "hostName": "192.168.216.1",
                "status": "UP",
                "overriddenStatus": "UNKNOWN",
                "leaseInfo": {
                    "renewalIntervalInSecs": 30,
                    "durationInSecs": 90,
                    "registrationTimestamp": 1537408212824,
                    "lastRenewalTimestamp": 1537408361916,
                    "evictionTimestamp": 0,
                    "serviceUpTimestamp": 1537408212022
                },
                "isCoordinatingDiscoveryServer": false,
                "metadata": {
                    "management.port": "8130"
                },
                "lastUpdatedTimestamp": 1537408212824,
                "lastDirtyTimestamp": 1537408211890,
                "actionType": "ADDED",
                "asgName": null
            },
            "scheme": null
        }
    ]
    
    可以看到,該例項維護了 Node 應用的訪問地址"uri": “http://localhost:3000”,這也是接下來要說的:其他微服務可以通過 Sidecar 的服務名宣告式呼叫 Node 服務。

1.3 宣告式服務呼叫

以上,我們已經驗證了 Eureka 可以通過 Sidecar 間接的管理基於 Node 的微服務。而在微服務體系中,還有非常重要的一點,就是服務間的呼叫。Spring Cloud 允許我們使用服務名進行服務間的呼叫,摒棄了原先的固定寫死的 IP 地址,便於服務叢集的橫向拓展及維護。那麼,Non-JVM 的微服務與其他服務間是否可以通過服務名互相呼叫呢,答案是可以的。

1.3.1 被呼叫

我們假設下面一個場景,node服務提供了/index介面,返回json字串。而 consumer服務需要訪問node服務拿到返回的json資料。也就是 consumer服務需要訪問 node服務 的/index介面拿到json字串。

1. 在 node服務中實現/index介面,返回json字串
// 建立server
var server = http.createServer(function(req, res) {
    // 獲得請求的路徑
    var pathname = url.parse(req.url).pathname;
    res.writeHead(200, { 'Content-Type' : 'application/json; charset=utf-8' });
    // 訪問http://localhost:8060/,將會返回{"index":"歡迎來到首頁"}
    if (pathname === '/index') {
        res.end(JSON.stringify({ "index" : "歡迎來到首頁" }));
    }
    // 訪問http://localhost:8060/health,將會返回{"status":"UP"}
    else if (pathname === '/health') {
        res.end(JSON.stringify({ "status" : "UP" }));
    }
    // 其他情況返回404
    else {
        res.end("404");
    }
});
2. consumer作為 node 服務的呼叫者,需要宣告 Client 介面,程式碼如下
@FeignClient("nodeSidecar")
public interface NodeServiceClient {
    @GetMapping("/index")
    String getIndex();
}

注意到@FeignClient註解中呼叫的服務名填寫的是 nodeSidecar (大小寫不敏感),因為自始至終 Eureka 中註冊的是 Sidecar 的資訊,而 Sidecar 例項維護了 node服務 的地址資訊,所以它可以將請求轉發至 node服務。

1.3.2 呼叫其它微服務

其他微服務可以通過 Sidecar 例項的服務名間接呼叫 Node 服務。同樣的,node服務 也可以通過服務名呼叫其它微服務,這要歸功於@EnableZuulProxy。

訪問http://localhost:8130/consumer/index驚訝的發現,這和我們訪問http://localhost:8106/index結果是一樣的,這是由於 Sidecar 引入了 Zuul 閘道器,它可以獲取 Eureka 上註冊的服務的地址資訊,從而進行路由跳轉。因此,node訪問其它微服務的地址格式為:http:localhost:8130/{serviceName}/method

另外,可以直接使用eureka的HTTP介面整合異構服務。由於eureka也是通過HTTP協議的介面暴露自身服務的,因此我們可以在node.js中手動傳送HTTP請求實現服務的註冊、查詢和心跳功能。eureka介面描述資訊可以在官方github的wiki中找到。但是這種方式存在弊端:

  • 無法使用Spring Cloud元件Ribbob,Hystrix等提供的便利;
  • 無法通過服務名訪問其它微服務,其它服務的ip,port需暴露給node服務供http訪問。