本专栏学习内容来自尚硅谷周阳老师的视频
有兴趣的小伙伴可以点击视频地址观看
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka现已停更,但停更不停用
Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
单机Eureka是通过将服务注册到一个Eureka服务中心去,如下图所示
此服务只是一个注册服务,只需要一个启动类+配置文件即可
首先得引入Eureka服务端的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
接着修改配置文件,因为本身就是注册中心,所以不需要向注册中心去注册自己
server.port=7001
#eureka服务端的实例名称
eureka.instance.hostname=localhost
#false表示不向注册中心注册自己。
eureka.client.register-with-eureka=false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
eureka.client.fetch-registry=false
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
最后只需要在启动类上加如下注解
@EnableEurekaServer
启动服务,可以看到Eureka的交互式页面,此处表示还没有服务注册进来
在微服务中,经常会有一个服务调用另外一个服务的情况,在这里我们将前者称之为消费者,后者称之为提供者。现在我们需要把消费者也注册到服务中心
首先引入Eureka客户端依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
修改配置文件
server.port=80
spring.application.name=cloud-order-service
#eureka
#true表示向注册中心注册自己。
eureka.client.register-with-eureka=true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
eureka.client.fetch-registry=true
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
eureka.client.service-url.defaultZone=http://localhost:7001/eureka
在启动类上加上如下注解
@EnableEurekaClient
到这一步我们就已经修改完毕,这时候启动消费者,发现此服务已经被注册到服务中心
需要注意的是注册名与spring.application.name相关,一旦定下来,轻易不要修改
提供者修改与消费者完全相同,这里就不再赘述,现在我们的工程目录就变成了这个样子
commons是我们的公共包
7001是Eureka服务注册中心
80是消费者
8001是提供者
在开发中,我们常常会使用到集群,为了防止Eureka服务注册中心挂掉,会对Eureka进行集群处理。
以下是我们需要实现的图示,实现集群的关键点在于,两个服务相互注册
我们根据cloud-eureka-server7001复制一个cloud-eureka-server7002
因为是测试环境在本地,所有的ip都是localhost,为了区分两个服务,可以修改映射配置添加进hosts文件
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
接下来我们需要分别修改7001配置文件和7002配置文件,将7001注册进7002的服务中心,反之亦然
#eureka服务端的实例名称
eureka.instance.hostname=eureka7001.com #修改
#false表示不向注册中心注册自己。
eureka.client.register-with-eureka=false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
eureka.client.fetch-registry=false
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
eureka.client.service-url.defaultZone=http://eureka7002.com:7002/eureka/ #修改
分别启动两个注册中心服务,会发现他们已经相互注册成功
此时,集群已经配置完毕,我们还需要将消费者和提供者分别注册到集群的服务中心
修改两个服务的配置文件即可
eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
至此,我们就完成了Eureka集群的搭建
在实际开发过程中,为了降低服务压力,还需要实现多个提供者的集群,如下图所示
现在我们需要根据cloud-provider-payment8001复制一个cloud-provider-payment8002服务,此时Eureka页面中加载了两个CLOUD-PAYMENT-SERVICE
调用消费者接口,发现无论如何调用的都是8001的服务,那是因为消费者接口是如下的
@RestController
@Slf4j
public class OrderController {
public static final String PaymentSrv_URL = "http://localhost:8001";
@Resource
RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult createPayment(Payment payment){
return restTemplate.postForObject(PaymentSrv_URL + "/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult createPayment(@PathVariable Integer id){
return restTemplate.getForObject(PaymentSrv_URL + "/payment/get/" + id,CommonResult.class);
}
}
在配置URL时,写死了访问地址,现在需要把地址改成服务注册中心中application的服务名
public static final String PaymentSrv_URL = "http://CLOUD-PAYMENT-SERVICE";
这时候我们调用服务还会报错,这是因为服务注册中心有两个CLOUD-PAYMENT-SERVICE,消费者不知道需要去调用哪个
这时候要使用@LoadBalanced注解赋予RestTemplate负载均衡的能力,此时在测试可以发现8001、8002服务交替被调用
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
如下图所示,我们注册的服务含有主机名称
这个名称可以通过配置文件自定义
#服务主机名称
eureka.instance.instance-id=payment8002
只需要添加如下配置
#访问信息有ip提示
eureka.instance.prefer-ip-address=true
对于注册进Eureka里的微服务,可以通过服务发现来获得该服务的信息
需要在主启动类上加入@EnableDiscoveryClient
,这个注解我们将会经常使用,使用方法如下
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/discovery")
public Object discovery()
{
// 获取服务名称
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
// 获取服务的实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
默认情况下,Eureka是开启自我保护,可以使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式