1 背景
spring cloud是java應用世界中微服務的事實標準管引,它提供了非常豐富且完整的微服務組件,且非常方便與java應用程序進行集成闯两。但是褥伴,由于spring cloud很多功能是通過java jar包以SDK調(diào)用的形式集成到應用程序中的(如eureka client谅将,spring cloud config client等),使用其他語言(如go重慢、python饥臂、php、js等)開發(fā)的應用程序并不能直接使用這些jar包集成到spring cloud微服務系統(tǒng)中似踱。但是隅熙,由于各種現(xiàn)實的原因,要求業(yè)務系統(tǒng)所有的組件都采用java技術棧不是很現(xiàn)實核芽。同時囚戚,采用微服務技術的一個重要出發(fā)點是系統(tǒng)的不同組件可以獨立選擇技術棧,進行獨立設計轧简、開發(fā)弯淘、測試和部署升級,如果約束微服務系統(tǒng)的所有組件都必須采用單一的java技術棧吉懊,也違背了微服務設計思想的初衷庐橙。如果要讓非java應用集成到spring cloud系統(tǒng)中,一種方式是為每種語言開發(fā)一系列相應的客戶端組件借嗽,嵌入到應用程序中态鳖,仍然通過SDK調(diào)用的方式進行集成,與當前java應用程序采取的方式一樣恶导,這種方式是侵入式的浆竭;另一種方式是通過sidecar的方式,引入一個單獨的中間組件惨寿,非java應用程序只要做極少的適配邦泄,就可以復用spring cloud大部分微服務基礎能力,這種方式(基本上可以說)是非侵入式的裂垦。這里通過介紹一個sidecar的實現(xiàn)顺囊,即netflix公司提供的spring cloud netflix sidecar,來看看spring cloud如何通過sidecar集成非java應用程序蕉拢。
2 spring cloud netflix sidecar工作原理
2.1 java應用程序與非java應用程序通過sidecar相互調(diào)用過程
2.1.1 java應用程序調(diào)用非java應用程序服務過程
1)sidecar向注冊中心注冊自己
2)java-app從注冊中心獲取sidecar的實例列表信息
3)java-app從sidecar的實例列表中基于一定的負載均衡算法選擇一個具體的實例特碳,并將請求發(fā)送給選中的實例
4)sidecar接收到請求后,發(fā)現(xiàn)是調(diào)用自己代理的non-java-app服務晕换,然后將請求轉(zhuǎn)發(fā)給相應的non-java-app
5)non-java-app處理請求午乓,將響應發(fā)送給sidecar
6)sidecar將響應轉(zhuǎn)發(fā)給java-app
2.1.2 非java應用程序調(diào)用java應用程序服務過程
1)java-app向注冊中心注冊自己
2)non-java-app將請求發(fā)送給sidecar,請求url path的第一段值為java-app的服務名
3)sidecar從注冊中心獲取java-app的實例列表信息
4)sidecar從java-app的實例列表中基于一定的負載均衡算法選擇一個具體的實例闸准,并將請求發(fā)送給選中的實例
5)java-app處理請求益愈,將響應發(fā)送給sidecar
6)sidecar將響應轉(zhuǎn)發(fā)給non-java-app
2.2 spring cloud netflix sidecar內(nèi)部原理
spring cloud netflix sidecar內(nèi)部的核心工作是由netflix開源的微服務網(wǎng)關組件zuul提供的,sidecar其實是在zuul基礎上進行的二次開發(fā)夷家。
1)sidecar與non-java-app必須部署在同一個節(jié)點上蒸其,且一個sidecar唯一對應一個non-java-app
2)sidecar相當于non-java-app的代理敏释,由sidecar向注冊中心注冊服務,其他微服務看到的non-java-app的服務名是由sidecar注冊的枣接,服務端口號是sidecar的端口號
3)注冊中心如何檢測non-java-app的健康狀態(tài)
非java應用程序如果希望通過sidecar集成到spring cloud系統(tǒng)中颂暇,需要提供一個health檢測接口,當非java應用程序狀態(tài)正常時但惶,這個health接口返回一個json字符串耳鸯,內(nèi)容如下所示:
{
"status":"UP"
}
sidecar在向注冊中心反饋自己的健康狀態(tài)時,會調(diào)用相應非java應用程序提供的health檢測接口膀曾,這樣只有非java應用程序狀態(tài)正常時县爬,sidecar在注冊中心注冊的相應服務實例的狀態(tài)才是正常的。
4)sidecar如何確定請求是來自于被代理的non-java-app還是發(fā)送給non-java-app的
當sidecar接收到請求時添谊,會判斷http url的path路徑的第一段是不是一個服務名财喳。如果是,則會根據(jù)注冊中心注冊的實例信息斩狱,通過一定的負載均衡算法選擇一個具體的實例耳高,將請求轉(zhuǎn)發(fā)給選擇的實例。否則所踊,sidecar則認為是調(diào)用自己代理的非java應用程序服務泌枪,將請求的host改為127.0.0.1,port改為相應非java應用程序的port秕岛,然后將請求轉(zhuǎn)發(fā)給相應的非java應用程序
3 spring cloud netflix sidecar應用舉例
下面通過一個java應用程序與一個go應用程序通過spring cloud微服務相互調(diào)用來舉例說明sidecar是如何支持多語言微服務集成的碌燕,本例中使用的注冊中心為eureka。
3.1 go應用程序?qū)嵗?/h2>
go應用程序的代碼片段如下所示:
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調(diào)用其它微服務的接口继薛,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)
// 監(jiān)聽的端口是8093
err := http.ListenAndServe(":8093", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
該應用監(jiān)聽的port為8093遏考,提供了三個http接口:
1)/health.json
用于sidecar檢測應用的健康狀態(tài)慈鸠,eureka則通過sidecar間接確定應用的健康狀態(tài)
2)/hellogo
應用提供的服務接口,被調(diào)用時返回字符串"hellogo"
3)/remoteHellojava
該接口會通過調(diào)用sidecar的接口來間接調(diào)用其他微服務的接口诈皿。url格式為:
http://{sidecar-host}:{sidecar-port}/{目的服務的服務名}/{目的服務的path}
3.2 sidecar實例
sidecar代碼片段如下:
@EnableSidecar
@EnableEurekaClient
@SpringBootApplication
public class SidecarApplication {
public static void main(String[] args) {
SpringApplication.run(SidecarApplication.class, args);
}
}
可見林束,sidecar的基礎代碼非常簡單,只要添加幾個注解即可稽亏。@EnableSidecar注解用來開啟sidecar功能
基礎配置如下:
# 服務端口號
server.port: 8004
# 服務名
spring:
application:
name: go-app-service
# 服務注冊和發(fā)現(xiàn)
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
從上述的配置可見,除了跟sidecar相關的兩個配置項:sidecar.port和sidecar.health-uri缕题,其余的配置跟一個普通的spring cloud應用程序相同截歉,更準確的說,跟配置微服務網(wǎng)關zuul是一樣的烟零。
sidecar.port用來告訴sidecar go應用程序監(jiān)聽的服務端口(不提供go應用程序host的原因是sidecar調(diào)用go應用程序服務時固定使用127.0.0.1地址)瘪松,sidecar.health-uri用來告訴sidecar go應用程序提供的健康檢測接口
分別啟動go應用程序和sidecar记罚,可以在eureka中看到sidecar注冊的服務實例烟馅,如下:
GO-APP-SERVICE n/a (1) (1) UP (1) - 172.18.182.27:8004
3.3 java應用程序?qū)嵗?/h2>
java應用的代碼片段如下:
@FeignClient(value = "go-app-service")
@RequestMapping(value = "/")
public interface GoAppServiceFeignClient {
@GetMapping(value = "/hellogo")
String hello();
}
通過feign調(diào)用遠端微服務,@FeignClient(value = "go-app-service")指定微服務的名字捆憎,從前面可知,這個服務名即sidecar向eureka注冊的服務名
@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();
}
}
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
配置文件內(nèi)容如下:
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}
啟動程序太伊,可以在eureka中看到應用程序注冊的服務實例曙痘,如下:
JAVA-APP-SERVICE n/a (1) (1) UP (1) - 172.18.182.27:8003
3.4 相互調(diào)用測試
調(diào)用go應用程序的/remoteHellojava
接口,返回hellojava
,說明調(diào)用成功
% curl http://localhost:8093/remoteHellojava
hellojava
調(diào)用java應用程序的/remoteHellogo
接口超燃,返回hellogo
届良,說明調(diào)用成功
% curl http://localhost:8003/remoteHellogo
hellogo