spring cloud組件

這篇文章對springcloud 各個組件大概介紹了一下。轉載本文。其實我主要是對ribbon ,resttemplate ,feignclient三者之間的關係一直模糊不清。這篇文章裏提到了這三者。感覺介紹的還不錯。使用feignclient時,它會通過ribbon機制獲取對應服務的ip列表。比方說A服務,有兩個實例。每個實例的實際ip是:127.0.0.1:9871,127.0.0.1:9872.feignclient在訪問相應的rest接口時,會轉換爲127.0.0.1:9871/xxx/xxx,127.0.0.1:9872/xxx/xxx

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

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

2 微服務架構

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

    1、我們把整個系統根據業務拆分成幾個子系統。

    2、每個子系統可以部署多個應用,多個應用之間使用負載均衡。

    3、需要一個服務註冊中心,所有的服務都在註冊中心註冊,負載均衡也是通過在註冊中心註冊的服務來使用一定策略來實現。

    4、所有的客戶端都通過同一個網關地址訪問後臺的服務,通過路由配置,網關來判斷一個URL請求由哪個服務處理。請求轉發到服務上的時候也使用負載均衡。

    5、服務之間有時候也需要相互訪問。例如有一個用戶模塊,其他服務在處理一些業務的時候,要獲取用戶服務的用戶數據。

    6、需要一個斷路器,及時處理服務調用時的超時和錯誤,防止由於其中一個服務的問題而導致整體系統的癱瘓。

    7、還需要一個監控功能,監控每個服務調用花費的時間等。

3 Spring Cloud Netflix組件以及部署

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

    1、Eureka,服務註冊和發現,它提供了一個服務註冊中心、服務發現的客戶端,還有一個方便的查看所有註冊的服務的界面。 所有的服務使用Eureka的服務發現客戶端來將自己註冊到Eureka的服務器上。

    2、Zuul,網關,所有的客戶端請求通過這個網關訪問後臺的服務。他可以使用一定的路由配置來判斷某一個URL由哪個服務來處理。並從Eureka獲取註冊的服務來轉發請求。

    3、Ribbon,即負載均衡,Zuul網關將一個請求發送給某一個服務的應用的時候,如果一個服務啓動了多個實例,就會通過Ribbon來通過一定的負載均衡策略來發送給某一個服務實例。
    4、Feign,服務客戶端,服務之間如果需要相互訪問,可以使用RestTemplate,也可以使用Feign客戶端訪問。它默認會使用Ribbon來實現負載均衡。

    5、Hystrix,監控和斷路器。我們只需要在服務接口上添加Hystrix標籤,就可以實現對這個接口的監控和斷路器功能。

    6、Hystrix Dashboard,監控面板,他提供了一個界面,可以監控各個服務上的服務調用所消耗的時間等。

    7、Turbine,監控聚合,使用Hystrix監控,我們需要打開每一個服務實例的監控信息來查看。而Turbine可以幫助我們把所有的服務實例的監控信息聚合到一個地方統一查看。這樣就不需要挨個打開一個個的頁面一個個查看。

下面就是使用上述的子框架實現的爲服務架構的組架構圖: 
在上圖中,有幾個需要說明的地方:

    1、ZUUL網關也在註冊中心註冊,把它也當成一個服務來統一查看。

    2、負載均衡不是一個獨立的組件,它運行在網關、服務調用等地方,每當需要訪問一個服務的時候,就會通過Ribbon來獲得一個該服務的實例去掉用。Ribbon從Eureka註冊中心獲得服務和實例的列表,而不是發送每個請求的時候從註冊中心獲得。

    3、我們可以使用RestTemplate來進行服務間調用,也可以配置FeignClient來使用,不管什麼方式,只要使用服務註冊,就會默認使用Ribbon負載均衡。(RestTemplate需要添加@LoadBalanced)

    4、每個服務都可以開啓監控功能,開啓監控的服務會提供一個servlet接口/hystrix.stream,如果你需要監控這個服務的某一個方法的運行統計,就在這個方法上加一個@HystrixCommand的標籤。

    5、查看監控信息,就是在Hystrix Dashboard上輸入這個服務的監控url: http://serviceIp:port/hystrix.stream,就可以用圖表的方式查看運行監控信息。

    6、如果要把所有的服務的監控信息聚合在一起統一查看,就需要使用Turbine來聚合所需要的服務的監控信息。

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

    1、獨立部署一個網關應用

    2、服務註冊中心和監控可以配置在一個應用裏,也可以是2個應用。

    3、服務註冊中心也可以部署多個,通過區域zone來區分,來實現高可用。

    4、每個服務,根據負載和高可用的需要,部署一個或多個實例。

4 Spring Cloud Netflix組件開發

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

4.1 註冊和監控中心的開發

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

Java
// 省略import
@SpringBootApplication
@EnableEurekaServer
@EnableHystrixDashboard
public 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-server 和 spring-cloud-starter-hystrix-dashboard 等。

4.2 服務間調用

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

4.2.1 RestTemplate

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

Java
@Configuration
public class SomeCloudConfiguration {
    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

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

Java
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。還是直接看代碼:

4.2.2 FeignClient

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

Java
@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} 。 然後,在使用它的時候,就像注入一個一般的服務一樣注入後使用即可:

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

5 遇到的問題

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

5.1 請求超時問題

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

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

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

對應的配置如下:

Java
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 30000
ribbon:
    ReadTimeout: 30000
    ConnectTimeout: 15000

5.2 服務ID的問題

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

Java
spring:
  application:
      name: users

5.3 管理路徑的問題

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

5.3.1 註冊成功卻找不到服務

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

Java
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的版本存在。

5.3.2 設置了管理路徑的Hystrix監控

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

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

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

Java
turbine:
  aggregator:
      clusterConfig: USER
  appConfig: USER
  instanceUrlSuffix:
      USER: /user/hystrix.stream

5.3.3 管理路徑的安全性

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

5.4 服務間調用的權限驗證

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

Java
zuul:
 routes:
   users:
     path: /users/**
     sensitiveHeaders: Cookie,Set-Cookie,Authorization
     serviceId: user

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

Java
zuul.addProxyHeaders = false

如果你使用 RestTemplate 的方式調用,可以在請求裏面添加一個有header的 Options 。 也可以通過如下的攔截器的方式設置,它對 RestTemplate 方式和 FeignClient 的方式都可以起作用:

Java
@Bean
public RequestInterceptor requestInterceptor() {
    return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                String authToken
                     = getToken();
                template.header(AUTH_TOKEN_HEADER, authToken);
            }
        };
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章