是官方文檔的總結
http://spring.io/projects/spring-cloud-netflix#overview
講解基于2.0.2版本官方文檔
https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.0.2.RELEASE/single/spring-cloud-netflix.html
Netflix提供了以下功能:
- 服務發現:可以注冊Eureka實例,客戶端可以使用Spring的beans來發現這些實例
- 服務發現:可以通過聲明式Java配置來創建一個嵌入式Eureka服務器
- 斷路器:可以通過簡單的注解修飾方法來創建一個斷路器客戶端
- 斷路器:使用聲明式Java配置可以嵌入一個Hystrix儀表盤
- 客戶端負載均衡器:Ribbon
- 外部配置:從Spring環境到Archaius的橋梁(允許使用SpringBoot約定對Netflix組件進行本地配置)
- 路由器和過濾器:Zuul過濾器的自動重新配置,和用于反向創建代理的配置方法的簡單約定。
?
一、服務發現:Eureka客戶端
服務發現是基于微服務的體系結構的核心原則之一。嘗試手工配置每個客戶端或某種形式的約定可能很困難,而且很脆弱。Eureka是Netflix服務發現服務器和客戶端。可以將服務器配置和部署為高度可用,每個服務器都將注冊服務的狀態復制給其他服務器。
1.1、包含依賴
group ID:org.springframework.cloud
artifact ID:spring-cloud-starter-netflix-eureka-client
具體版本可以查看http://spring.io/projects/spring-cloud,查看對應Spring?Cloud版本匹配的Eureka客戶端版本
1.2、注冊到Eureka中
當客戶端向Eureka注冊時,它會提供關于自己的元數據,例如主機、端口、健康指示符URL、主頁和其他詳細信息。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應用,只要classpath中包含了spring-cloud-starter-netflix-eureka-client,應用就會自動注冊到Eureka服務器
要求通過配置來指向Eureka服務器
eureka:client:serviceUrl:defaultZone: http://localhost:8761/eureka/
?
defaultZone:當客戶端沒有明確指向一個Eureka服務器時,會采用這個默認URL。
默認應用程序名稱(即服務ID)、虛擬主機和非安全端口(從環境獲取)分別為${Spring.application.name}、${Spring.application.name}和${server.port}。
當classpath中包含了spring-cloud-starter-netflix-eureka-client時,使這個應用既是一個Eureka實例(把自己注冊到Eureka服務器中),也是一個Eureka客戶端(他可以查詢Eureka服務器定位其它服務)。
實例行為可以通過eureka.instance.*的屬性來配置,也可以通過設置spring.application.name(作為Eureka服務的ID或VIP)并作為實例行為的默認值。
更多配置屬性的信息,查看這2個Bean:EurekaInstanceConfigBean、EurekaClientConfigBean
https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBean.java
https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientConfigBean.java
禁止使用Eureka客戶端,可以設置eureka.client.enabled為false
1.3、通過Eureka服務器的驗證
在eureka.client.serviceUrl.defaultZone的路徑中嵌入一個憑證(http://user:password@localhost:8761/eureka),會自動為你提供一個HTTP?basic認證。
如果需要更加復雜的需求,可以創建一個DiscoveryClientOptionalArgs的Bean,并為他注入一個ClientFilter實例,用于客戶端請求服務器時使用。
由于Eureka中的限制,不可能支持每個服務器的basic認證憑據,因此只使用了找到的第一組。
1.4、狀態頁面和健康指標
默認路徑是Spring?Boot中Actuator下的/info和/health地址,
如果你使用非默認上下文路徑或非默認servlet路徑,則需要修改他們。
eureka:instance:statusPageUrlPath: ${server.servletPath}/infohealthCheckUrlPath: ${server.servletPath}/health
?
在Dalston版本中,還要求在更改管理上下文路徑時設置狀態和健康檢查URL。從Edgware開始,這一要求就被刪除了。
1.5、注冊一個安全的應用
如果應用需要通過HTTPS傳輸,可以設置2個屬性
eureka.instance.[nonSecurePortEnabled]=[false]
eureka.instance.[securePortEnabled]=[true]
設置了之后,Eureka客戶端會返回https的地址。Eureka的實例信息也會有一個安全的健康檢查URL
由于Eureka的設計,status頁、home頁并不會因為這個設置變成安全鏈接,需要手動更改。
eureka:instance:statusPageUrl: https://${eureka.hostname}/infohealthCheckUrl: https://${eureka.hostname}/healthhomePageUrl: https://${eureka.hostname}/
?${eureka.hostname}是一個本地占位符,最新版本的Eureka才支持;也可以使用Spring占位符${eureka.instance.hostName}
If your application runs behind a proxy, and the SSL termination is in the proxy (for example, if you run in Cloud Foundry or other platforms as a service), then you need to ensure that the proxy “forwarded” headers are intercepted and handled by the application. If the Tomcat container embedded in a Spring Boot application has explicit configuration for the 'X-Forwarded-\*` headers, this happens automatically. The links rendered by your app to itself being wrong (the wrong host, port, or protocol) is a sign that you got this configuration wrong.
?
1.6、Eureka健康檢查
默認情況下,Eureka使用心跳機制決定一個客戶端是否在線。
除非特殊設置,Eureka客戶端不會通過Actuator來傳遞應用的健康信息。
可以開啟Eureka健康檢查,
。。待補充
?
1.7、Eureka實例和客戶端的元數據
使用eureka.instance.metadataMap可以添加元數據,這些元數據可以在遠程客戶端中獲取。
一般元數據不會改變客戶端的行為,除了那些客戶端能識別到這些元數據的含義,有些元數據Spring?Cloud已經在使用。
這個是用于部署在云平臺,然后可以獲取平臺的一些數據?
?
1.8、使用Eureka客戶端
Eureka客戶端,可以通過Eureka服務器來發現其他服務的實例。
可以使用com.netflix.discovery.EurekaClient
@Autowired private EurekaClient discoveryClient;public String serviceUrl() {InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);return instance.getHomePageUrl(); }
?
Do not use the?EurekaClient
?in a?@PostConstruct
?method or in a?@Scheduled
method (or anywhere where the?ApplicationContext
?might not be started yet). It is initialized in a?SmartLifecycle
?(with?phase=0
), so the earliest you can rely on it being available is in another?SmartLifecycle
?with a higher phase.
1.8.1、禁用Jersey
默認情況下Eureka客戶端使用Jersey進行HTTP通信。
可以通過排除依賴禁用他,然后Spring?Cloud會通過Spring?RestTemplate來進行HTTP通信。
<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>
?
1.9、本地Netflix Eureka客戶端的替代方案
一般不會使用上面的EurekaClient來獲取真正的物理地址。
Spring?Cloud提供了一個Feign和Spring?RestTemplate,可以通過Eureka服務的識別碼(如服務ID或VIP)來訪問服務,而不是通過物理地址。
如果要用一個固定服務器物理地址列表來配置Ribbon,可以通過設置<client>.ribbon.listOfServers,使用一個逗號分隔的物理地址(或hostnames),<client>是客戶端的ID
也可以使用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; }
?
1.10、為什么注冊一個服務這么慢?
Being an instance also involves a periodic heartbeat to the registry (through the client’s?serviceUrl
) with a default duration of 30 seconds. A service is not available for discovery by clients until the instance, the server, and the client all have the same metadata in their local cache (so it could take 3 heartbeats). You can change the period by setting?eureka.instance.leaseRenewalIntervalInSeconds
. Setting it to a value of less than 30 speeds up the process of getting clients connected to other services. In production, it is probably better to stick with the default, because of internal computations in the server that make assumptions about the lease renewal period.
作為一個實例,還涉及到注冊表的周期性心跳(通過客戶端的serviceUrl),默認持續時間為30秒。直到實例、服務器和客戶端在其本地緩存中都有相同的元數據(因此可能需要3次心跳),客戶端才能發現服務。可以通過設置eureka.instance.leaseRenewalIntervalInSeconds.更改期間將其設置為小于30的值可以加快將客戶端連接到其他服務的過程。在生產中,可能最好還是堅持默認的做法,因為服務器中的內部計算會對租約續訂期做出假設。
?
1.11、分區
如果你的Eureka客戶端分開了幾個區域,并且希望調用服務時,先在相同區域調用服務,然后才調用其它區域的服務。
這就需要進行正確的配置。
首先,要吧Eureka服務器部署到每一個區域,并且他們之間是對等服務器
然后,你需要告訴Eureka服務器,你的服務是在哪一個區域,可以通過修改metadataMap屬性來實現
//Service 1 in Zone 1 eureka.instance.metadataMap.zone = zone1 eureka.client.preferSameZoneEureka = true //Service 1 in Zone 2 eureka.instance.metadataMap.zone = zone2 eureka.client.preferSameZoneEureka = true
?
二、Eureka服務器
2.1、如何包含Eureka服務器
group ID:org.springframework.cloud
artifact ID:spring-cloud-starter-netflix-eureka-server
具體版本可以查看http://spring.io/projects/spring-cloud,查看對應Spring?Cloud版本匹配的Eureka客戶端版本
2.2、運行一個Eureka服務器
@SpringBootApplication @EnableEurekaServer public class Application {public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(true).run(args);}}
?
服務器有一個首頁和一些HTTP API端點在/eureka/*下
有幾個Eureka的討論網站?
https://github.com/cfregly/fluxcapacitor/wiki/NetflixOSS-FAQ#eureka-service-discovery-load-balancer
https://groups.google.com/forum/?fromgroups#!topic/eureka_netflix/g3p2r7gHnN0
Due to Gradle’s dependency resolution rules and the lack of a parent bom feature, depending on?spring-cloud-starter-netflix-eureka-server
?can cause failures on application startup. To remedy this issue, add the Spring Boot Gradle plugin and import the Spring cloud starter parent bom as follows:
build.gradle.?
buildscript {dependencies {classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")}
}apply plugin: "spring-boot"dependencyManagement {imports {mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"}
}
?
2.3、高可用,分區與區域(High Availability, Zones and Regions)
Eureka沒有后端存儲,但是服務實例必須保持發送心跳消息來保持他們的注冊是最新的(在內存中實現)。
客戶端也有一個對Eureka注冊服務的內存緩存,這使得不用每一次調用服務,都去查找一次注冊的服務。
默認情況下,每個Eureka服務器也是Eureka客戶端,需要(至少一個)服務URL來定位對等服務器。如果你不提供,服務也能運行,只是在日志中會增加大量無法向服務器注冊的信息。
?
2.4、單例模式
通過2個緩存(服務器、客戶端)以及心跳機制,使得只要提供一些監控或者運行時彈性(如Cloud?Foundry)保持一個Eureka服務器持續運行,他就有很強的抗錯誤彈性。
在單例模式下,你可以考慮關閉掉客戶端行為,就不會一直嘗試注冊到不存在的服務器路徑失敗
關閉掉客戶端
server:port: 8761eureka:instance:hostname: localhostclient:registerWithEureka: falsefetchRegistry: falseserviceUrl:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
?
注意defaultZone指向本地Eureka實例
?
2.5、同伴意識
Eureka可以通過運行多個實例,并互相注冊,來實現更高可用高彈性。
實際上,這是一個默認行為,只需要指定一個可用的serviceUrl給同伴即可。
---
spring:profiles: peer1
eureka:instance:hostname: peer1client:serviceUrl:defaultZone: http://peer2/eureka/---
spring:profiles: peer2
eureka:instance:hostname: peer2client:serviceUrl:defaultZone: http://peer1/eureka/
在前面的示例中,我們有一個YAML文件,通過在不同的Spring profiles中運行該文件,可以在兩個主機(peer 1和peer 2)上運行相同的服務器。您可以使用此配置來通過操作/etc/host來解析主機名來測試單個主機上的對等感知(在生產中這樣做沒有多大價值)。實際上,如果在知道自己主機名的機器上運行(默認情況下,使用java.net.InetAddress查找),則不需要eureka.instance.hostname。
你可以添加多個同伴到一個系統中,只要他們互相通過至少一個連接而相連,他們之間的注冊實例就會互相同步。如果這些同伴是物理隔離的,那么這個系統就有能力抵抗腦裂(split-brain)失敗。
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
?
2.6、什么時候使用IP地址
在某些情況下,Eureka最好是公布服務的IP地址,而不是主機名。
設置eureka.instance.preferIpAddress為true,程序注冊到Eureka中的時候,會使用IP地址二不是hostname
如果主機名不能由Java確定,則將IP地址發送給Eureka。惟一明確設置主機名的方法是設置eureka.instance.hostname屬性。可以通過使用環境變量(例如eureka.instance.hostname=${host_name})在運行時設置主機名。
2.7、保護Eureka服務器
您只需通過spring-boot-starter-security將Spring Security添加到服務器的類路徑中,就可以保護您的Eureka服務器。默認情況下,當Spring Security在類路徑上時,它將要求在向應用程序的每個請求中發送一個有效的CSRF令牌。Eureka客戶端通常不會擁有有效的跨站點請求偽造(CSRF)令牌,您需要對/eureka/*端點禁用此要求。例如:
@EnableWebSecurity class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } }
?