目录
3.2 将服务提供者provider_service,注册到eureka注册中心
3.3 将服务消费者consumer_service,注册到eureka注册中心
1 介绍
Spirng Cloud Eureka 包含了服务端组件、客户端组件。
服务端与客户端均采用java编写,所以Eureka主要适用于通过java实现的分布式系统,或是JVM兼容语言构建的系统。
2 Eureka原理解析
2.1 总体介绍
Eureka架构中的三个核心角色
- 服务注册中心:Eureka服务端应用,提供服务管理(注册、发现)功能
- 服务提供者:对外发布服务,供消费者调用 ,要求统一对外提供Rest风格服务
- 服务消费者:服务提供者的调用方。从注册中心获取服务列表,通过列表调用服务提供者。
2.2 Eureka客户端与服务端的交互过程
- 服务注册(register)
Eureka Client会通过发送REST请求的方式,向Eureka Server注册自己的服务。
注册时,提供自身的元数据,比如ip地址、端口、运行状况指标、主页地址等信息。
Eureka Server接收到注册请求后,就会把这些元数据信息存储在一个双层的Map中。
注意:服务注册默认使用主机名而不是ip,如果想注册ip到注册中心,就在对应服务的配置文件中输入如下内容:
# 默认注册时使用的是主机名,想用ip进行注册添加如下配置
# ip地址
eureka.instance.ip-address: 127.0.0.1
# 更倾向于使用ip,而不是host名
eureka.instance.prefer-ip-address: true
- 服务续约(renew)
在服务注册后,Eureka Client会维护一个心跳来持续通知Eureka Server,说明服务一直处于可用状态,防止被剔除。
默认每隔30秒 `eureka.instance.lease-renewal-interval-in-seconds` 发送一次心跳来进行服务续约(续约:每隔一段时间,由Client发请求到server,告诉server“我是健康的服务”)。
# 配置到消费者或服务提供者:租约续约间隔时间,默认30秒
eureka.instance.lease-renewal-interval-in-seconds: 30
- 获取服务列表(get registry)
服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给Eureka Server,获取注册中心的服务清单,并且缓存在客户端本地。
同时,为了性能及安全性考虑,Eureka Server会每隔30秒更新一次缓存中的服务清单。
# 配置到消费者或服务提供者:每隔多久获取服务中心列表,(只读备份)
eureka.client.registry-fetch-interval-seconds: 30
- 服务调用
服务消费者在获取到服务清单后,可以根据清单中的服务信息,查找到该服务的地址,从而进行访问(远程调用)。
- 服务下线(cancel)
当Eureka Client需要关闭或重启时,就不希望在这个时间段内再有请求进来
所以,就需要提前先发送REST请求给Eureka Server,告诉Eureka Server自己要下线了
Eureka Server在收到请求后,就会把该服务状态置为下线(DOWN),并把该下线事件传播出去。
服务下线的操作可以对服务执行shutdown的命令,如果是在IDEA中,就点那个exit按钮,而不是点那个红色的停止服务按钮,这个似乎不怎么重要,就不去演示了。
- 失效剔除(evict)
服务实例可能会因为网络故障等原因,导致不能提供服务,而此时该实例也没有发送请求给Eureka Server来进行服务下线。
所以,还需要有服务剔除的机制。
Eureka Server在启动的时候会创建一个定时任务,每隔一段时间(默认60秒,这个时间怎么改还不清楚),从当前服务清单中把超时没有续约(默认90秒`eureka.instance.lease-expiration-duration-in-seconds`)的服务剔除。
总结:作为不再续约且时间超过90s的服务,针对这些服务,每隔60s统一进行一次删除,也就是说一个服务从挂掉到被从注册中心删除至少要经过150s,最多经过180s(服务在心跳检测开始前一瞬间挂掉)。
# 配置到消费者或服务提供者:租约到期,服务时效时间,默认值90秒
eureka.instance.lease-expiration-duration-in-seconds: 90
- 自我保护
既然Eureka Server会定时剔除超时没有续约的服务,那就有可能出现一种场景:
网络一段时间内发生了异常,所有的服务都没能够进行续约,Eureka Server就把所有的服务都剔除了,这样显然不太合理。
所以,就有了自我保护机制。自我保护机制是,当在短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制
在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。
自我保护开关:eureka.server.enable-self-preservation: false
在自我保护模式下,不会剔除任何服务实例,从而保证绝大多数服务的可用性
#配置到注册中心:向Eureka服务中心集群注册服务
eureka.server.enable-self-preservation: false # 关闭自我保护模式(默认值是打开)
Eureka会统计服务实例最近15分钟心跳续约的比例是否低于85%,如果低于则会触发自我保护机制,一旦触发则会出现如下页面:
3 使用Eureka
步骤
- 搭建eureka服务,创建eureka_server工程
- 服务提供者provider_service,注册到eureka注册中心
- 服务消费者consumer_service,注册到eureka注册中心
注意:Eureka服务端和客户端的SpringBoot版本要一致,且SpringBoot版本要能和Eureka版本匹配上
3.1 搭建eureka-server服务端
- 2.1.1 创建eureka_server的springboot工程
- 2.1.2 在启动类EurekaServerApplication声明当前应用为Eureka服务,使用
@EnableEurekaServer
注解,实现自动配置
@SpringBootApplication
@EnableEurekaServer //开启EurekaServer端的自动配置
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 2.1.3 编写配置文件application.yml(properties文件后缀名统一改成.yml)
#端口
server.port: 10086
#应用名称
spring.application.name: eureka-server
#注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
# 是否抓取注册列表
eureka.client.fetch-registry: false
# 是否注册服务中心Eureka(防止自己注册自己)
eureka.client.register-with-eureka: false
- 2.1.4 服务启动测试
3.2 将服务提供者provider_service,注册到eureka注册中心
在本文的第2部分中,分别创建了服务的提供者和消费者工程,这里就派上用场了。
- 2.2.1 在服务提供者provider_service工程中添加Eureka客户端依赖
<!--eureka客户端starter-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--SpringCloud,BOM,依赖清单导入,所有依赖管理的坐标-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 2.2.2 在启动类上开启Eureka客户端发现功能
@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
- 2.2.3 修改配置文件:指定应用名称、注册中心地址
# 端口
server.port: 9091
# 数据库连接配置信息
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.url: jdbc:mysql://127.0.0.1:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.password: root
spring.datasource.username: root
#配置应用名称
spring.application.name: user-service
#配置注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
- 2.2.4 重启项目,客户端代码会自动把服务注册到EurekaServer中,在Eureka监控页面可以看到服务注册成功信息
3.3 将服务消费者consumer_service,注册到eureka注册中心
操作同步骤2。
注意:SpringBoot和SpringCloud的版本兼容性问题,如下图:
在项目中,我的SpringBoot版本:2.1.1.RELEASE,对应SpringCloud版本:Greenwich.SR3 这样做是完全没问题的~
然而,我IDEA自动创建SpringBoot时的版本为2.5.1,按照如下图的要求,SpringCloud版本应该是2020.0.3,但是出错了。可能是我没刷新项目依赖包?或者我把SpringCloud版本号写错了?(暂时先不管)
3.4 消费者通过Eureka访问服务提供者
步骤
- 通过注册中心客户端对象DiscoveryClient,获取Eureka中注册的user-service实例列表
- 获取user-service服务实例对象
- 从实例对象中获取host地址和端口,拼接请求地址
- 使用RestTemplate发送请求
/**
* 消费者的控制层,提供服务,访问提供者接口,为真实用户返回信息
*/
@RestController
public class ConsumerController {
/**
* 用来发http请求
*/
@Autowired
private RestTemplate restTemplate;
/**
* DiscoveryClient 注册中心客户端对象,缓存着注册列表信息
*/
@Autowired
private DiscoveryClient discoveryClient;
/**
* 通过注册中心的注册列表访问服务提供者,动态获取要调用服务的ip和端口信息
*/
@RequestMapping("/consumer/{id}")
public User findById(@PathVariable("id") Integer id){
//从注册列表中获取服务实例对象(由于这个服务可能是一个集群,所以返回的是列表)
List<ServiceInstance> instances = discoveryClient.getInstances("USER-SERVICE");
//取出第一个元素
ServiceInstance userServiceInstance = instances.get(0);
//获取服务的host、port、元数据
String host = userServiceInstance.getHost();
int port = userServiceInstance.getPort();
Map<String, String> metadata = userServiceInstance.getMetadata();
System.out.println("host:"+host);
System.out.println("port:"+port);
System.out.println("元数据:"+metadata);
//动态拼接地址
String url = "http://"+host+":"+port+"user/findById?id="+id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
}