一.简介
1.1 什么是服务的注册与发现?
服务注册是指向服务注册中心注册一个服务实例,服务提供者将自己的服务信息(如服务名,IP地址等)告知服务注册中心。服务发现是指当服务消费者需要消费另外一个服务时,服务注册中心能够告知服务消费者它需要消费服务的实例信息(如服务名,IP地址等)。通常情况下,一个服务既是服务提供者,也是服务消费者。服务消费者一般使用HTTP协议或者消费组件这种轻量级的通信机制来进行服务消费。
服务注册中心会提供服务的健康检查方案,检查被注册的服务是否可用。通常一个服务实例注册后,会定时向服务注册中心提供“心跳”,以表明自己还处于可用的状态。当一个服务实例停止向服务注册中心提供心跳一段时间后,服务注册中心会认为该服务实例不可用,会将该服务实例从服务注册表中剔除。如果这个被剔除的服务实例过一段时间后向注册中心提供心跳,那么服务注册中心会将该服务实例重新加入服务注册中心的列表中。另外,微服务的服务注册组件都会提供服务的健康状况查看的UI界面,开发人员或者运维人员只需要登录相关的界面就知道服务的健康状态。
1.2 Eureka
1.2.1 什么是Eureka?
Spring Cloud封装了Netfilx公司开发的Eureka模块来实现服务实例。是一个基于REST的服务,并且提供了基于Java的客户端组件,能够非常方便地将服务注册到Spring Cloud Eureka中进行统一管理。
1.2.2 为什么选择Eureka?
- Eureka完全开源,功能和性能上都非常稳定,可以放心使用
- Eureka是Spring Cloud首选推荐的服务注册和发现组件
- Eureka和其他组件,比如负载均衡Ribbon,熔断器组件Hystrix等相互配合。这些组件都是由Netfilx公司开源的,一起被称为Netfilx OSS组件。
二.Eureka使用
2.1 两个组件
Eureka Server:提供服务注册服务。各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
Eureka Client:通过注册中心进行访问。是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒),如果Eureka Server在多个心跳周期内没有接受到某个节点的心跳,Eureka Server将服务注册表中把这个服务节点移除(默认90秒)
2.2 Eureka服务端安装
1.创建一个Maven工程
2.修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>LearnCloud</artifactId>
<groupId>com.lun.springcloud</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
3.添加application.yml
server:
port: 7001
eureka:
instance:
hostname: locathost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4.在主启动类上加上注册@EnableEurekaServer,开启Eureka Server的功能
5.可以直接通过http://localhost:7001/在浏览器访问,就会看到Eureka提供的Web控制台
2.3 Eureka 服务提供者
1.创建一个Maven项目
2.修改pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.添加application.yaml
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
4.在主启动类添加注解@EnableEurekaClient
5.启动Server工程。浏览器输入 - http://localhost:7001/ 主页内的Instances currently registered with Eureka会显示服务提供者的配置文件application.yml设置的应用名。
2.4 Eureka 消费者
1.创建一个Maven项目
2.修改pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.添加application.yaml
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
4.在主启动类添加注解@EnableEurekaClient
5.直接调用接口。RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够提高客户端的编写效率。通过配置RestTemplate来调用接口,代码如下:
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
创建接口,代码如下:
@RestController
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
}
}
6.通过Eureka来消费接口。首先改造RestTemplate的配置,添加一个@LoadBalanced注解,这个注解会自动构造LoadBalancerClient接口注册到Spring容器中,代码如下:
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalaced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
改造调用代码,不再写固定地址,而是写成服务的名称,这个名称就是提供者注册到Eureka中的名称,是属性文件中的spring.application.name。
2.5 集群搭建
在生产环境中必须搭建一个集群来保证高可用。Eureka的集群搭建方法非常简单:每一台的Eureka只需要在配置中指定多个Eureka的地址就可以实现一个集群的搭建。
(Server)搭建过程如上所示,需要更改这个配置文件:
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#集群指向其它eureka,有多个的时候用英文,隔开
defaultZone: http://eureka7002.com:7002/eureka/
#单机就是7001自己
#defaultZone: http://eureka7001.com:7001/eureka/
2.5 自我保护机制
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。如果在Eureka 的Web控制台看到下图内容,就说明保护模式已经打开
导致原因:一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。属于CAP里面的AP分支。
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
如果Eureka在server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间( 90秒中)内丢失了大量的服务实例心跳,这时候Eurekaserver会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通但是EurekaClient为出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的)。在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
怎么禁止自我保护?使用eureka.server.enable-self-preservation = false
可以禁用自我保护模式
2.6 开发时快速移除失效服务
- eureka.instance.lease-renewal-interval-in-seconds 表示 Eureka Client 发送心跳给 server 端的频率。
- eureka.instance.lease-expiration-duration-in-seconds 表示 Eureka Server 至上一次收到 client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则移除该 Instance。
3.Eureka Client获取服务实例这样慢?
3.1 Eureka Client的注册延迟
Eureka Client启动后,不是立即向Eureka Server注册的,而是有一个延迟向服务端注册的时间。默认时间为40秒
3.2 Eureka Server的响应缓存
Eureka Server维护每30秒更新一次响应缓存,可通过更改配置来修改,也就是说即使刚刚注册的实例,也不会立即出现在服务注册列表中
3.3 Eureka Client的缓存
Eureka Client保存注册表的缓存信息,该缓存每30秒更新一次。因此,Eureka Client刷新本地缓存并发现其他新的注册实例可能需要30秒
3.4 LoadBalancr的缓存
Ribbon的负载均衡器从本地的Eureka Client获取服务注册信息。Ribbon本省还维护了缓存,避免每个请求都需要从Eureka Client获取服务注册列表,此缓存美30秒刷新一次(ribbon.ServerListRefreshInterval配置),所以至少需要30秒的时间才能使用新的注册实例。