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,
}
相关文章
相关标签/搜索