服務發現是基於微服務架構的關鍵原理之一,嘗試手動配置每一個客戶端或某種形式的約定可能很難作到而且可能很脆弱,Eureka是Netflix Service Discovery服務器和客戶端,服務器能夠被配置和部署爲高可用性,每一個服務器將註冊服務的狀態複製到其餘服務器。java
要在項目中包含Eureka Client,請使用組ID爲org.springframework.cloud
和工件ID爲spring-cloud-starter-netflix-eureka-client
的啓動器。git
當客戶端向Eureka註冊時,它會提供有關自身的元數據 — 例如主機、端口、健康指示器URL、主頁和其餘詳細信息,Eureka從屬於服務的每一個實例接收心跳消息,若是心跳故障超過可配置的時間表,則一般會從註冊表中刪除該實例。github
如下示例顯示了最小的Eureka客戶端應用程序:web
@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服務器須要進行配置,如如下示例所示:spring
application.ymlapache
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
在前面的示例中,「defaultZone」是一個神奇的字符串回退值,它爲任何不表示首選項的客戶端提供服務URL(換句話說,它是一個有用的默認值)。bootstrap
默認應用程序名稱(即服務ID)、虛擬主機和非安全端口(取自Environment
)分別是${spring.application.name}
、${spring.application.name}
和${server.port}
。segmentfault
在類路徑上使用spring-cloud-starter-netflix-eureka-client
使應用程序成爲Eureka「實例」(即,它本身註冊)和「客戶端」(它能夠查詢註冊表以查找其餘服務),實例行爲由eureka.instance.*
配置鍵驅動,但若是你確保應用程序具備spring.application.name
的值(這是Eureka服務ID或VIP的默認值),則默認值很好。緩存
有關可配置選項的更多詳細信息,請參閱EurekaInstanceConfigBean和EurekaClientConfigBean。安全
要禁用Eureka Discovery Client,能夠將eureka.client.enabled
設置爲false
。
若是其中一個eureka.client.serviceUrl.defaultZone
URL中嵌入了憑據,則會自動將HTTP基自己份驗證添加到你的eureka客戶端(curl樣式,以下所示:http://user:password@localhost:8761/eureka
)。對於更復雜的需求,你能夠建立一個類型爲DiscoveryClientOptionalArgs
的@Bean
並將ClientFilter
實例注入其中,全部這些都應用於從客戶端到服務器的調用。
因爲Eureka的限制,沒法支持每一個服務器基自己份驗證憑據,所以僅使用找到的第一個集合。
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開始刪除此要求。
若是你的應用程序想經過HTTPS聯繫,你能夠在EurekaInstanceConfig
中設置兩個標誌:
eureka.instance.[nonSecurePortEnabled]=[false]
eureka.instance.[securePortEnabled]=[true]
這樣作會使Eureka發佈顯示對安全通訊明確偏好的實例信息,對於以這種方式配置的服務,Spring Cloud DiscoveryClient
始終返回以https
開頭的URI,一樣,當以這種方式配置服務時,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或其餘平臺中做爲服務運行),而後,你須要確保代理「轉發」 headers被應用程序攔截和處理。若是嵌入在Spring Boot應用程序中的Tomcat容器具備針對
X-Forwarded-\*
headers的顯式配置,則會自動發生,應用程序呈現到自身的連接錯誤(錯誤的主機、端口或協議)代表你的配置錯誤。
默認狀況下,Eureka使用客戶端心跳來肯定客戶端是否已啓動,除非另有說明,不然Discovery Client不會根據Spring Boot Actuator傳播應用程序的當前健康檢查狀態,所以,在成功註冊後,Eureka始終宣佈應用程序處於「UP」狀態,經過啓用Eureka健康檢查能夠更改此行爲,從而將應用程序狀態傳播到Eureka。所以,每一個其餘應用程序都不會向「UP」之外的狀態下的應用程序發送流量,如下示例顯示如何爲客戶端啓用健康檢查:
application.yml
eureka: client: healthcheck: enabled: true
eureka.client.healthcheck.enabled=true
應該只在application.yml
中設置,在bootstrap.yml
中設置值會致使不良反作用,例如在Eureka中以UNKNOWN
狀態註冊。
若是你須要更多控制健康檢查,請考慮實現本身的com.netflix.appinfo.HealthCheckHandler
。
值得花一些時間瞭解Eureka元數據的工做原理,所以你能夠在平臺中使用它,有用於信息的標準元數據,如主機名、IP地址、端口號、狀態頁和健康檢查。這些發佈在服務註冊表中,客戶端使用它們以直接的方式聯繫服務,能夠將額外元數據添加到eureka.instance.metadataMap
中的實例註冊中,而且能夠在遠程客戶端中訪問此元數據。一般,除非客戶端了解元數據的含義,不然額外元數據不會更改客戶端的行爲,本文稍後將介紹幾種特殊狀況,其中Spring Cloud已經爲元數據映射賦予了意義。
Cloud Foundry有一個全局路由器,所以同一個應用程序的全部實例都具備相同的主機名(具備相似架構的其餘PaaS解決方案),這不必定是使用Eureka的障礙。可是,若是你使用路由器(建議甚至強制使用,具體取決於你的平臺的設置方式),你須要明確設置主機名和端口號(安全或非安全),以便他們使用路由器。你可能還但願使用實例元數據,以便區分客戶端上的實例(例如,在自定義負載均衡器中),默認狀況下,eureka.instance.instanceId
是vcap.application.instance_id
,如如下示例所示:
application.yml
eureka: instance: hostname: ${vcap.application.uris[0]} nonSecurePort: 80
根據在Cloud Foundry實例中設置安全規則的方式,你能夠註冊並使用主機VM的IP地址進行直接服務到服務調用,Pivotal Web Services(PWS)還沒有提供此功能。
若是計劃將應用程序部署到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; }
一個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應用程序中,所以不須要隨機值。
一旦你擁有一個發現客戶端的應用程序,就可使用它從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(); }
不要在@PostConstruct
方法或@Scheduled
方法中使用EurekaClient
(或者可能還沒有啓動ApplicationContext
的任何地方),它在SmartLifecycle
中初始化(phase=0
),所以最先能夠依賴它的是另外一個具備更高階段的SmartLifecycle
。
默認狀況下,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>
你無需使用原始Netflix EurekaClient
,此外,在某種包裝後面使用它一般更方便,Spring Cloud經過邏輯Eureka服務標識符(VIP)而不是物理URL支持Feign(REST客戶端構建器)和Spring RestTemplate
。要使用固定的物理服務器列表配置Ribbon,能夠將<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; }
做爲實例還涉及到註冊表的按期心跳(經過客戶端的serviceUrl
),默認持續時間爲30秒,在實例、服務器和客戶端在其本地緩存中都具備相同的元數據以前,客戶端沒法發現服務(所以可能須要3個心跳)。你能夠經過設置eureka.instance.leaseRenewalIntervalInSeconds
來更改週期,將其設置爲小於30的值會加快使客戶端鏈接到其餘服務的過程,在生產中,最好堅持使用默認值,由於服務器中的內部計算會對租約續期作出假設。
若是你已將Eureka客戶端部署到多個區域,你可能但願這些客戶端在嘗試另外一個區域中的服務以前使用同一區域內的服務,要進行此設置,你須要正確配置Eureka客戶端。
首先,你須要確保將Eureka服務器部署到每一個區域,並確保它們彼此對等,有關詳細信息,請參閱有關Zones和Regions的部分。
接下來,你須要告訴Eureka你的服務所在的區域,你可使用metadataMap
屬性執行此操做,例如,若是將service 1
部署到zone 1
和zone 2
,則須要在service 1
中設置如下Eureka屬性:
zone 1的service 1
eureka.instance.metadataMap.zone = zone1 eureka.client.preferSameZoneEureka = true
zone 2的service 1
eureka.instance.metadataMap.zone = zone2 eureka.client.preferSameZoneEureka = true