此项目将Netflix OSS的组件集成到Spring环境中,这样就可以快速地通过注解构建分布式系统。Netflix组件包括服务发现(Eureka),熔断器(Hystrix),智能路由(Zuul)和客户端的负载均衡(Ribbon)。


1.服务发现:Eureka客户端


1.1.怎么去引入Eureka客户端

使用group IDorg.springframework.cloud和artifact IDspring-cloud-starter-netflix-eureka-client在你的项目中引入Eureka客户端。
版本的选择需要依据下面表中的对应关系。

表1.Spring Cloud的版本和Spring Boot的版本对应关系

Release Train Boot Version
Hoxton 2.2.x, 2.3.x (Starting with SR5)
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

Spring Cloud Dalston, Edgware, and Finchley have all reached end of life status and are no longer supported.


1.2.服务注册

当一个客户端注册到了Eureka,会向Eureka提供一些元数据,比如host,port,健康检查url,主页和一些其他信息。
Eureka会从每一个实例中收到心跳消息,如果在配置的一定时间内没有收到某个客户端服务的心跳,Eureka会将其从注册中心中移除。

下面是一个最简单的Eureka客户端服务应用:

  1. @SpringBootApplication
  2. @RestController
  3. public class Application {
  4. @RequestMapping("/")
  5. public String home() {
  6. return "Hello world";
  7. }
  8. public static void main(String[] args) {
  9. new SpringApplicationBuilder(Application.class).web(true).run(args);
  10. }
  11. }

这看起来就像是一个普通的Spring Boot应用,但如果你的classpath中包含了spring-cloud-starter-netflix-eureka-client的依赖,那么你的应用就会自动注册到Eureka注册中心。

为了定位到Eureka服务端,需要在配置文件中进行配置:
application.yml

  1. eureka:
  2. client:
  3. serviceUrl:
  4. defaultZone: http://localhost:8761/eureka/

注意: defaultZone属性是大小写敏感的,必须是驼峰命名。因为ServiceUrl是一个Map

在Eureka中,服务默认的服务id,域名和端口的取值是服务中对应的三个配置:
${spring.application.name}${spring.application.name}${server.port}

注册到Eureka中的服务可以看作是一个实例,也可以看作是Eureka的一个客户端。
实例的一些行为可以通过eureka.instance.*这样的键的值来配置。当然,如果你的${spring.application.name}配置有定义,使用Eureka默认的配置就可以。

具体的配置可以从这两个类中了解:EurekaInstanceConfigBeanEurekaClientConfigBean

可以将eureka.client.enabledspring.cloud.discovery.enabled设置成false来禁用Eureka客户端的服务发现。


1.3.认证

如果在eureka.client.serviceUrl.defaultZone配置中包含了认证信息(比如:user:password@localhost:8761/eureka),http基本的认证会自动进行。如果有更复杂的认证需求,可以创建DiscoveryClientOptionalArgs这样的一个Bean,并且注入ClientFilter实例,所有的这些过滤器都会应用到客户端到服务端的调用中。

如果Eureka服务端要求客户端做tls认证,可以通过下列的配置来配置认证信息:
application.yml

  1. eureka:
  2. client:
  3. tls:
  4. enabled: true
  5. key-store: <path-of-key-store>
  6. key-store-type: PKCS12
  7. key-store-password: <key-store-password>
  8. key-password: <key-password>
  9. trust-store: <path-of-trust-store>
  10. trust-store-type: PKCS12
  11. trust-store-password: <trust-store-password>

需要将eureka.client.tls.enabled配置成true来开启tls认证。如果没有配置eureka.client.tls.trust-store,那么会默认使用jvm的trust store。eureka.client.tls.key-store-typeeureka.client.tls.trust-store-type的默认值是PKCS12。如果没有配置密码,会使用空密码。


1.4.状态页和健康标识

状态页和健康标识默认的路径是/info和/health。如果你的应用中没有配置context path或servlet path。
那么状态页和健康标识的默认值就是下面这样的:
application.yml

  1. eureka:
  2. instance:
  3. statusPageUrlPath: ${server.servletPath}/info
  4. healthCheckUrlPath: ${server.servletPath}/health

1.5.https支持

使用下面的两个配置来让应用使用https进行数据传输:

  • eureka.instance.[nonSecurePortEnabled]=[false]
  • eureka.instance.[securePortEnabled]=[true]

这两个配置明确向外界表明实例需要使用安全的连接方式。并且Spring Cloud的DiscoveryClient会向服务返回https开头的url。并且Eureka实例会有一个安全的健康检查url。

application.yml

  1. eureka:
  2. instance:
  3. statusPageUrl: https://${eureka.hostname}/info
  4. healthCheckUrl: https://${eureka.hostname}/health
  5. homePageUrl: https://${eureka.hostname}/

1.6.健康检查

默认情况下,Eureka使用客户端心跳来决定一个客户端是否已启动。默认情况下,DiscoveryClient 不会传播每个springboot服务的运行状态。因此,当服务在成功注册后,Eureka总是宣布服务是处于“启动”状态的。可以通过启用Eureka健康检查来改变这一点。当启动健康检查后,其他所有服务都不会向不处于“启动”状态的服务发送数据。
可以通过以下配置来开启Eureka健康检查:
application.yml

  1. eureka:
  2. client:
  3. healthcheck:
  4. enabled: true

上面的配置应该仅仅配置在application.yml配置文件中。如果在bootstrap.yml中配置此项,可能会有注册到Eureka上的服务会是UNKNOW状态。

如果你想自己实现自己的健康检查逻辑,可以实现自己的健康检查处理器:com.netflix.appinfo.HealthCheckHandler


1.7.Eureka元数据

标准的元数据包括域名,IP地址,端口号,状态页和健康检查url。这些元数据会被注册中心暴露出去,然后被其他的客户端使用来直接调用服务。
可以使用eureka.instance.metadataMap配置来自定义元数据,但是客户端不会被这些元数据所影响,除非客户端明白自定义元数据的意义。


1.8.使用EurekaClient

可以使用com.netflix.discovery.EurekaClient来从Eureka Server中发现服务实例。

  1. @Autowired
  2. private EurekaClient discoveryClient;
  3. public String serviceUrl() {
  4. InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
  5. return instance.getHomePageUrl();
  6. }

不要在@PostConstruct方法中或@Schedule方法(以及其他ApplicationContext还未初始化之前的方法)中使用EurekaClient,EurekaClient在SmartLifecycle中以phase=0的优先级进行初始化,所以你需要在优先级更低,即phase的值更大的阶段使用EurekaClient。

1.8.1.在EurekaClient中不使用Jersey

默认情况下,EurekaClient使用Jersey来进行http请求。如果你不想使用Jersey,你可以在排除它的依赖。Spring Cloud会基于RestTemplate来建立一个传输客户端。
下面是排除Jersey依赖的例子:

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  4. <exclusions>
  5. <exclusion>
  6. <groupId>com.sun.jersey</groupId>
  7. <artifactId>jersey-client</artifactId>
  8. </exclusion>
  9. <exclusion>
  10. <groupId>com.sun.jersey</groupId>
  11. <artifactId>jersey-core</artifactId>
  12. </exclusion>
  13. <exclusion>
  14. <groupId>com.sun.jersey.contribs</groupId>
  15. <artifactId>jersey-apache-client4</artifactId>
  16. </exclusion>
  17. </exclusions>
  18. </dependency>

1.9.EurekaClient的替代品

你不需要去使用未加工的EurekaClient。经过一些包装之后,使用它会更方便。
Spring Cloud支持通过逻辑服务标识符的Feign和Spring RestTemplate来替代物理的url。
可以将逗号分隔的物理服务地址的列表设置在<client>.ribbon.listOfServers上来配置Ribbon。是客户端的ID。
你也可以使用org.springframework.cloud.client.discovery.DiscoveryClient来发现服务。它提供了一个简单的API。

  1. @Autowired
  2. private DiscoveryClient discoveryClient;
  3. public String serviceUrl() {
  4. List<ServiceInstance> list = discoveryClient.getInstances("STORES");
  5. if (list != null && list.size() > 0 ) {
  6. return list.get(0).getUri();
  7. }
  8. return null;
  9. }

1.10.为什么注册服务非常慢?

Eureka实例会通过ServiceUrl向注册中心发送心跳,间隔时间默认是30秒。只有当实例,Eureka Server和客户端的元数据一致时,一个服务在系统中才是可用的,所以可能会需要3次心跳来同步元数据。
可以通过更改eureka.instance.leaseRenewalIntervalInSeconds的配置来改变心跳的频率,让它少于30可以加快服务被发现的速度。
但是,在生产环境,还是建议使用默认值,因为在Eureka Server中,会假定这个频率。(什么鸟意思,也不知道)


1.11.空间

如果你在不同的空间部署了Eureka Client,你可能想在尝试其他空间的服务前优先使用本区域的服务。要做到这一点,你需要对Client做准确的配置。

但是首先,你要在不同的空间都部署上Eureka Server,并且他们是对等的。

然后,你需要告诉你的Eureka Client它是属于哪个空间的。可以使用metadataMap来配置。
比如下面的例子,同一个服务在不同的空间:
Service 1 in Zone 1

  1. eureka.instance.metadataMap.zone = zone1
  2. eureka.client.preferSameZoneEureka = true

Service 1 in Zone 2

  1. eureka.instance.metadataMap.zone = zone2
  2. eureka.client.preferSameZoneEureka = true

1.12.刷新客户端

默认情况下,Eureka Client是可刷新的。这意味着Client的属性可以被改变并刷新。
但当刷新发生时,Client会从注册中心脱离,然后在刷新的这个短暂的时间内,所有的服务会不可用。
可以使用eureka.client.refresh.enable=false来禁用刷新。


1.13.在Eureka中使用Spring Cloud的负载均衡


2.服务发现:Eureka Server

2.1.怎么引入Eureka Server

groupID org.springframework.cloud
artifactID spring-cloud-starter-netflix-eureka-server

如果你的项目中使用了Thymeleaf作为模板引擎,那么Eureka中的Freemarker模板就不能正确地加载。 所以需要手动地去配置模板:

  1. spring:
  2. freemarker:
  3. template-loader-path: classpath:/templates/
  4. prefer-file-system-access: false

2.2.如何启动Eureka Server

下面是一个最简单的Eureka Server:

  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class Application {
  4. public static void main(String[] args) {
  5. new SpringApplicationBuilder(Application.class).web(true).run(args);
  6. }
  7. }

Eureka Server有一个UI的主页,和/eureka/**路径下的HTTP API。

在Gradle中,仅仅添加Server的依赖是启动不了的。需要添加Spring Boot的插件和Spring Cloud的父依赖管理:

  1. buildscript {
  2. dependencies {
  3. classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
  4. }
  5. }
  6. apply plugin: "spring-boot"
  7. dependencyManagement {
  8. imports {
  9. mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
  10. }
  11. }

2.3.高可用,空间和区域

Eureka Server没有后台存储。服务发送过来的心跳数据都存储在内存中。
Clients也有自己的内存缓存,所以不是每一次对服务的调用都需要经过注册中心。

默认情况下,每一个Eureka Server也是一个Client,也需要提供serviceUrl。如果没有提供,会在日志中出现很多警告日志。


2.4.单机模式