通過自動配置和綁定到Spring環境和其他Spring編程模型慣例,為Spring Boot應用程序提供Netflix OSS集成。 通過幾個簡單的注釋,您可以快速啟用和配置應用程序中的常見功能模塊,並使用久經考驗的Netflix組件構建大型分布式系統。 提供的功能模塊包括服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul)和客戶端負載均衡(Ribbon)。
服務發現:Eureka客戶端(Service Discovery: Eureka Clients)
服務發現是microservice基礎架構的關鍵原則之一。試圖着手配置每個客戶端或某種格式的約定可以說是非常困難的和非常脆弱的。Eureka是Netflix服務發現的一種服務和客戶端。這種服務是可以被高可用性配置的和部署,並且在注冊的服務當中,每個服務的狀態可以互相復制給彼此。
注冊到Eureka(Registering with Eureka)
當一個客戶端注冊到Eureka,它提供關於自己的元數據(諸如主機和端口,健康指標URL,首頁等)Eureka通過一個服務從各個實例接收心跳信息。如果心跳接收失敗超過配置的時間,實例將會正常從注冊里面移除。
eureka 客戶端例子:
@Configuration @ComponentScan @EnableAutoConfiguration @EnableEurekaClient @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); } }
在這個例子里我們使用 @EnableEurekaClient
來聲明, 但只有使 Eureka 生效還得 使用 @EnableDiscoveryClient
。 配置要求 定位Eureka服務端。 例如:
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
"defaultZone"是一個神奇的字符串回退值,它提供了服務的URL給任何客戶端,而這不意味優先級。 (i.e. 是一個常用的默認值).
默認應用名(服務ID),物理主機地址和非安全端口, 分別從環境的 ${spring.application.name}
, ${spring.application.name}
和 ${server.port}
獲取。
@EnableEurekaClient
使Eureka做為一個實例(注冊直接)和客戶端(它能通過查找注冊來定位其它服務)注冊到應用里面。實例的行為由eureka.instance.*
的配置項來決定,但是你最好確保你的spring.application.name
有個默認值。(這是Eureka的默認ID或VIP)。
對Eureka服務的身份驗證(Authenticating with the Eureka Server)
如果其中一個eureka.client.serviceUrl.defaultZone
的url已經把憑證嵌入到它里面,那么HTTP基本的身份驗證將會被自動添加到你的eureka客戶端(curl風格,如 http://user:password@localhost:8761/eureka)。 對於更復雜的需求,您可以創建一個帶“@Bean”注解的“DiscoveryClientOptionalArgs”類型並且為它注入“ClientFilter”實例。
由於Eureka的一個限制是不可能支持每個服務器基本授權認證,所以只被發現的第一組會被使用。
健康指標和狀態頁面(Status Page and Health Indicator)
健康指標和狀態頁面分別對應一個Eureka實例的"/health"和"/info",是在一個Spring Boot Actuator應用默認的配置位置中很有用的一個點。即便是一個Actuator應用,如果你使用非默認的上下文路徑或者servlet路徑(如server.servletPath=/foo
)或管理端點的路徑(如management.contextPath=/admin
),你都需要做出相應的改變。例如:
application.yml
eureka: instance: statusPageUrlPath: ${management.context-path}/info healthCheckUrlPath: ${management.context-path}/health
這些鏈接呈現出在元數據所消耗的客戶端,並且使用在某些情況下決定是否發送請求給你應用程序,如果這些信息准確的話它們是有用的。
注冊一個安全應用(Registering a Secure Application)
如果你的應用想通過HTTPS被聯系上你需要設置兩個標記,分別是EurekaInstanceConfig
, viz eureka.instance.[nonSecurePortEnabled,securePortEnabled]=[false,true]
。這將使Eureka推送實例的信息展示一個顯式的安全通信。Spring Cloud的DiscoveryClient
將總是通過這種方式返回一個服務的配置的URI(https://…;
), 並且Eureka實例(native)的信息將有一個安全的健康檢查的URL
因為Eureka的內部工作方式,它將繼續推送一個非安全的URL的狀態和主頁,除非你還覆蓋那些聲明。你可以使用占位符娶配置eureka實例的url。 例子:
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或其它平台上跑的話),那么你將要確保應用能夠攔截和處理代理轉發的頭信息。如果它明確配置有'X-Forwarded-\*`這類頭信息的情況下,在一個Spring Boot應用里面內嵌的Tomcat容器自動處理的。出現這個錯誤的原因,是因為你的應用程序提供的鏈接本身弄錯了。(錯誤的主機,端口,協議)。
Eureka 健康檢查(Eureka’s Health Checks)
默認情況下,Eureka使用客戶端心跳來確定一個客戶端是否活着。除非另有指定Discovery Client不會傳播當前Spring Boot Actuator的應用性能的健康檢查狀態。也就是說在成功注冊Eureka后總會宣布的應用程序在“UP”的狀態。這種發送應用狀態給Eureka的行為將觸發Eureka的健康檢查。因此其他每個應用程序在其他狀態下不會給應用程序發送通信然后才‘UP’。
application.yml
eureka: client: healthcheck: enabled: true
如果你有更多超出健康檢查的監控,你可以考慮實現自己的com.netflix.appinfo.HealthCheckHandler
.
Eureka給客戶端和實例的元數據(Eureka Metadata for Instances and Clients)
值得花一點時間了解Eureka元數據是如何工作的,所以你可以使用它的方式在你的平台是有意義的。有標准的元數據,如主機名、IP地址、端口號、狀態頁面和健康檢查。這些元數據在服務注冊和客戶端聯系服務端時以一種簡單的方式被推送出去。額外的元數據可以被添加到實例注冊在eureka.instance.metadataMap
里面,並且這都是在遠程客戶端可訪問到的,但通常不會改變客戶的行為,除非它是識別到元數據的含義。有一些特殊的情況:Spring Cloud已經分配元數據映射的含義。
在 Cloudfoundry 使用 Eureka(Using Eureka on Cloudfoundry)
Cloudfoundry有總的路由,所有在同個應用的實例有相同的主機名(在其他PaaS解決方案有類似的架構和這一樣)。這不妨礙我們使用Eureka,但如果你使用路由(推薦,甚至強制性的根據您的平台建立的方式),你需要顯式地設置主機名和端口號(安全或不安全的),以便他們使用路由器。您可能還希望使用實例元數據,這樣你就可以區分在客戶端實例(如:在一個定制的負載均衡器)。默認情況下,eureka.instance.instanceId
相當於vcap.application.instance_id
。例如:
eureka: instance: hostname: ${vcap.application.uris[0]} nonSecurePort: 80
按照規則把安全規則設置在你的Cloudfoundry實例里面,你就可以通過服務到服務的調用方式注冊和使用主機虛擬機的IP地址。這個功能還沒有可以在關鍵的Web服務。 (PWS).
在AWS上使用Eureka(Using Eureka on AWS)
假如應用要計划部署到AWS雲,那么Eureka實例必須配置為亞馬遜可識別的並且可以通過以下方式定制 EurekaInstanceConfigBean :
@Bean @Profile("!default") public EurekaInstanceConfigBean eurekaInstanceConfig() { EurekaInstanceConfigBean b = new EurekaInstanceConfigBean(); AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka"); b.setDataCenterInfo(info); return b; }
修改Eureka實例ID(Changing the Eureka Instance ID)
Netflix Eureka實例是一個身份證,等於其域名注冊(即只有一個服務/主機)。Spring Cloud Eureka提供了合理的默認值,看起來像這樣:${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}
。例如` Myhost:myappname:8080 `。
使用Spring雲可以通過eureka.instance.instanceId
提供一個惟一的標識符覆蓋它:
application.yml
eureka:
instance:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
根據這種元數據,並且多個實例部署在localhost,隨機值可以確保實例的唯一。但是在Cloudfoundry中,vcap.application.instance_id
將被自動賦值在一個Spring Boot應用程序中,因此隨機值將不再被需要。
使用EurekaClient(Using the EurekaClient)
一旦你有一個應用是 @EnableDiscoveryClient
(或 @EnableEurekaClient
) 你使用它從 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
的更高階段依賴它生效。
代替原生的Netflix EurekaClient(Alternatives to the native Netflix EurekaClient)
你不一定要使用內存Netflix EurekaClient
並且通常使用它背后的某種形式的封裝是更方便的。 Spring Cloud已經支持Feign(一個REST客戶端構建)並且Spring RestTemplate
也使用邏輯Eureka服務標識符(VIP)代替物理的URL。去配置一個固定物理服務器的列表的Ribbon,你可以對<client>
是客戶端的ID,用一個逗號分隔物理地址(或主機名)列表來簡單地設置<client>.ribbon.listOfServers
。
你也可以使用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; }
為什么注冊一個服務這么慢? (Why is it so Slow to Register a Service?)
作為一個實例向注冊中心還包括一個默認持續30秒的周期心跳(通過客戶的serviceUrl
)。一個服務對於客戶端的discovery不可用的,直到實例、服務端和客戶端全都擁有相同的元數據在它們的緩存里面(這可能還需要3次心跳)。你可以改變使用eureka.instance.leaseRenewalIntervalInSeconds
並且這將加快這一進程的客戶端連接到其他服務。在實際生產中堅持默認可能是更好的,因為有一些在服務器內部計算對租賃復興時期做出假設。
服務發現: Eureka Server(Service Discovery: Eureka Server)
例如 eureka 服務 (例子: 使用 spring-cloud-starter-eureka-server 去設置 classpath):
@SpringBootApplication @EnableEurekaServer public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } }
服務端有一個帶UI的首頁並且HTTP API端點按正常的Eureka功能下的/eureka/*
。
由於Gradle的依賴解析規則缺乏父依賴,單純的依靠spring-cloud-starter-eureka-server會導致啟動失敗。為了填補這一缺陷,必須添加Spring Boot Gradle插件和導入Spring cloud starter的父依賴,如:
buildscript { dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE") } }
apply plugin: "spring-boot" 應用“spring-boot”插件
dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE" } }
高可用性(High Availability, Zones and Regions)
Eureka服務端沒有后台存儲,但是服務實例在注冊里面全都得發送心跳去保持注冊更新(在內存里操作)客戶端們也有一份內存緩存着eureka的注冊信息(因此,他們不必表為每單一的請求注冊到一個服務)。
默認情況下每個Eureka服務端也是一個Eureka客戶端並且通過請求服務的URL去定位一個節點。如果你不提供的話,服務雖然還會運行和工作,但是它不會向你打印一堆關於沒有注冊的節點日志。
標准模式(Standalone Mode)
這兩個緩存的組合(客戶端和服務器)和心跳使一個標准模式的Eureka客戶端彈性掛掉,只要有某種monitor或者elastic運行保持它活着。(如 Cloud Foundry)在標准模式,您可能更傾向於關閉客戶端的行為,所以它不能保持失敗重試和重新找回它的那些節點。如:
server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
請注意,serviceUrl
是指向同一個主機的本地實例。
節點感知(Peer Awareness)
Eureka甚至可以更有彈性和可用的運行多個實例,並讓他們互相注冊。事實上,這也是默認的行為,因此所有你需要讓它工作的,只要添加一個有效的節點serviceUrl
,例如:
application.yml (Two Peer Aware Eureka Servers)
--- 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
)。
你可以添加更多的節點進一個系統,並且只要他們全都互相能通過最少一邊來互相連接,他們將同步互相注冊他們自己。如果節點被分離(在一個數據中心或者多個數據中心之間)那么系統原則上split-brain類型存活失敗。
IP偏好(Prefer IP Address)
在一些案例里,更可取的是Eureka廣播服務的IP地址而不是主機名。設置eureka.instance.preferIpAddress
為true
以便當應用注冊到eureka的濕乎乎,它將使用它的IP地址而不是主機名。
斷路器(Circuit Breaker: Hystrix Clients)
Netflix意見創建了一個庫叫 Hystrix 實現了 circuit breaker pattern。 在microservice架構中有多個層的服務調用。
一個低水平的服務群中一個服務掛掉會給用戶導致級聯失效的。當調用一個特定的服務達到一定閾值(在Hystrix里默認是5秒內20個失敗),斷路由開啟並且調用沒有成功的。開發人員能夠提供錯誤原因和開啟一個斷路由回調。
出現公開的電路停止連鎖故障並且允許解決或者失敗的服務時間來修復。回調能被另一個Hystrix保護調用,靜態數據或者合理的空數據。回調 可以綁定因此第一次回調會有一些其它的業務調用反過來才落回到靜態數據。
Example boot app:
啟動應用示例:
@SpringBootApplication @EnableCircuitBreaker public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } @Component public class StoreIntegration { @HystrixCommand(fallbackMethod = "defaultStores") public Object getStores(Map<String, Object> parameters) { //do stuff that might fail } public Object defaultStores(Map<String, Object> parameters) { return /* something useful */; } }
Netflix路由庫提供@HystrixCommand
調用 "javanica". Spring Cloud 在這個注解下自動包裹Spring beans進一個代理里面,被連接到了一個Hystrix斷路由。斷路器計算何時打開和關閉電路,並在失敗的情況下做什么。
你可以使用commandProperties
參數和@HystrixProperty
注解的集合來配置@HystrixCommand
。請見 here 更多細節. 請見 the Hystrix wiki 有關可用的屬性。
傳播安全上下文或使用 Spring Scopes(Propagating the Security Context or using Spring Scopes)
如果你想一些線程的本地的上下文傳播到一個@HystrixCommand
,默認聲明將不會工作,因為他在線程池里執行命令。(在超時的情況下)。你可以切換Hystrix去使用同個線程讓調用者使用一些配置,或直接在注解中,讓它去使用不同的“隔離策略”。舉例:
@HystrixCommand(fallbackMethod = "stubMyService", commandProperties = { @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE") } ) ...
如果你使用@SessionScope
或 @RequestScope
同樣適用。你會知道你要這么做,因為一個runtime異常說他不能找到作用域上下文。
健康指標(Health Indicator)
連接的斷路器的狀態也暴露在呼叫應用程序的/health
端點中。
{ "hystrix": { "openCircuitBreakers": [ "StoreIntegration::getStoresByLocationLink" ], "status": "CIRCUIT_OPEN" }, "status": "UP" }
Hystrix 指標流(Hystrix Metrics Stream)
使Hystrix指標流包括依賴於spring-boot-starter-actuator
。這將使/hystrix.stream
流作為一個管理端點。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
斷路器: Hystrix 儀表盤(Circuit Breaker: Hystrix Dashboard)
Hystrix的主要作用是會采集每一個HystrixCommand的信息指標,把每一個斷路器的信息指標顯示的Hystrix儀表盤上。
運行Hystrix儀表板需要在spring boot主類上標注@EnableHystrixDashboard
。然后訪問/hystrix
查看儀表盤,在hystrix客戶端應用使用/hystrix.stream
監控。
Turbine
看一個實例Hystrix數據對於整個系統的健康不是很有用. Turbine 是一個應用程序,該應用程序匯集了所有相關的/hystrix.stream
端點到 /turbine.stream
用於Hystrix儀表板。運行turbine使用@EnableTurbine
注解你的主類,使用spring-cloud-starter-turbine這個jar。配置請參考 the Turbine 1 wiki 唯一的區別是turbine.instanceUrlSuffix
不需要端口號前綴,因為這是自動處理,除非turbine.instanceInsertPort=false
。
turbine.appConfig
配置是一個eureka服務ID列表,turbine將使用這個配置查詢實例。turbine stream在hystrix dashboard中使用如下的url配置: http://my.turbine.server:8080/turbine.stream?cluster=<CLUSTERNAME>,如果集群的名稱是default,集群參數可以忽略)。這個cluster
參數必須和turbine.aggregator.clusterConfig
匹配。從eureka返回的值都是大寫的,因此我們希望下面的例子可以工作,如果一個app使用eureka注冊,並且被叫做"customers":
turbine:
aggregator:
clusterConfig: CUSTOMERS
appConfig: customers
clusterName
可以使用SPEL表達式定義,在turbine.clusterNameExpression
。 默認值是appName
,意思是eureka服務ID最終將作為集群的key,例如customers的 InstanceInfo
有一個CUSTOMERS的appName
。另外一個例子是turbine.clusterNameExpression=aSGName
,將從AWS ASG name獲取集群名稱。作者例子:
turbine:
aggregator:
clusterConfig: SYSTEM,USER
appConfig: customers,stores,ui,admin
clusterNameExpression: metadata['cluster']
在這種情況下,集群名稱從4個服務從其元數據映射,期望包含“SYSTEM”和“USER
所有的app使用default,你需要一個文字表達式(使用單引號):
turbine:
appConfig: customers,stores
clusterNameExpression: "'default'"
spring cloud提供一個spring-cloud-starter-turbine
,所有依賴項你需要運行一個turbine服務器。使用@EnableTurbine
創建一個spring boot應用。
默認情況下Spring Cloud 允許 Turbine 在集群的每個主機下使用主機名和端口運行多個進程。如果你想在集群中的每個主機使用本機原生Netfix行為且不允許多個進程創建運行Turbine。(實例id的key為主機名)然后設置屬性turbine.combineHostPort=false
Turbine Stream
在一些環境(Pass), 在所有分布式下典型的Turbine 模型的Hystrix 命令都不工作,在這種情況下,你可能想要 Hystrix 命令 推送到 Tuibine, 和Spring Cloud進行消息傳遞,那么你需要要做的是在客戶端添加一個依賴spring-cloud-netflix-hystrix-stream
和你所選擇的 spring-cloud-starter-stream-*
的依賴(相關信息請查看 Spring Cloud Stream 方檔,以及如何配置客戶端憑證,和工作時的要本地代理)
創建一個帶有注解 @EnableTurbineStream
的Spring boot 應用服務器,端口默認 8989 (Hystrix 儀表盤的URL都使用此端口), 如果你想自定義端口,可以配置 server.port
或 turbine.stream.port
任一個,如果你使用了 spring-boot-starter-web
和 spring-boot-starter-actuator
,那么你可以提供(使用Tomcat默認情況下) management.port
不同的端口,並打開這個單獨的執行器端口
你可以把Dashboard指向Turbine Stream Server來代替個別Hystrix streams。如果Tubine Stream 使用你本機的8989端口運行,然后把 http://myhost:8989
在流輸入字段Hystrix儀表板 Circuits 將由各自的 serverId關綴,緊隨其后的是一個點,然后是circuit 名稱
Spring Cloud提供了一個 spring-cloud-starter-turbine-stream
,包括了運行 Turibine Stream Server運行所需要的所有依賴,如spring-cloud-starter-stream-rabbit
. 因為使用了Netty-based,所以你需要Java 8 運行此應用
客戶端負載均衡器(Client Side Load Balancer: Ribbon)
在Ribbon一個核心概念是命名的客戶端.每個負載 平衡器是共同組件的集合的一部分,通過遠程服務器聯系, 你把它作為應用程序開發者(例如,使用 @FeignClient
注解)的名稱,Spring Cloud創建一個新的整體使用RibbonClientConfiguration
為每一個客戶端命名的 ApplicationContext
,這包含(除其他事項外)的ILoadBalancer
,一個RestClient
實現和ServerListFilter
。
Spring clound 使用額外的配置(RibbonClientConfiguration
)可以讓你充分控制客戶端, 使用@RibbonClient
. 例子:
@Configuration @RibbonClient(name = "foo", configuration = FooConfiguration.class) public class TestConfiguration { }
在這種情況下客戶端組件由 RibbonClientConfiguration
和一些 FooConfiguration
組成 (通常后者會覆蓋前者)
FooConfiguration
必須有 @Configuration
,而不是由主應用程序@ComponentScan
收集,其他情況下會被 @RibbonClients
共享,如果你使用@ComponentScan
或@SpringBootApplication
,那么你應放入單獨的,非同名的包下或明確@ComponentScan
掃描的指定包。
Spring Cloud Netflix ribbon默認提供了以下beans (BeanType
beanName: ClassName
):
-
IClientConfig
ribbonClientConfig:DefaultClientConfigImpl
-
IRule
ribbonRule:ZoneAvoidanceRule
-
IPing
ribbonPing:NoOpPing
-
ServerList<Server>
ribbonServerList:ConfigurationBasedServerList
-
ServerListFilter<Server>
ribbonServerListFilter:ZonePreferenceServerListFilter
-
ILoadBalancer
ribbonLoadBalancer:ZoneAwareLoadBalancer
創建一個bean來自其中一個類型,並把它放在一個@RibbonClient
的配置(比如上面的FooConfiguration
), 允許你覆蓋一個描述的Bean. 例:
@Configuration public class FooConfiguration { @Bean public IPing ribbonPing(IClientConfig config) { return new PingUrl(); } }
這里用PingUrl
替換NoOpPing
.
在Eureka中使用Ribbon(Using Ribbon with Eureka)
當 Eureka 和 Ribbon 的 ribbonServerList
一起使用來自Eureka中被覆蓋且擴展的 DiscoveryEnabledNIWSServerList
服務器列表,它用IPing
接口 和 NIWSDiscoveryPing
委托給Eureka來確保服務器是否啟動,安裝在ServerList
缺省值為 DiscoveryEnabledNIWSServerList
這樣的目的是使用物理元數據提供給負載均衡器,而無需使用AWS AMI元數據(這是Netflix的依賴),默認情況下,服務器列表將由“區域”中提供的信息實例元數據(遠程客戶端設置eureka.instance.metadataMap.zone
),如果缺少它可以使用從服務器主機名作為區域代理的域名(如標志approximateZoneFromHostname
設置)。一旦該區域信息可用它可以在一個ServerListFilter
使用。默認情況下,將被使用在同一區域定位服務器作為客戶端,因為默認是ZonePreferenceServerListFilter
。的區域客戶被確定相同的方式由缺省遠程實例即通過eureka.instance.metadataMap.zone
。
NOTE:規范的 "archaius" 方式去設置客戶端區,可以使用配置屬性"@zone", Spring Cloud 將優先於所有的其他設置(注意:該重點將在 YAML 配置被引用)
NOTE:如果沒有其他來源的區域數據則由基於客戶機的配置(而不是實例配置).我們把 eureka.client.availabilityZones
, 這是一個從區域名稱映射到區域的列表,並拿出第一個區為實例自己的區域(即eureka.client.region
, 默認為"us-east-1" 與 原生的Netflix comatibility)
示例: 沒有Eureka時如何使用Ribbon(Example: How to Use Ribbon Without Eureka)
Eureka 是一種很方便的抽象方式去發現遠程服務器,所以你不需要硬編碼他們的客戶端URL,但如果你不喜歡使用它,Ribbon 和 Feign 仍然經得起檢驗,假設你已經聲明了 @RibbonClient
為 "stores", 與 Eureka 是不使用(甚至不能在類路徑). Ribbon 客戶端默認配置的服務器列,並且可以提供這樣的配置
stores:
ribbon:
listOfServers: example.com,google.com
禁用Eureka使用Ribbon(Example: Disable Eureka use in Ribbon)
設置屬性ribbon.eureka.enabled = false
, 使用Ribbon時禁用Eureka
ribbon: eureka: enabled: false
直接使用Ribbon的API(Using the Ribbon API Directly)
你也可以直接使用 LoadBalancerClient
,例子:
public class MyClass { @Autowired private LoadBalancerClient loadBalancer; public void doStuff() { ServiceInstance instance = loadBalancer.choose("stores"); URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort())); // ... do something with the URI } }
聲明REST Client:Feign(Declarative REST Client: Feign)
Feign 是一個聲明web服務客戶端,這便得編寫web服務客戶端更容易,使用Feign 創建一個接口並對它進行注解,它具有可插拔的注解支持包括Feign注解與JAX-RS注解,Feign還支持可插拔的編碼器與解碼器,Spring Cloud 增加了對 Spring MVC的注解,Spring Web 默認使用了HttpMessageConverters
, Spring Cloud 集成 Ribbon 和 Eureka 提供的負載均衡的HTTP客戶端 Feign.
Example spring boot app
@Configuration @ComponentScan @EnableAutoConfiguration @EnableEurekaClient @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } StoreClient.java @FeignClient("stores") public interface StoreClient { @RequestMapping(method = RequestMethod.GET, value = "/stores") List<Store> getStores(); @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
}
在“@FeignClient”注解字符串值(上邊的“stores”)是任意客戶端名稱,用於創建一個帶負載均衡器(see< < spring-cloud-ribbon,支持> >)。您還可以指定一個URL使用的URL屬性(絕對值或只是一個主機名)。應用程序上下文中的bean的名稱是接口的完全有資格的名稱。還創建一個別名就是“名稱”屬性加上“FeignClient”。在上面的示例中,“@qualifier(“storesFeignClient”)“可以用來指示bean。
Ribbon Client 想要發現上邊提到 "stores" 服務的物理地址, 如果你的應用程序是 Eureka client的 則將在 Eureka 服務倉庫里解決服務的注冊, 如果你不想用Eureka, 你可以簡單配置服務列表above for example).
覆蓋Feign默認(Overriding Feign Defaults)
在Spring Cloud’s 的Feign支持的一個核心概念是命名的客戶端.每個feign客戶端是共同組件的集合的一部分,通過遠程服務器聯系, 你把它作為應用程序開發者(例如,使用 @FeignClient
注解)的名稱,Spring Cloud創建一個新的整體使用FeignClientsConfiguration
為每一個客戶端命名的 ApplicationContext
,這包含(除其他事項外)的feign.Decoder
,一個feign.Encoder
和feign.Contract
。
Spring Cloud 可以在@FeignClient
使用額外的配置(在FeignClientsConfiguration
)完全控制feign客戶端,例:
@FeignClient(name = "stores", configuration = FooConfiguration.class) public interface StoreClient { //.. }
在這種情況下,客戶端是由組件已經在FeignClientsConfiguration
連同任何FeignClientsConfiguration
(后者將會覆蓋前者)。
WARNING:FooConfiguration
必須是@ Configuration
但照顧它不是在@ ComponentScan
為主要應用程序上下文,否則將被用於每個@ FeignClient
。如果你使用@ComponentScan
(或@ SpringBootApplication
),你需要采取一些措施來避免它被列入(比如把它放在一個單獨的,非重疊的包,或者指定包在@ComponentScan明確掃描
)
該 serviceId
已經過時,建議使用 name
屬性
以前,使用 url
屬性,則 name
不是必須的,但現在是必須的.
占位符支持 name
和 url
屬性.
@FeignClient(name = "${feign.name}", url = "${feign.url}") public interface StoreClient { //.. }
Spring Cloud Neflix Feign 默認提供了以下 Bean (BeanType
beanName: ClassName
):
-
Decoder
feignDecoder:ResponseEntityDecoder
(which wraps aSpringDecoder
) -
Encoder
feignEncoder:SpringEncoder
-
Logger
feignLogger:Slf4jLogger
-
Contract
feignContract:SpringMvcContract
-
Feign.Builder
feignBuilder:HystrixFeign.Builder
Spring Coud Netflix Feign 默認不提以下Bean, 但仍可以從應用的上下文中查找以下類型創建 Feign 客戶端.
-
Logger.Level
-
Retryer
-
ErrorDecoder
-
Request.Options
-
Collection<RequestInterceptor>
創建這些類型的一個bean可以放在@FeignClient
配置中(如上FooConfiguration
),允許你覆蓋所描述的每一個bean. 例 子:
@Configuration public class FooConfiguration { @Bean public Contract feignContract() { return new feign.Contract.Default(); } @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "password"); } }
可以替換SpringMvcContract
和 feign.Contract.Default
, 並增加一個 RequestInterceptor
到 RequestInterceptor
中去.
默認配置可在 @EnableFeignClients
屬性, defaultConfiguration
通過類似的方式與上述被指定,不同的是,該結構將適用於 所有的 Feign 客戶端.
Feign Hystrix Support
如果 Hystrix 在 classPath下, 默認情況下 將包括 Feign 與 斷路器所有的方法。 返回一個 com.netflix.hystrix.HystrixCommand
去使用,允許您使用反應模式 ( 調用.toObservable()
或 .observe()
或異步使用 ( .queue()
).
要禁用Feign 的 Hystrix支持,設置feign.hystrix.enabled=false
.
要在每個客戶端上禁用 Hystrix 支持,創建一個 Feign.Builder
並將scope 設置為"prototype",例如:
@Configuration public class FooConfiguration { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
Feign Hystrix Fallbacks
Hystrix 支持回退的概念, 當線路打開有錯誤時則執行默認代碼路徑, 要啟用回退要給@FeignClient
設置fallback
屬性來實現回退的類名.
@FeignClient(name = "hello", fallback = HystrixClientFallback.class) protected interface HystrixClient { @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello iFailSometimes(); } static class HystrixClientFallback implements HystrixClient { @Override public Hello iFailSometimes() { return new Hello("fallback"); } }
有一個局限性,Feign的回退實現與Hystrix的回退一起工作, Fallbacks 目前不支持返回com.netflix.hystrix.HystrixCommand
and rx.Observable
的方法
Feign Inheritance Support
Feign 支持通過單繼承接口樣板的API。 這使得分組常見的操作方便的進入基本接口.
UserService.java public interface UserService { @RequestMapping(method = RequestMethod.GET, value ="/users/{id}") User getUser(@PathVariable("id") long id); } UserResource.java @RestController public class UserResource implements UserService { } UserClient.java package project.user; @FeignClient("users") public interface UserClient extends UserService { }
它通常是不可取的共享服務器和客戶機之間的接口。它引入了緊耦合,也其實並不在其目前的形式與Spring MVC的工作(方法參數映射不繼承)。
Feign 請求/響應 壓縮(Feign request/response compression)
你可考慮啟用請求或響應的 GZIP 壓縮 Feign 的請求, 你可以通過啟用一個屬性做到這一點:
feign.compression.request.enabled=true feign.compression.response.enabled=true
Feign 請求壓縮設置和您的web服務器請求壓縮設置類似。
feign.compression.request.enabled=true feign.compression.request.mime-types=text/xml,application/xml,application/json feign.compression.request.min-request-size=2048
用這些屬性可以有選擇性的對壓縮介質和最低要求的閾值。
Feign logging
日志是為每個創建Feign客戶端創建, 默認的日志名稱是用於創建Feign客戶端接口的完整類名,Feign日志只響應 DEBUG
級別。
application.yml
logging.level.project.user.UserClient: DEBUG
你能為每個客戶端配置Logger.Level
對象,記錄許多的日志,選項包括:
-
NONE
, No logging (DEFAULT). -
BASIC
, Log only the request method and URL and the response status code and execution time. -
HEADERS
, Log the basic information along with request and response headers. -
FULL
, Log the headers, body, and metadata for both requests and responses. -
NONE
, 不記錄 (DEFAULT). -
BASIC
, 僅記錄請求方式和URL及響應的狀態代碼與執行時間. -
HEADERS
, 日志的基本信息與請求及響應的頭. -
FULL
, 記錄請求與響應的頭和正文及元數據.
例如:以下設置Logger.Level
為 FULL
:
@Configuration public class FooConfiguration { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
外部配置: Archaius(External Configuration: Archaius)
Archaius Example class ArchaiusTest { DynamicStringProperty myprop = DynamicPropertyFactory .getInstance() .getStringProperty("my.prop"); void doSomething() { OtherClass.someMethod(myprop.get()); } }
Archaius有它自己的一套配置文件和負載優先級, Spring 應用程序通常不應直接應用Archaius, 本身仍然有配置Netflix工具的需求, Spring Cloud 環境以橋接方式讓Archaius要以閱讀Spring 的環境屬性, 在大多數情況下,這允許 Spring boot項目中使用正常的配置工具鏈,同時讓他們配置Netflix工具,作為記錄
路由和過濾器:Zuul(Router and Filter: Zuul)
路由是微服務架構中不可或缺的一部分。比如,/
可能需要映射到你的web應用, /api/users
映射到用戶服務, /api/shop
映射到商城服務. Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。
Netflix uses Zuul for the following:
-
Authentication
-
Insights
-
Stress Testing
-
Canary Testing
-
Dynamic Routing
-
Service Migration
-
Load Shedding
-
Security
-
Static Response handling
-
Active/Active traffic management
-
認證
-
Insights
-
壓力測試
-
金絲雀測試
-
動態路由
-
服務遷移
-
負載削減
-
安全
-
靜態響應處理
-
主動/主動交換管理
Zuul的規則引擎允許通過任何JVM語言來編寫規則和過濾器, 支持基於Java和Groovy的構建。
配置屬性 zuul.max.host.connections
已經被兩個新的配置屬性替代, zuul.host.maxTotalConnections
和 zuul.host.maxPerRouteConnections
, 默認值分別是200和20.
嵌入Zuul反向代理(Embedded Zuul Reverse Proxy)
Spring Cloud創建了一個嵌入式Zuul代理來緩和急需一個UI應用程序來代理調用一個或多個后端服務的通用需求, 這個功能對於代理前端需要訪問的后端服務非常有用, 避免了所有后端服務需要關心管理CORS和認證的問題.
在Spring Boot主函數上通過注解 @EnableZuulProxy
來開啟, 這樣可以讓本地的請求轉發到適當的服務. 按照約定, 一個ID為"users"的服務會收到 /users
請求路徑的代理請求(前綴會被剝離). Zuul使用Ribbon定位服務注冊中的實例, 並且所有的請求都在hystrix的command中執行, 所以失敗信息將會展現在Hystrix metrics中, 並且一旦斷路器打開, 代理請求將不會嘗試去鏈接服務.
Zuul starter沒有包含服務發現的客戶端, 所以對於路由你需要在classpath中提供一個根據service IDs做服務發現的服務.(例如, eureka是一個不錯的選擇)
在服務ID表達式列表中設置 zuul.ignored-services
, 可以忽略已經添加的服務. 如果一個服務匹配表達式, 則將會被忽略, 但是對於明確配置在路由匹配中的, 將不會被忽略, 例如:
application.yml zuul: ignoredServices: '*' routes: users: /myusers/**
在這個例子中, 除了"users", 其他所有服務都被忽略
增加或改變代理路由, 你可以添加類似下面的外部配置:
application.yml zuul: routes: users: /myusers/**
這個意味着http請求"/myusers"將被轉發到"users"服務(比如 "/myusers/101" 將跳轉到 "/101")
為了更細致的控制一個路由, 你可以直接配置路徑和服務ID:
application.yml zuul: routes: users: path: /myusers/** serviceId: users_service
這個意味着HTTP調用"/myusers"被轉發到"users_service"服務. 路由必須配置一個可以被指定為ant風格表達式的"path", 所以“/myusers/*”只能匹配一個層級, 但"/myusers/**"可以匹配多級.
后端的配置既可以是"serviceId"(對於服務發現中的服務而言), 也可以是"url"(對於物理地址), 例如:
application.yml zuul: routes: users: path: /myusers/** url: http://example.com/users_service
這個簡單的"url-routes"不會按照 HystrixCommand
執行, 也無法通過Ribbon負載均衡多個URLs. 為了實現這一指定服務路由和配置Ribbon客戶端(這個必須在Ribbon中禁用Eureka: 具體參考更多信息), 例如:
application.yml zuul: routes: users: path: /myusers/** serviceId: users ribbon: eureka: enabled: false users: ribbon: listOfServers: example.com,google.com
你可以使用regexmapper提供serviceId和routes之間的綁定. 它使用正則表達式組來從serviceId提取變量, 然后注入到路由表達式中.
@Bean public PatternServiceRouteMapper serviceRouteMapper() { return new PatternServiceRouteMapper( "(?<name>^.+)-(?<version>v.+$)", "${version}/${name}"); }
這個意思是說"myusers-v1"將會匹配路由"/v1/myusers/**". 任何正則表達式都可以, 但是所有組必須存在於servicePattern和routePattern之中. 如果servicePattern不匹配服務ID,則使用默認行為. 在上面例子中,一個服務ID為“myusers”將被映射到路徑“/ myusers/**”(沒有版本被檢測到),這個功能默認是關閉的,並且僅適用於服務注冊的服務。
設置 zuul.prefix
可以為所有的匹配增加前綴, 例如 /api
. 代理前綴默認會從請求路徑中移除(通過 zuul.stripPrefix=false
可以關閉這個功能). 你也可以在指定服務中關閉這個功能, 例如:
zuul: routes: users: path: /myusers/** stripPrefix: false
在這個例子中, 請求"/myusers/101"將被跳轉到"users"服務的"/myusers/101"上.
zuul.routes
實際上綁定到類型為 ZuulProperties
的對象上. 如果你查看這個對象你會發現一個叫"retryable"的字段, 設置為"true"會使Ribbon客戶端自動在失敗時重試(如果你需要修改重試參數, 直接使用Ribbon客戶端的配置)
X-Forwarded-Host
請求頭默認在跳轉時添加. 通過設置 zuul.addProxyHeaders = false
關閉它. 前綴路徑默認剝離, 並且對於后端的請求通過請求頭"X-Forwarded-Prefix"獲取(上面的例子中是"/myusers")
通過 @EnableZuulProxy
應用程序可以作為一個獨立的服務, 如果你想設置一個默認路由("/"), 比如 zuul.route.home: /
將路由所有的請求(例如: "/**")到"home"服務.
如果需要更細力度的忽略, 你可以指定特殊的表達式來配置忽略. 這些表達式從路由位置的頭開始匹配, 意味着前綴應該被包括在匹配表達式中. 忽略表達式影響所有服務和取代任何路由的特殊配置.
zuul: ignoredPatterns: /**/admin/** routes: users: /myusers/**
這個的意思是所有請求, 比如"/myusers/101"的請求會跳轉到"users"服務的"/101", 但包含"/admin/"的請求將不被處理.
Cookies和敏感HTTP頭(Cookies and Sensitive Headers)
在同一個系統中服務間共享請求頭是可行的, 但是你可能不想敏感的頭信息泄露到內部系統的下游。 你可以在路由配置中指定一批忽略的請求頭列表。 Cookies扮演了一個特殊的角色, 因為他們很好的被定義在瀏覽器中, 而且他們總是被認為是敏感的. 如果代理的客戶端是瀏覽器, 則對於下游服務來說對用戶, cookies會引起問題, 因為他們都混在一起。(所有下游服務看起來認為他們來自同一個地方)。
你得小心你的服務設計, 比如即使只有一個下游服務設置cookies, 你都必須讓他們回溯設置所有的調用路線. 當然, 如果你的代理設置cookies和你所有后端服務是同一個系統的一部分, 它可以自然的被簡單分享(例如, 使用spring session去將它們聯系在一起共享狀態). 除此之外, 任何被下游設置的cookies可能不是很有用, 推薦你對於不屬於你域名部分的路由添加(至少)"Set-Cookie"和"Cookie" 到敏感頭. 即使是屬於你的域名的路由, 嘗試仔細思考在允許cookies流傳在它們和代理之間的意義
每個路由中的敏感頭部信息配置按照逗號分隔, 例如:
application.yml zuul: routes: users: path: /myusers/** sensitiveHeaders: Cookie,Set-Cookie,Authorization url: https://downstream
敏感頭部也支持全局設置 zuul.sensitiveHeaders
. 如果在單個路由中設置 sensitiveHeaders
會覆蓋全局 sensitiveHeaders
設置.
注意: 這是sensitiveHeaders
的默認值, 你無需設置除非你需要不同的配置. 注. 這是Spring Cloud Netflix 1.1的新功能(在1.0中, 用戶無法直接控制請求頭和所有cookies).
除了per-route敏感頭以外, 你可以設置一個全局的 zuul.ignoredHeaders
在下游相互調用間去丟棄這些值(包括請求和響應). 如果沒有將Spring Security 添加到運行路徑中, 他們默認是空的, 否則他們會被Spring Secuity初始化一批安全頭(例如 緩存相關). 在這種情況下, 假設下游服務也可能添加這些頭信息, 我希望從代理獲取值.
路由Endpoint(The Routes Endpoint)
如果你使用 @EnableZuulProxy
同時引入了Spring Boot Actuator, 你將默認增加一個endpoint, 提供http服務的 /routes
. 一個GET請求將返回路由匹配列表. 一個POST請求將強制刷新已存在的路由.(比如, 在服務catalog變化的場景中)
路由列表應該自動應答服務登記變化, 但是POST是一種強制立即更新的方案.
窒息模式和本地跳轉(Strangulation Patterns and Local Forwards)
逐步替代舊的接口是一種通用的遷移現有應用程序或者API的方式, 使用不同的具體實現逐步替換它們. Zuul代理是一種很有用的工具, 因為你可以使用這種方式處理所有客戶端到舊接口的請求. 只是重定向了一些請求到新的接口.
Example configuration:
配置樣例:
zuul: routes: first: path: /first/** url: http://first.example.com second: path: /second/** url: forward:/second third: path: /third/** url: forward:/3rd legacy: path: /** url: http://legacy.example.com
在這個例子中我們逐步替換除了部分請求外所有到"legacy"應用的請求. 路徑 /first/**
指向了一個額外的URL. 並且路徑 /second/**
是一個跳轉, 所以請求可以被本地處理. 比如, 帶有Spring注解的 @RequestMapping
. 路徑 /third/**
也是一個跳轉, 但是屬於一個不同的前綴. (比如 /third/foo
跳轉到 /3rd/foo
)
忽略表達式並不是完全的忽略請求, 只是配置這個代理不處理這些請求(所以他們也是跳轉執行本地處理)
通過Zuul上傳文件(Uploading Files through Zuul)
如果你使用 @EnableZuulProxy
, 你可以使用代理路徑上傳文件, 它能夠一直正常工作只要小文件. 對於大文件有可選的路徑"/zuul/*"繞過Spring DispatcherServlet
(避免處理multipart). 比如對於 zuul.routes.customers=/customers/**
, 你可以使用 "/zuul/customers/*" 去上傳大文件. Servlet路徑通過 zuul.servletPath
指定. 如果使用Ribbon負載均衡器的代理路由, 在 處理非常大的文件時, 仍然需要提高超時配置. 比如:
application.yml hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000
注意: 對於大文件的上傳流, 你應該在請求中使用塊編碼. (有些瀏覽器默認不這么做). 比如在命令行中:
$ curl -v -H "Transfer-Encoding: chunked" \
-F "file=@mylarge.iso" localhost:9999/zuul/simple/file
簡單的嵌入Zuul(Plain Embedded Zuul)
你可以運行一個沒有代理功能的Zuul服務, 或者有選擇的開關部分代理功能, 如果你使用 @EnableZuulServer
(替代 @EnableZuulProxy
). 你添加的任何 ZuulFilter
類型 實體類都會被自動加載, 和使用 @EnableZuulProxy
一樣, 但不會自動加載任何代理過濾器.
在以下例子中, Zuul服務中的路由仍然是按照 "zuul.routes.*"指定, 但是沒有服務發現和代理, 因此"serviceId"和"url"配置會被忽略. 比如:
zuul: routes: api: /api/**
匹配所有路徑 "/api/**" 給Zuul過濾器鏈.
關閉Zuul過濾器(Disable Zuul Filters)
在代理和服務模式下, 對於Spring Cloud, Zuul默認加入了一批 ZuulFilter
類. 查閱 the zuul filters package 去獲取可能開啟的過濾器. 如果你想關閉其中一個, 可以簡單的設置 zuul.<SimpleClassName>.<filterType>.disable=true
. 按照約定, 在 filter
后面的包是Zuul過濾器類. 比如關閉 org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter
, 可設置zuul.SendResponseFilter.post.disable=true
.
通過Sidecar進行多語言支持(Polyglot support with Sidecar)
你是否有非jvm語言應用程序需要使用Eureka, Ribbon和Config Server的功能? Spring Cloud Netflix Sidecar 受 Netflix Prana 啟發. 它包含一個簡單的HTTP API去獲取所有注冊的實例信息(包括host和port信息). 你也可以通過依賴Eureka的嵌入式Zuul代理器代理服務調用. The Spring Cloud Config Server可以通過host查找 或Zuul代理直接進入. 非JVM應用程序提供健康檢查實現即可讓Sidecar向eureka同步應用程序up還是down.
為了開啟Sidecar, 創建一個包含 @EnableSidecar
的Springboot應用程序. 這個注解包括了 @EnableCircuitBreaker
, @EnableDiscoveryClient
和 @EnableZuulProxy
. 運行這個程序在非jvm程序的同一台主機上.
配置Sidecar, 添加 sidecar.port
and sidecar.health-uri
到 application.yml
中. 屬性 sidecar.port
配置非jvm應用正在監聽的端口. 這樣Sidecar能夠注冊應用到 Eureka. sidecar.health-uri
是一個非JVM應用程序提供模仿SpringBoot健康檢查接口的可訪問的uri. 它應該返回一個json文檔類似如下:
health-uri-document { "status":"UP" }
這個是Sidecar應用程序application.yml的列子:
server: port: 5678 spring: application: name: sidecar sidecar: port: 8000 health-uri: http://localhost:8000/health.json
DiscoveryClient.getInstances()
方法的API是 /hosts/{serviceId}
. 對於 /hosts/customers
響應的例子是返回兩個不同hosts的實例. 這個API對於非JVM 應用程序是可訪問的. (如果sidecar監聽在5678端口上) http://localhost:5678/hosts/{serviceId}
.
[ { "host": "myhost", "port": 9000, "uri": "http://myhost:9000", "serviceId": "CUSTOMERS", "secure": false }, { "host": "myhost2", "port": 9000, "uri": "http://myhost2:9000", "serviceId": "CUSTOMERS", "secure": false } ]
Zuul自動代理所有eureka中的服務, 路徑為 /<serviceId>
, 也就是customers服務可以通過 /customers
代理到. 非JVM應用程序可以通過 http://localhost:5678/customers
訪問customer服務(假設sidecar監聽在5678端口上).
如果配置服務已經在eureka里注冊, 非JVM應用可以通過Zuul代理訪問到它. 如果ConfigServer的serviceId是 configserver
和Sidecar監聽在5678端口上, 則它可以通過 http://localhost:5678/configserver 訪問到.
非JVM應用可以使用ConfigServer的功能返回YAML文檔. 比如, 調用 http://sidecar.local.spring.io:5678/configserver/default-master.yml 可以返回如下文檔:
client: serviceUrl: defaultZone: http://localhost:8761/eureka/ password: password info: description: Spring Cloud Samples url: https://github.com/spring-cloud-samples
RxJava 與 Spring MVC(RxJava with Spring MVC)
RxJava是一個Java VM實現http://reactivex.io /(Reactive Extensions):是一個使用可觀察數據流進行異步編程的編程接口,ReactiveX結合了觀察者模式、迭代器模式和函數式編程的精華。與異步數據流交互的編程范式
Spring Cloud Netflix提供並支持從Spring MVC Controllers返回rx.Single
對象. 它還支持使用 rx.Observable
對象,可觀察的對象為 Server-sent events (SSE). 如果你的內部api已經使用RxJava這會非常的方便(見< < spring-cloud-feign-hystrix > >為例)。
這里有一些使用rx.Single
的列子:
@RequestMapping(method = RequestMethod.GET, value = "/single") public Single<String> single() { return Single.just("single value"); } @RequestMapping(method = RequestMethod.GET, value = "/singleWithResponse") public ResponseEntity<Single<String>> singleWithResponse() { return new ResponseEntity<>(Single.just("single value"), HttpStatus.NOT_FOUND); } @RequestMapping(method = RequestMethod.GET, value = "/throw") public Single<Object> error() { return Single.error(new RuntimeException("Unexpected")); }
如果你有一個 Observable
, 而不是單一的, 你可以使用.toSingle()
或 .toList().toSingle()
. 下面是些例子:
@RequestMapping(method = RequestMethod.GET, value = "/single") public Single<String> single() { return Observable.just("single value").toSingle(); } @RequestMapping(method = RequestMethod.GET, value = "/multiple") public Single<List<String>> multiple() { return Observable.just("multiple", "values").toList().toSingle(); } @RequestMapping(method = RequestMethod.GET, value = "/responseWithObservable") public ResponseEntity<Single<String>> responseWithObservable() { Observable<String> observable = Observable.just("single value"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(APPLICATION_JSON_UTF8); return new ResponseEntity<>(observable.toSingle(), headers, HttpStatus.CREATED); } @RequestMapping(method = RequestMethod.GET, value = "/timeout") public Observable<String> timeout() { return Observable.timer(1, TimeUnit.MINUTES).map(new Func1<Long, String>() { @Override public String call(Long aLong) { return "single value"; } }); }
如果你有一個流端點和客戶端,SSE可能是一個選項。使用 RxResponse.sse()
將rx.Observable
轉換到Spring 的SseEmitter
. 以下是一些例子:
@RequestMapping(method = RequestMethod.GET, value = "/sse") public SseEmitter single() { return RxResponse.sse(Observable.just("single value")); } @RequestMapping(method = RequestMethod.GET, value = "/messages") public SseEmitter messages() { return RxResponse.sse(Observable.just("message 1", "message 2", "message 3")); } @RequestMapping(method = RequestMethod.GET, value = "/events") public SseEmitter event() { return RxResponse.sse(APPLICATION_JSON_UTF8, Observable.just( new EventDto("Spring io", getDate(2016, 5, 19)), new EventDto("SpringOnePlatform", getDate(2016, 8, 1)) )); }
指標: Spectator, Servo, and Atlas(Metrics: Spectator, Servo, and Atlas)
當Spectator/Servo 和 Atlas一起使用時, 提供一個接近實時操作的平台.
Spectator 和 Servo 的metrics的標准收集庫. Atlas 是 Netflix 的一個后端指標 管理多維時間序列數據。
Servo為netflix服務多年,仍然是可用的,但逐漸被淘汰,取而代之的是Spectator ,僅僅是為了與java8工作, Spring Cloud Netflix 兩者都支持, 但使用java8的推薦使用Spectator.
Dimensional vs. Hierarchical Metrics
Spring Boot Actuator指標等級和指標是分開的,這些名字常常遵循命名約定,嵌入key/value attribute 隔着時間的名稱。考慮以下指標為兩個端點,root 和 star-star:
{ "counter.status.200.root": 20, "counter.status.400.root": 3, "counter.status.200.star-star": 5, }
第一個指標為我們提供了一個規范化的成功請求的時間單位根節點. 歸一化計算對根節點成功請求的時間. 但是如果系統有20個節點和你想要一個對所有節點成功請求的計數呢? 分級指標后端將允許您指定一個 counter.status.200.
閱讀所有20個指標和聚合的結果.或者,你可以提供一個 HandlerInterceptorAdapter
攔截和記錄所有成功的請求無論節點 counter.status.200.all
, 但是現在你必須寫20多個不同的指標。同樣的如果你想知道所有節點成功請求服務的總數, y您可以指定一個通配符 counter.status.2
.*
.
即使后端參次指標支持它的存在,命名一致性是很困難的. 特別是這些標簽的位置名稱字符串可以隨着時間的推移,打破查詢. 例如, 假設我們為HTTP方法上面的分級指標添加一個額外的dimension . 然后“counter.status.200.root”成為“counter.status.200.method.get.root”等等。我們的“counter.status.200 *’突然不再具有相同的語義。 此外 , 如果新dimension不是整個代碼庫應用均勻,某些查詢可能會變得不可能。這很快就會失控。
Netflix metrics 標記 (又名 dimensional). 每個指標都有一個名字,但這一命名metric 可以包含多個數據和“標簽”鍵/值對,允許更多查詢的靈活性. 事實上, 數據本身是記錄在一個特殊的標記中的.
記錄Netflix Servo 或 Spectator, 一個計時器根節點上包含4統計和狀態碼,Spring Boot Actuator’s 計數的統計數據時相同的. 迄今為止如果我們遇到 HTTP 200 and 400 ,將會有8種可用數據點
{ "root(status=200,stastic=count)": 20, "root(status=200,stastic=max)": 0.7265630630000001, "root(status=200,stastic=totalOfSquares)": 0.04759702862580789, "root(status=200,stastic=totalTime)": 0.2093076914666667, "root(status=400,stastic=count)": 1, "root(status=400,stastic=max)": 0, "root(status=400,stastic=totalOfSquares)": 0, "root(status=400,stastic=totalTime)": 0, }