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的WebUI
http://localhost:8100/eureka-server
,nodeSidecar服務以被註冊到Eureka上,並且狀態為UP。
- 訪問 Sidecar 的首頁http://localhost:8130/,提供了三個介面
- 訪問hosts/nodeSidecar可以的到 nodeSidecar 例項的一些資訊
可以看到,該例項維護了 Node 應用的訪問地址"uri": “http://localhost:3000”,這也是接下來要說的:其他微服務可以通過 Sidecar 的服務名宣告式呼叫 Node 服務。[ { "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 } ]
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訪問。