使用 Consul 進行服務發現

服務發現是基於微服務架構的關鍵原則之一。嘗試手動設定每個用戶端或某種形式的慣例可能非常困難且非常脆弱。Consul 透過 HTTP APIDNS 提供服務發現服務。Spring Cloud Consul 利用 HTTP API 進行服務註冊和發現。這並不妨礙非 Spring Cloud 應用程式利用 DNS 介面。Consul Agent 伺服器在 叢集 中執行,該叢集透過 gossip 協議 進行通訊,並使用 Raft 共識協議

如何啟用

若要啟用 Consul 服務發現,請使用群組為 org.springframework.cloud 和 artifact id 為 spring-cloud-starter-consul-discovery 的 starter。請參閱 Spring Cloud 專案頁面,以取得使用目前 Spring Cloud Release Train 設定建置系統的詳細資訊。

向 Consul 註冊

當用戶端向 Consul 註冊時,它會提供關於自身的元數據,例如主機和端口、ID、名稱和標籤。預設會建立一個 HTTP 檢查,Consul 每 10 秒會對 /actuator/health 端點進行檢查。如果健康檢查失敗,則服務實例會被標記為嚴重。

Consul 用戶端範例

@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

(即完全正常的 Spring Boot 應用程式)。如果 Consul 用戶端位於 localhost:8500 以外的其他位置,則需要組態才能找到用戶端。範例

application.yml
spring:
  cloud:
    consul:
      host: localhost
      port: 8500
如果您使用 Spring Cloud Consul Config,並且您已設定 spring.cloud.bootstrap.enabled=truespring.config.use-legacy-processing=true 或使用 spring-cloud-starter-bootstrap,則上述值將需要放置在 bootstrap.yml 中,而不是 application.yml 中。

預設服務名稱、實例 ID 和端口,取自 Environment,分別為 ${spring.application.name}、Spring Context ID 和 ${server.port}

若要停用 Consul Discovery Client,您可以將 spring.cloud.consul.discovery.enabled 設定為 false。當 spring.cloud.discovery.enabled 設定為 false 時,Consul Discovery Client 也會被停用。

若要停用服務註冊,您可以將 spring.cloud.consul.discovery.register 設定為 false

將管理註冊為個別服務

當管理伺服器端口設定為與應用程式端口不同的值時,透過設定 management.server.port 屬性,管理服務將註冊為與應用程式服務不同的個別服務。例如

application.yml
spring:
  application:
    name: myApp
management:
  server:
    port: 4452

上述組態將註冊以下 2 個服務

  • 應用程式服務

ID: myApp
Name: myApp
  • 管理服務

ID: myApp-management
Name: myApp-management

管理服務將從應用程式服務繼承其 instanceIdserviceName。例如

application.yml
spring:
  application:
    name: myApp
management:
  server:
    port: 4452
spring:
  cloud:
    consul:
      discovery:
        instance-id: custom-service-id
        serviceName: myprefix-${spring.application.name}

上述組態將註冊以下 2 個服務

  • 應用程式服務

ID: custom-service-id
Name: myprefix-myApp
  • 管理服務

ID: custom-service-id-management
Name: myprefix-myApp-management

可以透過以下屬性進行進一步自訂

/** Port to register the management service under (defaults to management port) */
spring.cloud.consul.discovery.management-port

/** Suffix to use when registering management service (defaults to "management") */
spring.cloud.consul.discovery.management-suffix

/** Tags to use when registering management service (defaults to "management") */
spring.cloud.consul.discovery.management-tags

HTTP 健康檢查

Consul 實例的健康檢查預設為 "/actuator/health",這是 Spring Boot Actuator 應用程式中健康端點的預設位置。如果您使用非預設的 context path 或 servlet path (例如 server.servletPath=/foo) 或管理端點路徑 (例如 management.server.servlet.context-path=/admin),則即使對於 Actuator 應用程式,您也需要變更此設定。

Consul 用於檢查健康端點的間隔也可以設定。"10s" 和 "1m" 分別代表 10 秒和 1 分鐘。

此範例說明了上述情況 (請參閱 附錄頁面 中的 spring.cloud.consul.discovery.health-check-* 屬性以取得更多選項)。

application.yml
spring:
  cloud:
    consul:
      discovery:
        healthCheckPath: ${management.server.servlet.context-path}/actuator/health
        healthCheckInterval: 15s

您可以透過設定 spring.cloud.consul.discovery.register-health-check=false 完全停用 HTTP 健康檢查。

套用標頭

標頭可以套用至健康檢查請求。例如,如果您嘗試註冊使用 Vault BackendSpring Cloud Config 伺服器

application.yml
spring:
  cloud:
    consul:
      discovery:
        health-check-headers:
          X-Config-Token: 6442e58b-d1ea-182e-cfa5-cf9cddef0722

根據 HTTP 標準,每個標頭可以有多個值,在這種情況下,可以提供陣列

application.yml
spring:
  cloud:
    consul:
      discovery:
        health-check-headers:
          X-Config-Token:
            - "6442e58b-d1ea-182e-cfa5-cf9cddef0722"
            - "Some other value"

TTL 健康檢查

可以使用 Consul TTL 檢查 來代替預設設定的 HTTP 檢查。主要差異在於應用程式向 Consul Agent 發送心跳訊號,而不是 Consul Agent 向應用程式發送請求。

應用程式用於發送 ping 的間隔也可以設定。"10s" 和 "1m" 分別代表 10 秒和 1 分鐘。預設值為 30 秒。

此範例說明了上述情況 (請參閱 附錄頁面 中的 spring.cloud.consul.discovery.heartbeat.* 屬性以取得更多選項)。

application.yml
spring:
  cloud:
    consul:
      discovery:
        heartbeat:
          enabled: true
          ttl: 10s

TTL 應用程式狀態

對於 Spring Boot Actuator 應用程式,狀態由其可用的健康端點決定。當健康端點不可用時 (已停用或不是 Spring Boot Actuator 應用程式),它會假設應用程式處於良好狀態。

查詢健康端點時,預設使用根 健康群組。可以透過設定以下屬性來使用不同的健康群組

application.yml
spring:
  cloud:
    consul:
      discovery:
        heartbeat:
          actuator-health-group: <your-custom-group-goes-here>

您可以透過設定以下屬性完全停用健康端點的使用

application.yml
spring:
  cloud:
    consul:
      discovery:
        heartbeat:
          use-actuator-health: false
自訂 TTL 應用程式狀態

如果您想要組態自己的應用程式狀態機制,只需實作 ApplicationStatusProvider 介面

MyCustomApplicationStatusProvider.java
@Bean
public class MyCustomApplicationStatusProvider implements ApplicationStatusProvider {
	public CheckStatus currentStatus() {
        return yourMethodToDetermineAppStatusGoesHere();
    }
}

並使其在應用程式內容中可用

@Bean
public CustomApplicationStatusProvider customAppStatusProvider() {
     return new MyCustomApplicationStatusProvider();
}

Actuator 健康指示器

如果服務實例是 Spring Boot Actuator 應用程式,則可以提供以下 Actuator 健康指示器。

DiscoveryClientHealthIndicator

當 Consul 服務發現處於活動狀態時,會組態 DiscoverClientHealthIndicator 並使其在 Actuator 健康端點中可用。請參閱 此處 以取得組態選項。

ConsulHealthIndicator

組態了一個指示器,用於驗證 ConsulClient 的健康狀況。

預設情況下,它會檢索 Consul 領導節點狀態和所有已註冊的服務。在部署了許多已註冊服務的環境中,每次健康檢查都檢索所有服務可能成本很高。若要跳過服務檢索並僅檢查領導節點狀態,請設定 spring.cloud.consul.health-indicator.include-services-query=false

若要停用指示器,請設定 management.health.consul.enabled=false

當應用程式在 bootstrap context 模式 (預設) 下執行時,此指示器會載入到 bootstrap context 中,並且在 Actuator 健康端點中不可用。

元數據

Consul 支援服務上的元數據。Spring Cloud 的 ServiceInstance 具有 Map<String, String> metadata 欄位,該欄位從服務的 meta 欄位填充。若要填充 meta 欄位,請在 spring.cloud.consul.discovery.metadataspring.cloud.consul.discovery.management-metadata 屬性上設定值。

application.yml
spring:
  cloud:
    consul:
      discovery:
        metadata:
          myfield: myvalue
          anotherfield: anothervalue

上述組態將產生一個服務,其 meta 欄位包含 myfield→myvalueanotherfield→anothervalue

產生的元數據

Consul 自動註冊將自動產生一些條目。

表 1. 自動產生的元數據

'group'

屬性 spring.cloud.consul.discovery.instance-group。此值僅在 instance-group 不為空時產生'。

'secure'

如果屬性 spring.cloud.consul.discovery.scheme 等於 'https',則為 True,否則為 false。

屬性 spring.cloud.consul.discovery.default-zone-metadata-name,預設為 'zone'

屬性 spring.cloud.consul.discovery.instance-zone。此值僅在 instance-zone 不為空時產生'。

舊版本的 Spring Cloud Consul 透過剖析 spring.cloud.consul.discovery.tags 屬性來填充 Spring Cloud Commons 中的 ServiceInstance.getMetadata() 方法。這已不再支援,請遷移至使用 spring.cloud.consul.discovery.metadata map。

使 Consul 實例 ID 唯一

預設情況下,Consul 實例會以等於其 Spring Application Context ID 的 ID 註冊。預設情況下,Spring Application Context ID 為 ${spring.application.name}:comma,separated,profiles:${server.port}。在大多數情況下,這將允許一個服務的多個實例在一台機器上執行。如果需要進一步的唯一性,使用 Spring Cloud,您可以透過在 spring.cloud.consul.discovery.instanceId 中提供唯一識別碼來覆寫此設定。例如

application.yml
spring:
  cloud:
    consul:
      discovery:
        instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

使用此元數據,以及部署在 localhost 上的多個服務實例,隨機值將在那裡啟動,以使實例唯一。在 Cloudfoundry 中,vcap.application.instance_id 將在 Spring Boot 應用程式中自動填充,因此不需要隨機值。

查找服務

使用負載平衡器

Spring Cloud 支援 Feign (REST 用戶端建構器) 和 Spring RestTemplate,用於使用邏輯服務名稱/ID 而不是實體 URL 來查找服務。Feign 和可感知發現的 RestTemplate 都利用 Spring Cloud LoadBalancer 進行用戶端負載平衡。

如果您想要使用 RestTemplate 存取服務 STORES,只需宣告

@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
     return new RestTemplate();
}

並像這樣使用它 (請注意我們如何使用 Consul 中的 STORES 服務名稱/ID 而不是完整網域名稱)

@Autowired
RestTemplate restTemplate;

public String getFirstProduct() {
   return this.restTemplate.getForObject("https://STORES/products/1", String.class);
}

如果您在多個資料中心中擁有 Consul 叢集,並且想要存取另一個資料中心中的服務,則僅服務名稱/ID 不夠。在這種情況下,您可以使用屬性 spring.cloud.consul.discovery.datacenters.STORES=dc-west,其中 STORES 是服務名稱/ID,而 dc-west 是 STORES 服務所在的資料中心。

Spring Cloud 現在也提供對 Spring Cloud LoadBalancer 的支援。

使用 DiscoveryClient

您也可以使用 org.springframework.cloud.client.discovery.DiscoveryClient,它為不特定於 Netflix 的發現用戶端提供了一個簡單的 API,例如

@Autowired
private DiscoveryClient discoveryClient;

public String serviceUrl() {
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
        return list.get(0).getUri();
    }
    return null;
}

Consul Catalog Watch

Consul Catalog Watch 利用 Consul 監看服務 的能力。Catalog Watch 進行阻塞式 Consul HTTP API 呼叫,以判斷是否有任何服務發生變更。如果存在新的服務資料,則會發佈 Heartbeat Event。

若要變更呼叫 Config Watch 的頻率,請變更 spring.cloud.consul.config.discovery.catalog-services-watch-delay。預設值為 1000,單位為毫秒。延遲是前一次調用結束後到下一次調用開始之間的時間量。

若要停用 Catalog Watch,請設定 spring.cloud.consul.discovery.catalogServicesWatch.enabled=false

watch 使用 Spring TaskScheduler 來排程對 Consul 的呼叫。預設情況下,它是 ThreadPoolTaskScheduler,其 poolSize 為 1。若要變更 TaskScheduler,請建立一個類型為 TaskScheduler 的 bean,並以 ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME 常數命名。