Spring Cloud netflix 概覽和架構設計

  • 2019 年 11 月 24 日
  • 筆記

Spring Cloud簡介

Spring Cloud是基於Spring Boot的一整套實現微服務的框架。他提供了微服務開發所需的配置管理、服務發現、斷路器、智慧路由、微代理、控制匯流排、全局鎖、決策競選、分散式會話和集群狀態管理等組件。最重要的是,跟spring boot框架一起使用的話,會讓你開發微服務架構的雲服務非常好的方便。 Spring Cloud包含了非常多的子框架,其中,Spring Cloud netflix是其中一套框架,由Netflix開發後來又併入Spring Cloud大家庭,它主要提供的模組包括:服務發現、斷路器和監控、智慧路由、客戶端負載均衡等。

Spring Cloud Netflix項目的時間還不長,併入spring cloud大家族還是2年前,所以相關的使用文檔還比較少,除了官方文檔,中國也有一個中文社區。但是,如果是剛開始接觸這個,想使用它搭建一套微服務的應用架構,總是會有不知如何下手的感覺。所以,這篇文章就是從整體上來看看這個框架的各個組件、用處是什麼、如何相互作用。最後再結合實際的經驗,介紹一下可能會出現的問題,以及針對一些問題,採用什麼樣的解決方案。

微服務架構

首先,我們來看看一般的微服務架構需要的功能或使用場景:

  • 我們把整個系統根據業務拆分成幾個子系統。
  • 每個子系統可以部署多個應用,多個應用之間使用負載均衡。
  • 需要一個服務註冊中心,所有的服務都在註冊中心註冊,負載均衡也是通過在註冊中心註冊的服務來使用一定策略來實現。
  • 所有的客戶端都通過同一個網關地址訪問後台的服務,通過路由配置,網關來判斷一個URL請求由哪個服務處理。請求轉發到服務上的時候也使用負載均衡。
  • 服務之間有時候也需要相互訪問。例如有一個用戶模組,其他服務在處理一些業務的時候,要獲取用戶服務的用戶數據。
  • 需要一個斷路器,及時處理服務調用時的超時和錯誤,防止由於其中一個服務的問題而導致整體系統的癱瘓。
  • 還需要一個監控功能,監控每個服務調用花費的時間等。

Spring Cloud Netflix組件以及部署

Spring Cloud Netflix框架剛好就滿足了上面所有的需求,而且最重要的是,使用起來非常的簡單。Spring Cloud Netflix包含的組件及其主要功能大致如下:

  • Eureka,服務註冊和發現,它提供了一個服務註冊中心、服務發現的客戶端,還有一個方便的查看所有註冊的服務的介面。 所有的服務使用Eureka的服務發現客戶端來將自己註冊到Eureka的伺服器上。
  • Zuul,網關,所有的客戶端請求通過這個網關訪問後台的服務。他可以使用一定的路由配置來判斷某一個URL由哪個服務來處理。並從Eureka獲取註冊的服務來轉發請求。
  • Ribbon,即負載均衡,Zuul網關將一個請求發送給某一個服務的應用的時候,如果一個服務啟動了多個實例,就會通過Ribbon來通過一定的負載均衡策略來發送給某一個服務實例。
  • Feign,服務客戶端,服務之間如果需要相互訪問,可以使用RestTemplate,也可以使用Feign客戶端訪問。它默認會使用Ribbon來實現負載均衡。
  • Hystrix,監控和斷路器。我們只需要在服務介面上添加Hystrix標籤,就可以實現對這個介面的監控和斷路器功能。
  • Hystrix Dashboard,監控面板,他提供了一個介面,可以監控各個服務上的服務調用所消耗的時間等。
  • Turbine,監控聚合,使用Hystrix監控,我們需要打開每一個服務實例的監控資訊來查看。而Turbine可以幫助我們把所有的服務實例的監控資訊聚合到一個地方統一查看。這樣就不需要挨個打開一個個的頁面一個個查看。

下面就是使用上述的子框架實現的為服務架構的組架構圖:

在上圖中,有幾個需要說明的地方:

  • ZUUL網關也在註冊中心註冊,把它也當成一個服務來統一查看。
  • 負載均衡不是一個獨立的組件,它運行在網關、服務調用等地方,每當需要訪問一個服務的時候,就會通過Ribbon來獲得一個該服務的實例去掉用。Ribbon從Eureka註冊中心獲得服務和實例的列表,而不是發送每個請求的時候從註冊中心獲得。
  • 我們可以使用RestTemplate來進行服務間調用,也可以配置FeignClient來使用,不管什麼方式,只要使用服務註冊,就會默認使用Ribbon負載均衡。(RestTemplate需要添加@LoadBalanced
  • 每個服務都可以開啟監控功能,開啟監控的服務會提供一個servlet介面/hystrix.stream,如果你需要監控這個服務的某一個方法的運行統計,就在這個方法上加一個@HystrixCommand的標籤。
  • 查看監控資訊,就是在Hystrix Dashboard上輸入這個服務的監控url: http://serviceIp:port/hystrix.stream,就可以用圖表的方式查看運行監控資訊。
  • 如果要把所有的服務的監控資訊聚合在一起統一查看,就需要使用Turbine來聚合所需要的服務的監控資訊。

我們也可以從上圖中看出該架構的部署方式:

  • 獨立部署一個網關應用
  • 服務註冊中心和監控可以配置在一個應用里,也可以是2個應用。
  • 服務註冊中心也可以部署多個,通過區域zone來區分,來實現高可用。
  • 每個服務,根據負載和高可用的需要,部署一個或多個實例。

Spring Cloud Netflix組件開發

上面說到,開發基於Spring Cloud Netflix的微服務非常簡單,一般我們是和spring boot一起使用,如果你想在自己原先的java web應用中使用也可以通過添加相關配置來實踐。 有關開發的詳細內容,可以參考spring cloud中文社區的這個系列文章,裡面詳細介紹了每一種組件的開發。這裡,就只是來看一下服務註冊中和監控模組的開發,還有服務調用的開發,其他的可以直接參考上面的系列文章。

註冊和監控中心的開發

這個非常簡單,就下面一個類:

1234567891011

// 省略import@SpringBootApplication@EnableEurekaServer@EnableHystrixDashboardpublic class ApplicationRegistry { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); }}

這裡使用spring boot標籤的@SpringBootApplication說明當前的應用是一個spring boot應用。這樣我就可以直接用main函數在IDE裡面啟動這個應用,也可以打包後用命令行啟動。當然也可以把打包的war包用tomcat之類的伺服器啟動。

使用標籤@EnableEurekaServer,就能在啟動過程中啟動Eureka服務註冊中心的組件。它會監聽一個埠,默認是8761,來接收服務註冊。並提供一個web頁面,打開以後,可以看到註冊的服務。 添加@EnableHystrixDashboard就會提供一個監控的頁面,我們可以在上面輸入要監控的服務的地址,就可以查看啟用了Hystrix監控的介面的調用情況。 當然,為了使用上面的組件,我們需要在maven的POM文件里添加相應的依賴,比如使用spring-boot-starter-parent,依賴spring-cloud-starter-eureka-serverspring-cloud-starter-hystrix-dashboard等。

服務間調用

在網上的各種文檔中,對服務間調用,都沒有說明的很清楚,所以這裡特別說明一下這個如何開發。 有兩種方式可以進行服務調用,RestTemplateFeignClient。不管是什麼方式,他都是通過REST介面調用服務的http介面,參數和結果默認都是通過jackson序列化和反序列化。因為Spring MVC的RestController定義的介面,返回的數據都是通過jackson序列化成json數據。

RestTemplate

使用這種方式,只需要定義一個RestTemplate的Bean,設置成LoadBalanced即可:

@Configurationpublic class SomeCloudConfiguration {    @LoadBalanced    @Bean    RestTemplate restTemplate() {        return new RestTemplate();      }}

這樣我們就可以在需要用的地方注入這個bean使用:

public class SomeServiceClass {    @Autowired    private RestTemplate restTemplate;    public String getUserById(Long userId) {          UserDTO results = restTemplate.getForObject("http://users/getUserDetail/" + userId, UserDTO.class);        return results;      }}

其中,users是服務ID,Ribbon會從服務實例列表獲得這個服務的一個實例,發送請求,並獲得結果。對象UserDTO需要序列號,它的反序列號會自動完成。

FeignClient

除了上面的方式,我們還可以用FeignClient。還是直接看程式碼:

@FeignClient(value = "users", path = "/users")public interface UserCompositeService {      @RequestMapping(value = "/getUserDetail/{id}",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE)    UserDTO getUserById(@PathVariable Long id);}

我們只需要使用@FeignClient定義一個借口,Spring Cloud Feign會幫我們生成一個它的實現,從相應的users服務獲取數據。

其中,@FeignClient(value = "users", path = "/users/getUserDetail")裡面的value是服務ID,path是這一組介面的path前綴。 在下面的方法定義里,就好像設置Spring MVC的介面一樣,對於這個方法,它對應的URL是/users/getUserDetail/{id}

然後,在使用它的時候,就像注入一個一般的服務一樣注入後使用即可:

public class SomeOtherServiceClass {    @Autowired    private UserCompositeService userService;    public void doSomething() {   	// .....          UserDTO results = userService.getUserById(userId);        // other operation...      }}

遇到的問題

由於Spring Cloud說明文檔較少,微服務的架構相對來說也比較複雜,在開發的時候,難免會遇到很多問題,有一些是如何更好地使用這套框架去搭建架構,也有一些問題是如何配置。這裡就一些我在搭建微服務架構的時候遇到的問題提供一些方法。

請求超時問題

ZUUL網關默認的超時時間非常短,這是為了保證調用服務的時候能夠很快的響應。但是,我們會有一些業務方法運行的時間比較長,特別是在測試伺服器。這時候,就需要調整超時時間。這個超時有幾個地方:

負載均衡Ribbon,負載均衡有一個超時的設置,包括鏈接時間和讀取時間

Hystrix斷路器也有一個超時設置,它需要在適當的時候返回,而不是一直等在一個請求上。 對應的配置如下:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 30000

ribbon:

ReadTimeout: 30000

ConnectTimeout: 15000

服務ID的問題

服務的ID,也就是服務名,可以通過在application.yml或者bootstrap.yml裡面設置:

spring:

application:

name: users

管理路徑的問題

Spring Boot的應用默認都是開放一些管理的介面,如/info/health和metrics監控的介面/metrics等。如果你使用默認的路徑,使用Hystrix監控、服務註冊中心的監聽服務狀態都不會有問題,但是,如果你想使用別的路徑,例如/management/info/management/health,那就牽扯到很多地方,而且,每個版本可能會或多或少的有一些問題,導致你遇到的問題還會不一樣。我遇到過的問題有:

註冊成功卻找不到服務

首先,註冊可以成功,在Eureka伺服器頁面上也可以看到各個服務。但是,當你通過網關調用的時候,卻總是提示服務找不到。這時候可能就需要在每個服務的application.yml裡面進行如下配置:

eureka:

instance:

nonSecurePort: ${server.port}

appname: ${spring.application.name}

statusPageUrlPath: ${management.context-path}/info

healthCheckUrlPath: ${management.context-path}/health

簡單來說,這就是告訴在註冊的時候,同時告訴Eureka伺服器,服務的埠是什麼,用來監聽狀態的路徑是什麼。這是因為我們使用了不同的管理介面路徑,而Eureka伺服器沒有使用相應的路徑。

如果一切正常,你在Eureka伺服器上點擊一個註冊的服務,應該能打開一個info頁面。他可能是空白的,但是,至少Eureka伺服器能通過這個知道服務的運行正常。 這個問題也不是在所有的版本都存在,只是在某一些spring cloud的版本存在。

設置了管理路徑的Hystrix監控

剛才說了Hystrix監控的路徑是http://serviceIp:port/hystrix.stream,如果你設置了管理介面的路徑,那麼這個監控路徑也會變成:

http://serviceIp:port/${management.context-path}/hystrix.stream

如果這時候,你再想使用Turbine聚合,Turbine就會找不到了,因為它默認使用Eureka伺服器上的伺服器地址和埠,在後面添加/hystrix.stream。這時候,你就需要設置Turbine:

turbine:

aggregator:

clusterConfig: USER

appConfig: USER

instanceUrlSuffix:

USER: /user/hystrix.stream

管理路徑的安全性

對於微服務部署的幾台機器,可以通過開通防火牆來控制誰可以訪問管理介面,但是,即使是這樣,為了安全性等,我一般還是會把管理端介面也用spring security來保護。這樣一來,監控介面就沒法直接訪問了。

服務間調用的許可權驗證

一般我們的API介面都需要某種授權才能訪問,登陸成功以後,然後通過token或者cookie等方式才能調用介面。 使用Spring Cloud Netfix框架的話,登錄的時候,把登錄請求轉發到相應的用戶服務上,登陸成功後,會設置cookie或header token等。然後客戶端接下來的請求就會帶著這些驗證資訊,從ZUUL網關傳到相應的服務上進行驗證。 ZUUL網關在把請求轉發到後台的服務的時候,會默認把一些header傳到服務端,如:Cookie, Set-Cookie, Authorization。這樣,客戶端請求的相關headers就可以傳遞到服務端,服務端設置的cookie也可以傳到客戶端。 但是,如果你想禁止某些header透傳到服務端,可以在ZUUL網關的application.yml配置里通過下面的方式禁用:

zuul:

routes:

users:

path: /users/**

sensitiveHeaders: Cookie,Set-Cookie,Authorization

serviceId: user

剛才說了我們的某個服務有時候需要調用另一個服務,這時候,這個請求不是客戶端發起,他的請求的header裡面也不會有任何驗證資訊。這時候,要麼,通過防火牆等設置,保證服務間調用的介面,只能某幾個地址訪問;要麼,就通過某種方式設置header。 同時,如果你想在某個服務裡面獲得這個請求的真是IP,(因為請求的通過網關轉發而來,你直接通過request獲得ip得到的是網關的IP),就可以從headerX-Forwarded-Host獲得。如果想禁用這個header,也可以:

zuul.addProxyHeaders = false

如果你使用RestTemplate的方式調用,可以在請求裡面添加一個有header的Options

也可以通過如下的攔截器的方式設置,它對RestTemplate方式和FeignClient的方式都可以起作用:

@Beanpublic RequestInterceptor requestInterceptor() {    return new RequestInterceptor() {        @Override        public void apply(RequestTemplate template) {              String authToken = getToken();              template.header(AUTH_TOKEN_HEADER, authToken);          }      };}