spring cloud

  經過自動配置和綁定到Spring環境和其餘Spring編程模型慣例,爲Spring Boot應用程序提供Netflix OSS集成。 經過幾個簡單的註釋,您能夠快速啓用和配置應用程序中的常見功能模塊,並使用久經考驗的Netflix組件構建大型分佈式系統。 提供的功能模塊包括服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul)和客戶端負載均衡(Ribbon)。html

服務發現:Eureka客戶端(Service Discovery: Eureka Clients)

  服務發現是microservice基礎架構的關鍵原則之一。試圖着手配置每一個客戶端或某種格式的約定能夠說是很是困難的和很是脆弱的。Eureka是Netflix服務發現的一種服務和客戶端。這種服務是能夠被高可用性配置的和部署,而且在註冊的服務當中,每一個服務的狀態能夠互相複製給彼此。前端

註冊到Eureka(Registering with Eureka)

  當一個客戶端註冊到Eureka,它提供關於本身的元數據(諸如主機和端口,健康指標URL,首頁等)Eureka經過一個服務從各個實例接收心跳信息。若是心跳接收失敗超過配置的時間,實例將會正常從註冊裏面移除。java

eureka 客戶端例子:react

@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服務端。 例如:git

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

"defaultZone"是一個神奇的字符串回退值,它提供了服務的URL給任何客戶端,而這不意味優先級。 (i.e. 是一個經常使用的默認值).github

默認應用名(服務ID),物理主機地址和非安全端口, 分別從環境的 ${spring.application.name}, ${spring.application.name}${server.port} 獲取。web

@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」實例。spring

因爲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。 例子:

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或其它平臺上跑的話),那麼你將要確保應用可以攔截和處理代理轉發的頭信息。若是它明確配置有'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。例如:

application.yml
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的父依賴,如:

build.gradle
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)在標準模式,您可能更傾向於關閉客戶端的行爲,因此它不能保持失敗重試和從新找回它的那些節點。如:

application.yml (Standalone Eureka Server)
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.preferIpAddresstrue以便當應用註冊到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.portturbine.stream.port 任一個,若是你使用了 spring-boot-starter-webspring-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 客戶端默認配置的服務器列,而且能夠提供這樣的配置

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

禁用Eureka使用Ribbon(Example: Disable Eureka use in Ribbon)

設置屬性ribbon.eureka.enabled = false, 使用Ribbon時禁用Eureka

application.yml
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.Encoderfeign.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 不是必須的,但如今是必須的.

佔位符支持 nameurl 屬性.

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

Spring Cloud Neflix Feign 默認提供瞭如下 Bean (BeanType beanName: ClassName):

  • Decoder feignDecoder: ResponseEntityDecoder (which wraps a SpringDecoder)

  • 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");
    }
}

能夠替換SpringMvcContractfeign.Contract.Default, 並增長一個 RequestInterceptorRequestInterceptor 中去.

默認配置可在 @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
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.LevelFULL:

@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.maxTotalConnectionszuul.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提取變量, 而後注入到路由表達式中.

ApplicationConfiguration.java
@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 能夠關閉這個功能). 你也能夠在指定服務中關閉這個功能, 例如:

application.yml
 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"服務.

若是須要更細力度的忽略, 你能夠指定特殊的表達式來配置忽略. 這些表達式從路由位置的頭開始匹配, 意味着前綴應該被包括在匹配表達式中. 忽略表達式影響全部服務和取代任何路由的特殊配置.

application.yml
 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:

配置樣例:

application.yml
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"配置會被忽略. 好比:

application.yml
 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-uriapplication.yml 中. 屬性 sidecar.port 配置非jvm應用正在監聽的端口. 這樣Sidecar可以註冊應用到 Eureka. sidecar.health-uri 是一個非JVM應用程序提供模仿SpringBoot健康檢查接口的可訪問的uri. 它應該返回一個json文檔相似以下:

health-uri-document

{
  "status":"UP"
}

這個是Sidecar應用程序application.yml的列子:

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} .

/hosts/customers
[
    {
        "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,
}
相關文章
相關標籤/搜索