第三部分。Spring Cloud Netflix
1.0.0.BUILD-快照
該項目通過自動配置和Spring環境以及其他Spring編程模型習慣用法爲Spring Boot應用程序提供Netflix OSS集成。通過一些簡單的註釋,您可以快速啓用和配置應用程序內的常見模式,並使用經過實戰考驗的Netflix組件構建大型分佈式系統。提供的模式包括服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul)和客戶端負載平衡(Ribbon)。
11.服務發現:Eureka客戶
Service Discovery是基於微服務的體系結構的關鍵原則之一。嘗試手動配置每個客戶端或某種形式的約定可能很難做到並且可能很脆弱。Eureka是Netflix服務發現服務器和客戶端。可以配置和部署服務器以使其具有高可用性,每個服務器將註冊服務的狀態複製到其他服務器。
11.1如何包含Eureka客戶端
要在項目中包含Eureka Client,請使用具有組ID org.springframework.cloud
和工件ID 的starter spring-cloud-starter-netflix-eureka-client
。有關使用當前Spring Cloud Release Train設置構建系統的詳細信息,請參閱Spring Cloud Project頁面。
11.2在Eureka註冊
當客戶向Eureka註冊時,它會提供有關自身的元數據 - 例如主機,端口,運行狀況指示器URL,主頁和其他詳細信息。Eureka從屬於服務的每個實例接收心跳消息。如果心跳故障超過可配置的時間表,則通常會從註冊表中刪除該實例。
以下示例顯示了最小的Eureka客戶端應用程序:
@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應用程序。通過spring-cloud-starter-netflix-eureka-client
在類路徑上,您的應用程序會自動向Eureka Server註冊。找到Eureka服務器需要進行配置,如以下示例所示:
application.yml。
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
在前面的示例中,“defaultZone”是一個魔術字符串後備值,它爲任何不表示首選項的客戶端提供服務URL(換句話說,它是一個有用的默認值)。
默認的應用程序名稱(即,服務ID),虛擬主機,和非安全端口(從所拍攝的Environment
)是${spring.application.name}
,${spring.application.name}
和${server.port}
分別。
在spring-cloud-starter-netflix-eureka-client
類路徑上使應用程序成爲Eureka “ 實例 ”(即,它自己註冊)和“ 客戶端 ”(它可以查詢註冊表以查找其他服務)。實例行爲由eureka.instance.*
配置鍵驅動,但如果您確保應用程序具有值spring.application.name
(這是Eureka服務ID或VIP 的默認值),則默認值很好。
有關可配置選項的更多詳細信息,請參閱EurekaInstanceConfigBean和EurekaClientConfigBean。
要禁用Eureka Discovery Client,您可以設置eureka.client.enabled
爲false
。當尤里卡發現客戶端也將被禁止spring.cloud.discovery.enabled
設置爲false
。
11.3使用Eureka Server進行身份驗證
如果其中一個eureka.client.serviceUrl.defaultZone
URL中嵌入了憑據,則會自動將HTTP基本身份驗證添加到您的eureka客戶端(捲曲樣式,如下所示:) http://user:password@localhost:8761/eureka
。對於更復雜的需求,您可以創建一個@Bean
類型DiscoveryClientOptionalArgs
並將ClientFilter
實例注入其中,所有這些實例都應用於從客戶端到服務器的調用。
由於Eureka的限制,無法支持每服務器基本身份驗證憑據,因此僅使用找到的第一個集合。 |
11.4狀態頁面和健康指標
Eureka實例的狀態頁面和運行狀況指示器分別默認爲/info
和/health
Spring Boot Actuator應用程序中有用端點的默認位置。如果使用非默認上下文路徑或servlet路徑(例如server.servletPath=/custom
),則需要更改這些,即使對於Actuator應用程序也是如此。以下示例顯示了兩個設置的默認值:
application.yml。
eureka:
instance:
statusPageUrlPath: ${server.servletPath}/info
healthCheckUrlPath: ${server.servletPath}/health
這些鏈接顯示在客戶端使用的元數據中,並在某些情況下用於決定是否向您的應用程序發送請求,因此如果它們準確,則會很有幫助。
在Dalston中,還需要在更改管理上下文路徑時設置狀態和運行狀況檢查URL。從Edgware開始刪除此要求。 |
11.5註冊安全應用程序
如果您希望通過HTTPS聯繫您的應用,則可以在以下位置設置兩個標誌EurekaInstanceConfig
:
eureka.instance.[nonSecurePortEnabled]=[false]
eureka.instance.[securePortEnabled]=[true]
這樣做會使Eureka發佈實例信息,顯示對安全通信的明確偏好。對於以這種方式配置的服務,Spring Cloud DiscoveryClient
始終返回URI https
。同樣,當以這種方式配置服務時,Eureka(本機)實例信息具有安全的運行狀況檢查URL。
由於Eureka在內部工作的方式,它仍然會發布狀態和主頁的非安全URL,除非您也明確地覆蓋這些URL。您可以使用佔位符來配置eureka實例URL,如以下示例所示:
application.yml。
eureka:
instance:
statusPageUrl: https://${eureka.hostname}/info
healthCheckUrl: https://${eureka.hostname}/health
homePageUrl: https://${eureka.hostname}/
(請注意,這${eureka.hostname}
是一個僅在Eureka的更高版本中可用的本機佔位符。您也可以使用Spring佔位符實現相同的功能 - 例如,通過使用${eureka.instance.hostName}
。)
如果您的應用程序在代理後面運行,並且SSL終止在代理中(例如,如果您在Cloud Foundry或其他平臺中作爲服務運行),那麼您需要確保代理“ 轉發 ”標頭被截獲和處理通過申請。如果嵌入在Spring Boot應用程序中的Tomcat容器具有“X-Forwarded - \ *”標頭的顯式配置,則會自動發生這種情況。您的應用程序呈現給自己錯誤的鏈接(錯誤的主機,端口或協議)表明您的配置錯誤。 |
11.6 Eureka的健康檢查
默認情況下,Eureka使用客戶端心跳來確定客戶端是否已啓動。除非另有說明,否則Discovery Client不會根據Spring Boot Actuator傳播應用程序的當前運行狀況檢查狀態。因此,在成功註冊後,Eureka始終宣佈應用程序處於“UP”狀態。通過啓用Eureka運行狀況檢查可以更改此行爲,從而將應用程序狀態傳播到Eureka。因此,每個其他應用程序都不會向“UP”以外的狀態下的應用程序發送流量。以下示例顯示如何爲客戶端啓用運行狀況檢查:
application.yml。
eureka:
client:
healthcheck:
enabled: true
|
如果您需要更多控制健康檢查,請考慮實施自己的健康檢查com.netflix.appinfo.HealthCheckHandler
。
11.7實例和客戶端的Eureka元數據
值得花一點時間瞭解Eureka元數據的工作原理,因此您可以在平臺中使用它。存在標準元數據,用於提供主機名,IP地址,端口號,狀態頁和運行狀況檢查等信息。這些發佈在服務註冊表中,客戶端使用它們以直接的方式聯繫服務。可以將附加元數據添加到實例註冊中eureka.instance.metadataMap
,並且可以在遠程客戶端中訪問此元數據。通常,除非客戶端了解元數據的含義,否則其他元數據不會更改客戶端的行爲。本文稍後將介紹幾種特殊情況,其中Spring Cloud已經爲元數據映射賦予了意義。
11.7.1在Cloud Foundry上使用Eureka
Cloud Foundry有一個全局路由器,因此同一個應用程序的所有實例都具有相同的主機名(具有類似架構的其他PaaS解決方案具有相同的安排)。這不一定是使用Eureka的障礙。但是,如果您使用路由器(建議甚至強制使用,具體取決於您的平臺的設置方式),您需要明確設置主機名和端口號(安全或非安全),以便他們使用路由器。您可能還希望使用實例元數據,以便區分客戶端上的實例(例如,在自定義負載平衡器中)。默認情況下,eureka.instance.instanceId
is vcap.application.instance_id
,如以下示例所示:
application.yml。
eureka:
instance:
hostname: ${vcap.application.uris[0]}
nonSecurePort: 80
根據在Cloud Foundry實例中設置安全規則的方式,您可以註冊並使用主機VM的IP地址進行直接的服務到服務調用。Pivotal Web Services(PWS)尚未提供此功能。
11.7.2在AWS上使用Eureka
如果計劃將應用程序部署到AWS雲,則必須將Eureka實例配置爲支持AWS。您可以通過自定義EurekaInstanceConfigBean來執行此操作,如下所示:
@Bean
@Profile("!default")
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
EurekaInstanceConfigBean b = new EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
b.setDataCenterInfo(info);
return b;
}
11.7.3更改Eureka實例ID
一個vanilla Netflix Eureka實例註冊的ID等於其主機名(即每個主機只有一個服務)。Spring Cloud Eureka提供合理的默認值,定義如下:
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}
一個例子是myhost:myappname:8080
。
通過使用Spring Cloud,您可以通過提供唯一標識符來覆蓋此值eureka.instance.instanceId
,如以下示例所示:
application.yml。
eureka:
instance:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
使用前面示例中顯示的元數據和部署在localhost上的多個服務實例,將隨機值插入其中以使實例唯一。在Cloud Foundry中,vcap.application.instance_id
會自動在Spring Boot應用程序中填充,因此不需要隨機值。
11.8使用EurekaClient
一旦您擁有作爲發現客戶端的應用程序,就可以使用它從Eureka Server發現服務實例。一種方法是使用本機com.netflix.discovery.EurekaClient
(而不是Spring Cloud DiscoveryClient
),如以下示例所示:
@Autowired
private EurekaClient discoveryClient;
public String serviceUrl() {
InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
}
不要 |
11.8.1沒有Jersey的EurekaClient
默認情況下,EurekaClient使用Jersey進行HTTP通信。如果您希望避免來自Jersey的依賴項,則可以將其從依賴項中排除。Spring Cloud基於Spring自動配置傳輸客戶端RestTemplate
。以下示例顯示Jersey被排除在外:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</exclusion>
</exclusions>
</dependency>
11.9 Native Netflix EurekaClient的替代方案
您無需使用原始Netflix EurekaClient
。此外,在某種包裝後面使用它通常更方便。Spring Cloud 通過邏輯Eureka服務標識符(VIP)而不是物理URL 支持Feign(REST客戶端構建器)和SpringRestTemplate
。要使用固定的物理服務器列表配置功能區,可以設置<client>.ribbon.listOfServers
以逗號分隔的物理地址(或主機名)列表,其中<client>
是客戶端的ID。
您還可以使用org.springframework.cloud.client.discovery.DiscoveryClient
,爲發現客戶端提供簡單的API(不特定於Netflix),如以下示例所示:
@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;
}
11.10爲什麼註冊服務這麼慢?
作爲實例還涉及到註冊表(通過客戶端serviceUrl
)的定期心跳,默認持續時間爲30秒。在實例,服務器和客戶端在其本地緩存中都具有相同的元數據之前,客戶端無法發現服務(因此可能需要3次心跳)。您可以通過設置更改期間eureka.instance.leaseRenewalIntervalInSeconds
。將其設置爲小於30的值會加快使客戶端連接到其他服務的過程。在生產中,最好堅持使用默認值,因爲服務器中的內部計算會對租賃續訂期做出假設。
11.11區域
如果已將Eureka客戶端部署到多個區域,則可能希望這些客戶端在嘗試其他區域中的服務之前使用同一區域內的服務。要進行此設置,您需要正確配置Eureka客戶端。
首先,您需要確保將Eureka服務器部署到每個區域,並確保它們彼此對等。有關 詳細信息,請參閱有關區域和區域的部分。
接下來,您需要告訴Eureka您的服務所在的區域。您可以使用該metadataMap
屬性來執行此操作。例如,如果service 1
部署到兩zone 1
和zone 2
,你需要設置以下尤里卡特性service 1
:
1區的服務1
eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true
2區服務1
eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true
11.12刷新Eureka Client
默認情況下,EurekaClient
bean是可刷新的,這意味着可以更改和刷新Eureka客戶端屬性。當刷新發生時,客戶端將從Eureka服務器取消註冊,並且可能存在短暫的時間,其中給定服務的所有實例都不可用。消除這種情況的一種方法是禁用刷新Eureka客戶端的能力。要做這個設置eureka.client.refresh.enable=false
。
12.服務發現:Eureka Server
本節介紹如何設置Eureka服務器。
12.1如何包含Eureka服務器
要在項目中包含Eureka Server,請使用組ID爲ID org.springframework.cloud
且工件ID爲的starter spring-cloud-starter-netflix-eureka-server
。有關使用當前Spring Cloud Release Train設置構建系統的詳細信息,請參閱Spring Cloud Project頁面。
如果您的項目已使用Thymeleaf作爲其模板引擎,則可能無法正確加載Eureka服務器的Freemarker模板。在這種情況下,有必要手動配置模板加載器: |
application.yml。
spring:
freemarker:
template-loader-path: classpath:/templates/
prefer-file-system-access: false
12.2如何運行Eureka服務器
以下示例顯示了最小的Eureka服務器:
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
服務器有一個主頁,其中包含用於正常Eureka功能的UI和HTTP API端點/eureka/*
。
以下鏈接有一些Eureka背景閱讀:磁通電容和谷歌小組討論。
由於Gradle的依賴關係解析規則和缺少父bom功能,取決於 的build.gradle。
|
12.3高可用性,區域和區域
Eureka服務器沒有後端存儲,但註冊表中的服務實例都必須發送心跳以使其註冊保持最新(因此可以在內存中完成)。客戶端還有一個Eureka註冊的內存緩存(因此,他們不必爲服務的每個請求轉到註冊表)。
默認情況下,每個Eureka服務器也是Eureka客戶端,並且需要(至少一個)服務URL來定位對等體。如果您不提供該服務,該服務將運行並正常運行,但它會在您的日誌中填充很多關於無法向對等方註冊的噪音。
有關區域和區域的客戶端功能區支持的詳細信息,請參見下文。
12.4獨立模式
兩個緩存(客戶端和服務器)和心跳的組合使得獨立的Eureka服務器能夠非常適應故障,只要有某種監視器或彈性運行時(例如Cloud Foundry)使其保持活動狀態即可。在獨立模式下,您可能更願意關閉客戶端行爲,以便它不會繼續嘗試並且無法訪問其對等方。以下示例顯示如何關閉客戶端行爲:
application.yml(獨立Eureka服務器)。
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
請注意,serviceUrl
它指向與本地實例相同的主機。
12.5同伴意識
通過運行多個實例並要求它們相互註冊,可以使Eureka更具彈性和可用性。實際上,這是默認行爲,因此要使其工作所需要做的就是serviceUrl
向對等方添加有效內容,如以下示例所示:
application.yml(兩個Peer Aware Eureka服務器)。
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2/eureka/
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1/eureka/
在前面的示例中,我們有一個YAML文件,可以用於在兩個主機(peer1
和peer2
)上運行相同的服務器,方法是在不同的Spring配置文件中運行它。您可以使用此配置通過操作/etc/hosts
來解析主機名來測試單個主機上的對等感知(在生產中執行此操作沒有太大價值)。實際上,eureka.instance.hostname
如果您在知道自己的主機名的計算機上運行,則不需要它(默認情況下,通過使用查找它java.net.InetAddress
)。
您可以將多個對等體添加到系統中,並且只要它們通過至少一個邊緣彼此連接,它們就會在它們之間同步註冊。如果對等體在物理上是分開的(在數據中心內或在多個數據中心之間),那麼系統原則上可以存在“ 裂腦 ”類型的故障。您可以向系統添加多個對等體,只要它們彼此直接連接,它們就會在它們之間同步註冊。
application.yml(Three Peer Aware Eureka Servers)。
eureka:
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
12.6何時優先選擇IP地址
在某些情況下,Eureka最好宣傳服務的IP地址而不是主機名。設置eureka.instance.preferIpAddress
爲true
,當應用程序向eureka註冊時,它使用其IP地址而不是其主機名。
如果Java無法確定主機名,則將IP地址發送給Eureka。只有設置主機名的明確方法是設置 |
12.7保護Eureka服務器
只需將Spring Security添加到服務器的類路徑中即可保護您的Eureka服務器spring-boot-starter-security
。默認情況下,當Spring Security位於類路徑上時,它將要求在應用程序的每個請求中發送有效的CSRF令牌。Eureka客戶端通常不會擁有有效的跨站點請求僞造(CSRF)令牌,您需要爲/eureka/**
端點禁用此要求。例如:
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
有關CSRF的更多信息,請參閱Spring Security文檔。
可以在Spring Cloud Samples 回購中找到演示Eureka Server 。
12.8 JDK 11支持
在JDK 11中刪除了Eureka服務器所依賴的JAXB模塊。如果您打算在運行Eureka服務器時使用JDK 11,則必須在POM或Gradle文件中包含這些依賴項。
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>