此项目将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客户端服务应用:
@SpringBootApplication
@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);
}
}
这看起来就像是一个普通的Spring Boot应用,但如果你的classpath中包含了spring-cloud-starter-netflix-eureka-client
的依赖,那么你的应用就会自动注册到Eureka注册中心。
为了定位到Eureka服务端,需要在配置文件中进行配置:
application.yml
eureka:
client:
serviceUrl:
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默认的配置就可以。
具体的配置可以从这两个类中了解:EurekaInstanceConfigBean,EurekaClientConfigBean
可以将eureka.client.enabled
或spring.cloud.discovery.enabled
设置成false
来禁用Eureka客户端的服务发现。
1.3.认证
如果在eureka.client.serviceUrl.defaultZone
配置中包含了认证信息(比如:user:password@localhost:8761/eureka),http基本的认证会自动进行。如果有更复杂的认证需求,可以创建DiscoveryClientOptionalArgs这样的一个Bean,并且注入ClientFilter实例,所有的这些过滤器都会应用到客户端到服务端的调用中。
如果Eureka服务端要求客户端做tls认证,可以通过下列的配置来配置认证信息:
application.yml
eureka:
client:
tls:
enabled: true
key-store: <path-of-key-store>
key-store-type: PKCS12
key-store-password: <key-store-password>
key-password: <key-password>
trust-store: <path-of-trust-store>
trust-store-type: PKCS12
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-type
和eureka.client.tls.trust-store-type
的默认值是PKCS12
。如果没有配置密码,会使用空密码。
1.4.状态页和健康标识
状态页和健康标识默认的路径是/info和/health。如果你的应用中没有配置context path或servlet path。
那么状态页和健康标识的默认值就是下面这样的:
application.yml
eureka:
instance:
statusPageUrlPath: ${server.servletPath}/info
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
eureka:
instance:
statusPageUrl: https://${eureka.hostname}/info
healthCheckUrl: https://${eureka.hostname}/health
homePageUrl: https://${eureka.hostname}/
1.6.健康检查
默认情况下,Eureka使用客户端心跳来决定一个客户端是否已启动。默认情况下,DiscoveryClient
不会传播每个springboot服务的运行状态。因此,当服务在成功注册后,Eureka总是宣布服务是处于“启动”状态的。可以通过启用Eureka健康检查来改变这一点。当启动健康检查后,其他所有服务都不会向不处于“启动”状态的服务发送数据。
可以通过以下配置来开启Eureka健康检查:
application.yml
eureka:
client:
healthcheck:
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中发现服务实例。
@Autowired
private EurekaClient discoveryClient;
public String serviceUrl() {
InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
}
不要在@PostConstruct方法中或@Schedule方法(以及其他ApplicationContext还未初始化之前的方法)中使用EurekaClient,EurekaClient在SmartLifecycle中以phase=0的优先级进行初始化,所以你需要在优先级更低,即phase的值更大的阶段使用EurekaClient。
1.8.1.在EurekaClient中不使用Jersey
默认情况下,EurekaClient使用Jersey来进行http请求。如果你不想使用Jersey,你可以在排除它的依赖。Spring Cloud会基于RestTemplate
来建立一个传输客户端。
下面是排除Jersey依赖的例子:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</exclusion>
</exclusions>
</dependency>
1.9.EurekaClient的替代品
你不需要去使用未加工的EurekaClient。经过一些包装之后,使用它会更方便。
Spring Cloud支持通过逻辑服务标识符的Feign和Spring RestTemplate来替代物理的url。
可以将逗号分隔的物理服务地址的列表设置在<client>.ribbon.listOfServers
上来配置Ribbon。
你也可以使用org.springframework.cloud.client.discovery.DiscoveryClient来发现服务。它提供了一个简单的API。
@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;
}
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
eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true
Service 1 in Zone 2
eureka.instance.metadataMap.zone = zone2
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模板就不能正确地加载。 所以需要手动地去配置模板:
spring:
freemarker:
template-loader-path: classpath:/templates/
prefer-file-system-access: false
2.2.如何启动Eureka Server
下面是一个最简单的Eureka Server:
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
Eureka Server有一个UI的主页,和/eureka/**路径下的HTTP API。
在Gradle中,仅仅添加Server的依赖是启动不了的。需要添加Spring Boot的插件和Spring Cloud的父依赖管理:
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
}
}
2.3.高可用,空间和区域
Eureka Server没有后台存储。服务发送过来的心跳数据都存储在内存中。
Clients也有自己的内存缓存,所以不是每一次对服务的调用都需要经过注册中心。
默认情况下,每一个Eureka Server也是一个Client,也需要提供serviceUrl。如果没有提供,会在日志中出现很多警告日志。