1. 程式人生 > >Spring Cloud Netflix中文文件翻譯筆記

Spring Cloud Netflix中文文件翻譯筆記

Spring Cloud Netflix

1.2.2.RELEASE

Spring Cloude Netflix這個專案提供Netflix OSS和Spring Boot apps整合,通過自動配置和繫結到Spring環境和其他Spring程式設計模組。通過一些簡單的註解配置,你可以很快的使用和配置應用的模組,和構建大型分散式高可用的Netflix元件。這裡提供包括服務發現(Service Discovery Eureka),斷路器或熔斷器(Circuit Breaker Hystrix),智慧路由(intelligent routing Zuul)和 負載均衡(Ribbon)。

Service Discovery:Eureka Client

服務發現是微服務架構依賴的一個關鍵模組。嘗試去管理每個客戶端的配置或者某些形式的約定是非常困難而且不穩定。Eureka是Netflix服務發現的服務端和客戶端。服務端可義高可用的配置和部署,每個伺服器可以相互複製已註冊的服務的狀態。

如何引入Eureka Client

如何在你的專案中使用Eureka Client?使用org.spring.cloud和artifactid spring-cloud-starter-eureka。 請瀏覽網頁spring cloud project page去檢視詳情如何配置你的系統使用當前的spring cloud版本。

Eureka註冊

當一個客戶端註冊到Eureka,它就提供了一些自己的元資料,比如host和port,可用的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);
}

}

(i.e 完全普通的spring boot app)。在這個例子中我們顯示的使用用@EnableEurekaClient(explicitly)。但是僅可用的Eureka你可以使用@EnableDiscoveryClient。我們必須通過配置去定位一個Eureka服務。比如:

application.yml
eureka:
    client:
        srviceUrl:
            defaultZone:http://localhost:8761/eureka/

這個 “defaultZone” 是神奇的字串值,他提供一個服務地址供其他任何客戶端使用,沒有明確的優先順序。(i.e. 他是預設可用的)。

這個預設的應用名稱(service ID),主機和非安全的埠,分別是 spring.application.name{server.port}

@EnableEurekaClient 使你的應用同時變成一個 Eureka 例項(i.e. it registers itself)和 一個客戶端 (i.e. 它可以查詢註冊中心去定位其他服務)。這個例項行為通過 eureka.instance.* 驅動,但是預設的就夠了,如果確保你的應用有一個 spring.application.name (這是個預設的Eureka服務ID,或則VIP)。

Eureka Server驗證

如果一個eureka客戶端嵌入了認證資訊在 “eureka.client.serviceUrl.defaultZone”中,那麼HTTP基礎身份驗證會自動新增到eureka客戶端(比如,http://user:[email protected]:8761/eureka)。更多的複雜的配置你需要建立一個 型別為DiscoveryClientOptionalArgs 的 @Bean,而且注入一個ClientFilter例項,這些都會被應用到客戶端到服務端之間的呼叫上。
注意: 由於Eureka的一個限制是不可能支援每個伺服器基本授權認證,所以只被發現的第一組會被使用。

狀態頁和健康指示器

Eureka的狀態頁和健康指示器預設分別為“/info”和“/health”,是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

這些連結暴露了客戶端消費的元資料,和在一些情況下決定是否傳送請求到你的應用,所以非常有用如果資料精確。

註冊安全應用

如果你的app想通過HTTPS連線,你需要設定兩個標記在EurekaInstanceConfig,即分別是,eureka.instance.[nonSecurePortEnabled,securePortEnabled]=[false,true]。這會使Eureka例項明確的使用安全通道。spring cloud DiscoveryClient總是返回一個https://;服務的URI配置成這種方式,Eureka例項資訊將會有一個安全的檢查URL。
因為Eureka的內部工作方式,它將繼續推送一個非安全的URL的狀態和主頁,除非你還覆蓋那些宣告。你可以使用佔位符娶配置eureka例項的url。 例子:

application.yml
eureka:
    instance:
        statusPageUrl:https//${eureka.hostname}/info
        healthCheckUrl:https//${eureka.hostname}/health
        homePageUrl:https//${eureka.hostname}/

(注意:eureka.hostname使Springplaceholders{eureka.instance.hostName})

注意:如果你的應用在慢於一個代理啟動執行,並且SSL終端在代理裡面(如:如果你的應用作為一個服務在Cloud Foundry或其它平臺上跑的話),那麼你將要確保應用能夠攔截和處理代理轉發的頭資訊。如果它明確配置有’X-Forwarded-*`這類頭資訊的情況下,在一個Spring Boot應用裡面內嵌的Tomcat容器自動處理的。出現這個錯誤的原因,是因為你的應用程式提供的連結本身弄錯了。(錯誤的主機,埠,協議)。

Eureka的健康檢查

預設的,Eureka使用客戶端心跳確定一個客戶端是否線上。除非指定了其他的
Discovery Client不會傳播當前的spring boot actuator健康檢查狀態。這就是說只要成功註冊了Eureka就會一直通知應用是在“UP”狀態。這個動作可以被改變通過使用Eureka health checks,這種傳送應用狀態給Eureka的行為將觸發Eureka的健康檢查,因此其他每個應用程式在其他狀態下不會給應用程式傳送通訊然後才‘UP’。

application.yml
eureka:
    client:
        healthcheck:
            enabled:true

警告:eureka.client.healthcheck.enabled=true應該只在application.yml中被設定。如果設定在bootstrap.yml將會引起不可預知的影響比如註冊eureka出現unknown status。

如果你需要更多的配置關於health checks,你可能考慮實現你自己的com.netflix.appinfo.HealthCheckHandler。

Eureka例項和客戶端的元資料

這裡值得我們花一點時間去理解eureka元資料是如何工作的,所以你可以在平臺上使用它找點感覺。這裡有一個標準的元資料比如hostname,ip address,port numbers,status page和health check。他們被髮布到服務註冊,而且被客戶端聯絡服務端通過一種直接的方式。另外的,元資料可以被新增到例項註冊在eureka.instance.metadataMap,而且這會被遠端客戶端容易訪問,但是通常的不要去改變客戶端的行為,除非你知道了元資料的意義。有一些特殊情況下的描述,spring cloud 已經分配好了有意義的元資料對映。

在Cloudfoundry使用eureka在cloudfoundry

Cloudfoundry有一個全域性的根路由器,因此所有相同的app都有一樣的hostname(在其他PaaS解決方案也是類似的架構)。這不妨礙我們使用Eureka(推薦的,或者強制的依賴你的平臺),你需要明確的設定hostname和post(secure of non-secure)以便他們使用路由器。你可能也想使用例項元資料,以便你可以區分例項在客戶端(在一個定製的負載均衡器)。預設的,eureka.instance.instanceId 是 vcap.application.instance_id。例如:

application.yml
eureka:
    instance:
        hostname:${vcap.application.uris[0]}
        nonSecurePort:80

根據安全規則的方式設定你的Cloudfoundry例項,你可能想註冊和使用主機的ip address去直接進行服務到服務之間的呼叫。這個特性目前還不能在 Pivotal Web Services。

在AWS上使用Eureka

如果應用準備釋出到AWS,eureka例項需要配置成Amazon aware,這個可以通過以下方式定製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

Netflix Eureka例項是一個身份證,等於其域名註冊(i.e.每個host一個service)。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部署的服務例項,random.value設定會保證例項唯一。在cloudfoundry中,vcap.appliation.instance_id在spring boot中是自動增長的,所以random.value並不必須。

使用EurekaClient

如果你有一個 @EnalbeDicoveryClient(或@EurekaClient)的應用,你可以使用它從Eukeka Server去發現服務例項。一種方式是使用原生的com.netflix.discovery.EurekaClient(而不是spring cloud DiscoveryClient)。

@Autowired
private EurekaClient discoveryClient;

public String serviceUrl(){
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES",false);
    return instance.getHomePageUrl();   
}

提示:不要使用eurekaClient在@PostConstruct方法或者其他@Scheduled方法(或者任何ApplicationContext還沒有啟動的其他地方)。它初始化在一個SmartLifecycle(phase=0的條件)所以想盡早的使用就必須在另一個更高phase的SmartLifecycle上使用。

原生Netflix EurekaClient的替代品

你不必使用原始的Netflix EurekaClient,通常使用一個包裝器會更方便。Spring Cloud提供Fegin(REST客戶端構建器)和Spring RestTemplate去使用Eureka service的邏輯識別符號替代物理URLS。配置帶固定的物理伺服器集合的Ribbon,你可以簡單的設定.ribbon.listOfServers的物理伺服器地址(或者hostname)集合,並使用逗號分隔符分開,是客戶端的ID。

你也可以使用 org.springframework.cloud.client.discoveryClient,它提供了一個簡單的API而不是特定於Netflix。

@Autowired
private DiscoveryClient discoveryClient;

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

為什麼註冊一個服務很慢

成為一個例項也包含一個到註冊中心的心跳(serviceUrl),預設30秒。一個服務不會被客戶端發現,直到例項、服務端和客戶端全都擁有相同的元資料在它們的快取裡面(這可能還需要3次心跳)。你可以改變這個週期通過 eureka.instance.leaseRenewalIntervalInSeconds,這會加速client連線到其他的services。在生產環境或許最好保持預設值,因為server有些本地的計算去確保假設的更新週期(make assumptions about the lease renewal period)。

服務發現:Eureka Server

如何引入Eureka Server

引入Eureka Server到你的專案你需要使用org.springframework.cloud和spring-cloud-starter-eureka-server。訪問spring cloud project page檢視更多詳情。

如何執行Eureka Server

eureka server示例:

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

Server有一個UI主頁,和HTTP API端點提供平常的功能,地址:/eureka/*

TIP:由於Gradle的依賴解析規則,它沒有父bom依賴的特性,簡單的spring-cloud-starter-eureka-server依賴會引發錯誤。為了補救,必須新增Spring Boot的Gradle外掛,而且引入Spring cloud starter的父bom。like so:

build.gradle
buildscript{
    dependencies{
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE")
    }   
}

apply plugin: "spring-boot"

dependencyManagement{
    imports{
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"  
    }
}

高可用,Zones和Regions

Eureka server沒有一個後端的儲存,但是服務例項在註冊裡面全都得傳送心跳去保持註冊更新(在記憶體裡操作)。Clients同樣有一個erureka註冊中心的記憶體快取(所以他們不是去為每一個到service的請求都去一次註冊中心)。
預設的,每一個Eureka server同樣是一個Eureka client,而且需要(至少一個)service url去定位同伴。如果你沒有提供,service同樣會執行和工作,但他會產生很多無法與其他同伴註冊的錯誤日誌。

也可檢視 客戶端Riboon支援,Zones 和 Regions。

獨立模式

client和server結合的快取和心跳會使一個單機的Eureka server很好的彈性失敗(fairly resilient to failure),有一些監視和elastic runtime會使它保持活躍(比如:cloud foundry)。在獨立模式下,,您可能更傾向於關閉客戶端的行為,所以它不能保持失敗重試和重新找回它的那些節點。如:

application.yml
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到每一個同伴讓它可以工作。

applicatin.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檔案,它可以用來運行同樣的兩個server在兩個hosts上(peer1 和 peer2),兩個不同的Spring profiles。你可以使用這個配置測試單機上的兩個對等例項(沒有多少價值在生產環境中這樣做)。通過修改/etc/hosts改變hostnames。事實上,eureka.instance.hostname並不需要如果你執行你自己直到的主機名的機器(它預設使用java.net.InetAddress檢查)。

你可以新增多個例項到一個系統上,而且只要他們都彼此連線到至少一個邊緣,他們將會同步註冊資訊。If the peers are physically separated (inside a data centre or between multiple data centres) then the system can in principle survive split-brain type failures.

Prefer IP Address

一些情況,相比hostname,Eureka更好的是使用IP Adresses。設定 eureka.instance.preferIpAddress=true,當應用註冊到eureka上的時候,他們將使用IP Address替代hostname。

電子斷路器:Hystrix Clients

Netflix建立了一個庫叫Hystrix實現了電子斷路器模組。在一個微服務架構中它一般有多個服務呼叫層。
Hystrix

一個底層的服務錯誤會引起級聯錯誤一直反饋到使用者。當呼叫一個特定的服務到達一定的閥值後(20 failures in 5 seconds is the default in Hystrix),迴路開啟然後呼叫也不會成功。一些錯誤情況下可以由程式設計師提供
open circuit a fallback。
fallback

Having an open circuit stops cascading failures and allows overwhelmed or failing services time to heal. The fallback can be another Hystrix protected call, static data or a sane empty value. Fallbacks may be chained so the first fallback makes some other business call which in turn falls back to static data.

如何引入Hystrix

在你的專案中通過 org.springframework.cloud 和 spring-cloud-starter-hystrix 引入Hystrix。檢視詳情並設定你的系統使用當前的spring cloud Release
例如:

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

@Component
public class StoreIntegration{
    @HystrixCommand(fallbakMethod="defaultStores")
    public Object getStore(Map<String,Object> parameters){
        //do stuff that might fail
    }
    public Object defaultStores(Map<String,Object> paramters) {
        return /*something useful*/;
    }
}

Netflix的普通釋出庫叫Javanica提供了@HystrixCommand註解。Spring Cloud使用註解自動適配spring bean使用代理去連線到Hystrix斷路器。斷路器計算何時開啟和關閉斷路,並決定在失敗的情況下做什麼。

配置@HystrixCommand你可以使用commandProperties屬性,它有@HystrixProperty的註解列表。通過這裡檢視更多詳情.訪問Hystrix wiki檢視更多可用的屬性。

Propagating the Security Context or using Spring Scopes

如果你想把本地執行緒上下文傳播到@HystrixCommand,預設的宣告將不可用因為它是在一個執行緒池中被啟動的。你可以選擇讓Hystrix使用同一個執行緒,通過一些配置,或直接寫在註解上,通過使用isolation strategy屬性。例如:

@HystrixCommand(fallbackMethod="stubMyService",
    commandProperties={@HystrixProperty(name="execution.isolation.strategy",value="SEMAPHORE")})

同樣的方式適用於如果你用@SessionScope 或者 @RequestScope。你應該知道什麼時候去做這件事因為有些執行時異常報找不到scoped上下文。

你還可以選擇設定 hystrix.shareSecurityContext 屬性為true。設定這個值會自動配置一個Hystrix兵法策略會把securityContext從主執行緒傳輸到你使用的Hystrix command。Hystrix does not allow multiple hystrix concurrency strategy to be registered so an extension mechanism is available by declaring your own HystrixConcurrencyStrategy as a Spring bean. Spring Cloud will lookup for your implementation within the Spring context and wrap it inside its own plugin

Health Indicator

斷路器的狀態同樣暴露在/health端點上。

{
"hystrix": {
    "openCircuitBreakers": [
        "StoreIntegration::getStoresByLocationLink"
    ],
    "status": "CIRCUIT_OPEN"
},
"status": "UP"
}

Hystrix Metrics Stream

使用Hystrix metrics stream需要引入依賴 spring-boot-starter-actuator。這會暴露/hystrix.stream作為一個管理端點。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Circuit Breaker: Hystrix Dashboard

Hystrix的主要好處就是她收集了關於每個HystrixCommand的指標。Hystrix儀表盤用一種高效的方式展示了斷路器的健康資料。
Dashboard

如何引入Hystrix儀表盤

…org.springframework.cloud and artifact id spring-cloud-starter-hystrix-dashboard…Spring Cloud Project page

在Spring boot main class上加@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=,如果叢集的名稱是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 名稱

客戶端負載均衡:Ribbon

Ribbon是一個客戶端的負載均衡器,可以提供很多HTTP和TCP的控制行為。Feign已經使用了Ribbon,所以如果你使用了@FeignClient,Riboon也同樣被應用了。

Ribbon核心的概念是named client。每個負載均衡器都是共同體的一部分,可以一起執行去連線遠端伺服器,你會給你的應用設定一個名字(比如使用@FeignClient註解)。Spring Cloud creates a new ensemble as an ApplicationContext on demand for each named client using RibbonClientConfiguration. This contains (amongst other things) an ILoadBalancer, a RestClient, and a ServerListFilter.

如何引入Ribbon

org.springframework.cloud and artifact id spring-cloud-starter-ribbon. 檢視詳情 Spring Cloud Project page

定製Ribbon Clietn

你可以配置一些Ribbon client的屬性在外部的屬性檔案裡(application.properties/yml),如.ribbon.*,這個和Netflix APIS本身沒有什麼不同。本機選項可以被檢查使用CommonClientConfigKey等靜態欄位。

Spring cloud還允許你完全控制客戶端通過宣告額外的配置,使用@RibbonClient(位於RibbonClientConfiguration的頂部)。
例如:

@Configuration
@RibbonClient(name="foo",configuration=FooConfiguration.class)
public class TestConfiguration{
}

In this case the client is composed from the components already in RibbonClientConfiguration together with any in FooConfiguration (where the latter generally will override the former).

警告:FooConfiguration已經設定為@Configuration,但是注意它不是@ComponentScan在主程式上下文,另外它會被所有的@RibbonClients共享。如果你使用了@ComponentScan(或者@SpringBootApplication)你需要採取措施去避免引入。(例如把他分割開來,不要重疊包,或者指定明確的包路徑在@ComponentScan)。

Spring Cloud Netflix預設為Ribbon提供瞭如下beans(BeanType beanName:ClassName):
* IClientConfig ribbonClientConfig: DefaultClientConfigImpl
* IRule ribbonRule: ZoneAvoidanceRule
* IPing ribbonPing: NoOpPing
* ServletList ribbonServerList: ConfigurationBasedServerlList
* ServerListFilter ribbonServerListFilter:
* ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer

建立一個這些型別的一個Bean放置到@RibbonClient配置類中(就像上面的FooConfiguration一樣),它允許你重寫每一個bean的描述。例如:

@Configuration
public class FooConfiguation {
    @Bean
    public IPing ribbonPing(IClientConfig config){
        return new PingUrl();
    }
}

這裡使用PingUrl替換了NoOpPing。

Customizing the ribbon client using properties

從1.2.0版本開始,sping cloud netflix支援使用配置檔案的方式定製RibbOn clients並且與文件相容 Ribbon documentation

這允許你在不同環境中,改變啟動時的行為。

這些屬性都列在下面,並且他們必須使用 .ribbon.作為字首。
* NFLoadBalancerClassName: should implement ILoadBalancer
* NFLoadBalanceerRuleClassName: should implement IRuld
* NFLoadBalancePingClassName: should implement IPing
* NIWSServerListClassName: should implement ServerList
* NIWServerListFilterClassName: should implement ServerListFilter

注意: 類中定義了這些屬性將會優先於@RibbonClient(configuration=MyRibbonConfig.class),預設的是Spring Cloud Netflix提供了。

給服務名為user設定IRule,你可以如下設定:

application.yml
user:
    ribbon:
        NFLoadBalancerRuleClassName:com.netflix.loadbalancer.WeightedResponseTimeRule

從 Ribbon documentation 檢視Ribbon的實現。

Using Ribbon with Eureka

當Eureka跟Ribbon結合使用的時候(都在classpath),ribbonServerList會被一個外部的DiscoveryEnabledNIWServerList重寫,它填充了服務懶得列表從Eureka中。它同樣使用了NIWDiscoveryPing替換了IPing,它讓Eureka去確定一個server是否啟動。serverList預設使用的是DomainExtractingServerList,目的是讓物理元資料用於負載均衡器而不是AWS AMI(這是Netflix依賴的)。預設srverlist會構造”zone”資訊提供給例項使用(遠端客戶端設定eureka.instance.metadataMap.zone),如果沒有設定它可以使用域名伺服器的主機名作為區域代理(如果approximateZoneFromHostname被設定了)。一旦zone資訊可用,它也會被用在ServerListFilter。預設它會用來定位一個客戶端在同一個zone,因為預設的是ZonePrefeerenceServerListFilter。client的zone預設跟遠端例項的一樣。i.e. eureka.instance.metadataMap.zone。

注意:正統的“archaius”方式設定client zone是通過配置屬性”@zone”,Sping Cloud將會優先使用這個設定(它會去引用YAML的配置)。

注意:If there is no other source of zone data then a guess is made based on the client configuration (as opposed to the instance configuration). We take eureka.client.availabilityZones, which is a map from region name to a list of zones, and pull out the first zone for the instance’s own region (i.e. the eureka.client.region, which defaults to “us-east-1” for comatibility with native Netflix).

Example:How to Use Ribbon Without Eureka

Eureka是一個方便的方式去抽象遠端服務發現,所以你不需要在客戶端硬編碼他們的URLS。但是如果你不想用eureka,Ribbon和Feign仍然可用。假設你已經在“stores”定義了@RibbOnClient,而且沒有使用Eureka(沒有在classpath中)。Ribbon client預設要配置server list,你可以提供配置像這樣:

application.yml
stores:
    ribbon:
        listOfServers: example.com.google.com

Example:Disable Eureka use in Ribbon

設定property ribbon.eureka.enable=false將會明確的讓Eureka的ribbon失效。

application.yml
ribbon:
    eureka:
        enabled: false

Using the Ribbon API Directly

你也可以直接使用 LoadBalancerClient。例如:

public class MyClass {
    @Autowired
    private LoadBalancerClient loadBalancer;

    public void dostuff(){
        ServiceInstance instance = loadBalancer.choose("stors");
        URI storeUri = URI.create(String.format("httP://%s:%s",instance.getHost(),instance.getPort()));
        //... do something with the URI
    }
}

Declarative REST Client:Feign

Feign是一種宣告式的web service client。它讓web service變得更容易。使用Feign你只需要建立一個介面並且寫上註解。它提供插拔式的Feign註解和JAX-RS註解支援。Feign同樣提供插拔式的編碼解碼器。Spring Cloud添加了Spring MVC的註解支援,在Spring web中預設使用相同的HttpMessageConverters。spring cloud集成了Ribbon 和 Eureka去提供負載均衡。

How to include Feign

org.springframework.cloud and artifact id spring-cloud-starter-feign。Spring Cloud Project page

Example spring boot app:

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@EnableFeignClients
public class Application {

    public stati void main(String[] args){
        SpringApplication.run(Application.class,args);
    }
}

StoreClient.java

@FeignClient("sotes")
public interface StoreClient{
    @RequestMapping(method=RequestMethod.GET,value="/stores")
    List<Store> getStores();

    @RequestMapping(method=RequestMethod.POST,value="/stores/{storeId}",consumes="appliation/json")
    Store update(@PathVariable("storeId") Long storeId,Store store);
}

在@FeignClient註解裡是一個任意的服務端的名字(比如 “store”),用於建立一個Ribbon負載均衡。你也可以指定一個URL,通過使用url屬性(絕對值或者只是個hostname)。應用程式上下文中的bean的名稱是介面的完全限定名稱。一個別名同樣被建立就是 “name”屬性上附加上“FeignClient”。看上面的列子,@Qualifire(“storesFeignClient”)可以用來引用bean,如果你想改變預設@Qualifier值,這可以在@FeignClient使用qualifier值。

Ribbon client會發現“stores”服務的實體地址。如果你的應用是Eureka client然後Eureka註冊中心會決定service的地址。如果你不想使用Eureka,你可以簡單的配置一個 server list 在你的外配配置中。

Overriding Feign Defaults

Sping cloud Feign支援的一個核心概念就是宣告的客戶端。每一個Feign client是整體的的一部分一起通過遠端伺服器聯絡,使用@FeignClient註解指定一個整體使用的名字。Sping cloud為每一個使用FeignClientConfiguration宣告的客戶端建立一個新的ApplictionContxt。這包括(除去其他東西)feign.Decode,feign.Encoder和feign.Contract。

Spring cloud提供通過@FeignClient.新增添額外的配置的方法讓你完全控制feign client。例如:

@FeignClient(name="stores", configuration=FooConfiguration.class)
public interface StoreClient{
}

在這個例子中,FeignClientsConfiguration已經有的和FooConfiguration自定義的共同組成了client(後者會覆蓋先者)。

警告: FooConfiguration必須是@Configuration,但是注意不能在@CompinentScan中,否則將被用於每個@FeignClient。如果你使用@ComponentScan(或@ SpringBootApplication),你需要採取一些措施來避免它被列入(比如把它放在一個單獨的,非重疊的包,或者指定包在@ComponentScan明確掃描)。

注意:該 serviceId 已經過時,建議使用 name 屬性

警告:以前,使用 url 屬性,則 name 不是必須的,但現在是必須的.

name 和 url 屬性都支援佔位符。

@FeignClient(name="${feign,name}",url="${feign.url}")
public interface StoreClient{
}

Spring cloud netflix預設給feign提供如下bean(BeanType beanName:ClassName)
* Decoder feignDecoder: RespinseEntityDecoder(包裝了SpringDeccoder)
* Encoder fergnEncoder: SpringEncoder
* Logger feignLogger: SLF4JLogger
* contract feignContract:SpringMvcContract
* Feign.Builder feignBuilder: HystrixFeign.Builder
* Client feignClient:如果Ribbon可用就是loadBalancerFeignClient,否則預設feign client。

OkHttpClient和ApacheHttpClient feign clients可以通過分別設定fiegn.okhttp.enable 或者 feign.httpclient.enable為true,並且新增到classpath。

Spring cloud netflix預設沒有提供一下bean,但是仍然可以從上下文中查詢這些bean並建立feign client:
* Logger.Level
* Retryer
* ErrorDecoder
* Request.options
* Collection

建立這些型別的一個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 clients。

Creating Feign Clients Manually

在一些情況下可能需要自定義Feign clients但是不能用以上的方法。所以你可以使用Feign Builder API建立clients。下面是一個例子,建立了兩個相同介面的client但是用配置了分開的攔截器。

@Import(FeignClientsConfiguration.class)
class FooController {

private FooClient fooClient;

private FooClient adminClient;

@Autowired
public FooController(
        ResponseEntityDecoder decoder, SpringEncoder encoder, Client client) {
    this.fooClient = Feign.builder().client(client)
            .encoder(encoder)
            .decoder(decoder)
            .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
            .target(FooClient.class, "http://PROD-SVC");
    this.adminClient = Feign.builder().client(client)
            .encoder(encoder)
            .decoder(decoder)
            .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
            .target(FooClient.class, "http://PROD-SVC");
    }
}

注意:在這個例子中,FeignClientsConfiguration.class是Spring Cloud Netflix預設提供的配置。

PROD-SVC是我們提供的服務名稱,會接收相應的客戶端的請求。

Feign Hystrix Support

如果Hystrix在classpath中,預設Feign用熔斷器包裝所有方法。返回一個 com.netflix.hystrix.HystrixCommand 也是可用的。這允許你以被動模式使用(使用.toObservable()或者.observer())或者 非同步呼叫(.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");
    }
}

如果一個請求需要觸發回退,可以使用fallbackFactory屬性替換@FeignClient。

@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello iFailSometimes();
}

@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
    @Override
    public HystrixClient create(Throwable cause) {
        return new HystrixClientWithFallBackFactory() {
            @Override
            public Hello iFailSometimes() {
                return new Hello("fallback; reason was: " + cause.getMessage());
            }
        };
    }
}

警告:There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work. Fallbacks are currently not supported for methods that return 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 {

}
<