Spring Cloud在Netflix後時代的走向?

banq發表於2019-04-10

如果有人問你關於Spring Cloud的問題,那麼你首先想到的可能是Netflix OSS的支援。對Eureka,Zuul或Ribbon等工具的支援不僅由Spring提供,還可基於其他流行框架Apache Camel,Vert.x或Micronaut等構建微服務架構。

目前,Spring Cloud Netflix是最受歡迎的專案,它是Spring Cloud的一部分。它在GitHub上有大約3.2k的星星,而第二個同類最好的大約有1.4k。

但是,Pivotal宣佈大部分Spring Cloud Netflix模組正在進入維護模式,這是非常令人驚訝的。您可以在Spencer GibbSpring部落格上釋出的帖子中閱讀更多相關內容。

讓我們簡要介紹一下這些變化。

從Spring Cloud Greenwich釋出列車開始,Netflix OSS,Archaius,Hystrix,Ribbon和Zuul正在進入維護模式。這意味著不會向這些模組新增任何新功能,Spring Cloud團隊只會執行一些錯誤修復並修復安全問題。維護模式不包括仍受支援的Eureka模組。

對這些變化的解釋非常簡單。主要是其中兩個原因:

目前,Netflix並未積極開發Ribbon和Hystrix,儘管它們仍在大規模部署。

此外,Hystrix已經被稱為Atlas的遙測新解決方案所取代。

Zuul的情況並不那麼明顯,Netflix於2018年5月宣佈Zuul 2的開源。新版Zuul閘道器建立在Netty伺服器之上,包括一些改進和新功能。您可以在Netflix部落格上閱讀有關它們的更多資訊。

儘管Netflix雲團隊做出了這一決定,但Spring Cloud團隊已經放棄了Zuul模組的開發。我只能猜測這是因為早先決定在Spring Cloud系列中啟動了一個專門用於API閘道器的Spring Cloud Gateway新模組。

最後一塊拼圖是Eureka:這是一個發現伺服器。它仍在開發中,但這裡的情況也很有趣。我將在本文的下一部分中對此進行描述。

所有這些訊息激勵我看一下Spring Cloud的現狀,並討論未來可能發生的一些變化。作為Mastering Spring Cloud的一本書的作者,我試圖跟隨該專案的發展以保持最新狀態。值得一提的是,我們的組織內部還有微服務 - 當然是使用Eureka,Zuul和Ribbon等模組構建在Spring Boot和Spring Cloud之上。在本文中,我想討論一些流行的微服務模式,如服務發現,分散式配置,客戶端負載平衡和API閘道器。

服務發現

Eureka是唯一尚未轉移到維護模式的重要Spring Cloud Netflix模組。但是,我不會說它是積極開發的。Netflix維護的儲存庫中的最後一次提交是從1月11日開始的。前段時間,他們已經開始研究Eureka 2,但似乎這項工作已被放棄,或者他們只是推遲了最新版本的開源。

在這裡您可以找到一個有趣的評論:“2.x分支目前被凍結,因為我們已經對eureka2進行了一些內部更改,並且沒有任何時間來開源新的變更。”所以,我們有兩種可能性。也許,Netflix將決定將這些內部更改作為Eureka伺服器的第2版開源。值得記住的是,Eureka是Netflix直接大規模使用的經過實戰驗證的解決方案,也可能是許多其他組織通過Spring Cloud實現驗證過的。

第二個選項是選擇另一個發現伺服器。目前,Spring Cloud支援基於各種工具的發現:ZooKeeper,Consul,Alibaba Nacos和Kubernetes。事實上,Kubernetes基於etcd。Spring Cloud也正在開發對etcd的支援,但它還處於孵化階段,目前還不知道它是否會被推廣到官方釋出列車。在我看來,這些解決方案中有一個領導者--HashiCorp的Consul。

Consul現在被描述為服務網格解決方案,提供具有服務發現,配置和分段功能的全功能控制平面。它可以用作基於微服務的體系結構中的發現伺服器或鍵/值儲存。與Consul的整合由Spring Cloud Consul專案實現。要為您的應用程式啟用Consul客戶端,您只需要在Maven中包含以下依賴項pom.xml:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

預設情況下,Spring嘗試在地址localhost:8500上與Consul連線。如果您需要覆蓋此地址,則應在其中設定適當的屬性application.yml:

spring:  
  cloud:
    consul:
      host: 192.168.99.100
      port: 8500

您可以使用作為Docker容器啟動的Consul的本地例項輕鬆測試此解決方案:

$ docker run -d --name consul -p 8500:8500 consul

正如您所看到的,使用Spring Cloud進行Consul發現實現非常簡單 - 就像Eureka一樣。Consul對Eureka有一個無可置疑的優勢 - 它由HashiCorp持續維護和開發。它的受歡迎程度快速增長。它是HashiCorp最大的生態系統的一部分,包括Vault,Nomad和Terraform。與Eureka相比,Consul不僅可以用於服務發現,還可以用作基於微服務的體系結構中的配置伺服器。

分散式配置

Netflix Archaius是一個有趣的解決方案,用於管理微服務架構中的外部化配置。雖然它提供了一些有趣的功能,如動態和型別屬性,或者支援動態資料來源,如URL,JDBC或AWS DynamoDB,但Spring Cloud也決定將其轉移到維護模式。然而,由於Pivotal團隊和社群 - Spring Cloud Config完全建立的類似專案的存在,Spring Cloud Archaius的受歡迎程度有限。

Spring Cloud Config支援多個源儲存庫,包括Git,JDBC,Vault或簡單檔案。您可以在我之前的帖子中找到許多使用此專案為您的微服務提供分散式配置的示例。今天,我不打算談論它。

正如我在上一節末尾提到的,Consul也可以用作配置伺服器。如果您使用Eureka作為發現伺服器,使用Spring Cloud Config作為配置伺服器是很自然的選擇,因為Eureka根本不提供此類功能。如果您決定使用Consul,情況就不是這樣。現在選擇兩種解決方案是有意義的:Spring Cloud Consul Config和Spring Cloud Config。

當然,它們都有其優點和缺點。例如,您可以使用Consul節點輕鬆構建叢集,而使用Spring Cloud Config則必須依賴外部發現。

現在,讓我們看看如何使用Spring Cloud Consul來管理應用程式中的外部配置。要在應用程式端啟用它,您只需要在Maven中包含以下依賴項pom.xml:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>

與服務發現相同,如果要覆蓋某些預設客戶端設定,則需要設定屬性spring.cloud.consul.*。但是,必須在bootstrap.yml內部提供這樣的配置。

spring:  
  application:
    name: callme-service
  cloud:
    consul:
      host: 192.168.99.100
      port: 8500

在Consul上建立的屬性源的名稱應該bootstrap.yml與config資料夾中提供的應用程式名稱相同。您應該建立server.port值為的值  0,以強制Spring Boot隨機生成偵聽埠號。如果需要設定應用程式的預設偵聽埠,則應使用以下配置: key 是config/callme-service/server.port 值是0。

API閘道器

Spring Cloud Netflix Zuul的繼任者是Spring Cloud Gateway。這個專案大約在兩年前開始,現在是第二個最受歡迎的Spring Cloud專案,在GitHub上有1.4k星。它提供了一個建立在Spring Ecosystem之上的API閘道器,包括:Spring 5,Spring Boot 2和Project Reactor。它在Netty上執行,不能與Tomcat或Jetty等傳統的servlet容器一起使用。它允許我們定義路由,謂詞和過濾器。

API閘道器就像每個Spring Cloud微服務一樣,可以輕鬆地與基於Consul的服務發現整合。我們只需要在裡面包含適當的依賴項pom.xml。我們將使用Spring Cloud庫的最新開發版本 - 2.2.0.BUILD-SNAPSHOT。這是所需依賴項的列表:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    <version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
    <version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>

Consul還將提供閘道器配置。因為我們有比樣本微服務更多的配置設定,我們將它儲存為YAML檔案。為此,我們應該在Consul的Key / Value的/config/gateway-service/data路徑下建立一個YAML檔案  。下面顯示的配置啟用服務發現整合並定義到下游服務的路由。每條路由都包含在服務發現中註冊的目標服務的名稱,用於下游服務公開的呼叫端點的匹配路徑和重寫路徑。我們的API閘道器在啟動時載入以下配置:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: caller-service
          uri: lb://caller-service
          predicates:
            - Path=/caller/**
          filters:
            - RewritePath=/caller/(?.*), /$\{path}
        - id: callme-service
          uri: lb://callme-service
          predicates:
            - Path=/callme/**
          filters:
            - RewritePath=/callme/(?.*), /$\{path}

最後一步是強制gateway-service讀取儲存為YAML的配置。為此,我們需要將屬性設定spring.cloud.consul.config.format為YAML。這是bootstrap.yml裡面提供的完整配置。

spring:
  application:
    name: gateway-service
  cloud:
    consul:
      host: 192.168.99.100
      config:
        format: YAML

客戶端負載均衡器

在2.2.0.BUILD-SNAPSHOTSpring Cloud Commons Ribbon 版本中,它仍然是HTTP客戶端的主要自動配置負載均衡器。儘管Spring Cloud團隊已宣佈Spring Cloud Load Balancer將成為Ribbon的繼任者,但我們目前在文件或網路上找不到有關該專案的更多資訊。我們可能期望Netflix Ribbon也是如此,即任何配置對我們來說都是透明的,特別是如果我們使用發現客戶端。目前,該spring-cloud-loadbalancer模組是Spring Cloud Commons專案的一部分。您可以通過宣告以下依賴項將其直接包含在您的應用程式中pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
    <version>2.2.0.BUILD-SNAPSHOT</version>
</dependency>

出於測試目的,排除一起包含spring-cloud-starter-consul-discovery 啟動器的一些Netflix模組是值得的  。現在,我們確信Ribbon不用作了後臺用作負載均衡器。這是我為示例應用程式設定的排除列表:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    <version>2.2.0.BUILD-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-archaius</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-httpclient</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-loadbalancer</artifactId>
        </exclusion>
    </exclusions>
</dependency>

首先,我們應該用我們的main或配置類註釋@LoadBalancerClient。與往常一樣,客戶端的名稱應與登錄檔中註冊的目標服務的名稱相同。註釋還應包含具有客戶端配置的類。

@SpringBootApplication
@LoadBalancerClients({
 @LoadBalancerClient(name = "callme-service", configuration = ClientConfiguration.class)
})
public class CallerApplication {
 public static void main(String[] args) {
  SpringApplication.run(CallerApplication.class, args);
 }
 @Bean
 RestTemplate template() {
  return new RestTemplate();
 }
}

這是我們的負載均衡器配置類。它包含單個宣告@Bean。我選擇了RoundRobinLoadBalancer型別。

public class ClientConfiguration {
 @Bean
 public RoundRobinLoadBalancer roundRobinContextLoadBalancer(LoadBalancerClientFactory clientFactory, Environment env) {
  String serviceId = clientFactory.getName(env);
  return new RoundRobinLoadBalancer(serviceId, clientFactory
   .getLazyProvider(serviceId, ServiceInstanceSupplier.class), -1);
 }
}

最後,這是caller-service控制器的實現。它LoadBalancerClientFactory直接用於查詢callme-service可用例項的列表。然後,它選擇單個例項,獲取其主機和埠,並將其設定為目標URL。

@RestController
@RequestMapping("/caller")
public class CallerController {
 @Autowired
 Environment environment;
 @Autowired
 RestTemplate template;
 @Autowired
 LoadBalancerClientFactory clientFactory;
 @GetMapping
 public String call() {
  RoundRobinLoadBalancer lb = clientFactory.getInstance("callme-service", RoundRobinLoadBalancer.class);
  ServiceInstance instance = lb.choose().block().getServer();
  String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/callme";
  String callmeResponse = template.getForObject(url, String.class);
  return "I'm Caller running on port " + environment.getProperty("local.server.port") +
   " calling-> " + callmeResponse;
 }

總結

下圖說明了樣本系統的架構:

我們有兩個例項callme-service,一個例項caller-service,它使用Spring Cloud Balancer查詢可用例項列表callme-service。埠是動態生成的。API閘道器從外部客戶端隱藏了我們系統的複雜性。它在埠8080上可用,並根據請求上下文路徑將請求轉發到下游。

Spring Cloud在Netflix後時代的走向?

啟動後,您應該在Consul節點上註冊所有微服務。

現在,您可以嘗試caller-service通過閘道器公開的端點:http:// localhost:8080 / caller。

示例應用程式原始碼可在儲存庫中GitHub上獲得

相關文章