spring cloud组件之Eureka注册中心:通过自动注册、发现、状态监控来管理服务地址

首页 > spring cloud组件之Eureka注册中心:通过自动注册、发现、状态监控来管理服务地址 > 列表

spring cloud组件之Eureka注册中心:通过自动注册、发现、状态监控来管理服务地址

Eureka:解决服务地址的管理。自身不提供服务(不注册自己到Eureka中,如果是集群则要注册,因为其他Eureka服务器也要发现这台Eureka)也不消费服务(不来取服务)。默认情况下要注册和拉取的

在刚才的案例中,user-service对外提供服务,需要对外暴露自己的地址。而consumer-demo(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目可能会拆分出十几,甚至几十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。

Eureka负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。(即发现是通过服务提供方的名字获取实例集合,然后从实例集合中取出一个实例,再从实例中获取IP和端口)

同时,服务提供方Eureka之间通过 “心跳” 机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。

这就实现了服务的自动注册、发现、状态监控。

Eureka:就是服务注册中心(EurekaServer可以是一个集群),对外(服务提供方和服务消费方)暴露自己的地址

提供者:启动向Eureka注册自己信息(地址,提供什么服务)

消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新

心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态

Eureka详解

基础架构

Eureka架构中的三个核心角色:

1、服务注册中心(没有集群时,不需注册和发现)

Eureka的服务端应用,提供服务注册和发现功能,就是刚刚我们建立的eureka-server

2、服务提供者(只需注册,不需要发现)

提供服务的应用,可以是SpringBoot应用,也可以是其它任意技术实现,只要对外提供的是Rest风格服务即可。本例中就是我们实现的user-service

3、服务消费者(不仅要注册还要发现)

消费应用从注册中心获取服务集合,从而得知每个服务方的信息(ip和port),知道去哪里调用服务方。本例中就是我们实现的consumer-demo

搭建eureka-server工程

1、创建一个Maven的java工程eureka-server

zwh-springcloud右键→new→Module→maven→ArtifactId: eureka-server →next→Module name: eureka-server,Content root: C:\Users\miracle\IdeaProjects\springcloud_2020\zwh-springcloud\eureka-server→finish

2、导入依赖坐标:spring-cloud-starter-netflix-eureka-server

dependency
groupIdorg.springframework.cloud/groupId
artifactIdspring-cloud-starter-netflix-eureka-server/artifactId
/dependency

3、编写启动类,添加@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

4、编写配置文件application.yml,配置eureka的地址,这个地址是其他服务注册的地址,也是获取服务名的地址

server:
  port: 10086
spring:
  application:
    name: eureka-server
eureka:
  client:
  #Eureka服务地址,如果是集群的话,需要制定集群其他Eureka地址 service-url: defaultZone: http://127.0.0.1:10086/eureka fetch-registry: false #默认为true register-with-eureka: false #默认为true

如果是eureka-server集群的话,则fetch-registry和register-with-eureka均为true

5、启动服务,访问:http://127.0.0.1:10086

搭建服务提供方工程,服务注册

注册服务,就是在服务(提供者)上添加Eureka的客户端依赖eureka-client,服务在启动时,如果eureka.client.register-with-eureka=true,则客户端代码会自动把服务注册到EurekaServer中

1、添加依赖,我们在user-service中添加Eureka客户端依赖:spring-cloud-starter-netflix-eureka-client

dependency
groupIdorg.springframework.cloud/groupId
artifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency

2、在启动类上通过添加@EnableDiscoveryClient开启Eureka客户端发现功能。可以不用添加该注解,因为服务提供方不需要发现。

@SpringBootApplication
@MapperScan("com.zwhxpp.user.mapper")
@EnableDiscoveryClient  #开启Eureka客户端发现功能
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

3、在配置文件中添加了spring.application.name属性来指定应用名称,将来会作为服务的id使用。

spring:
  application:
      name: user-service
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka  #告诉服务器Eureka在哪里,才能将服务注册到Eureka中

搭建服务消费方工程,服务发现

在服务消费方添加Eureka客户端依赖eureka-client,开启发现功能且eureka.client.fetch-registry=true,则会从EurekaServer服务的列表拉取只读备份,然后缓存在本地

我们可以使用DiscoveryClient的方法根据服务名称获取对应的服务地址列表来模拟。

1、添加依赖:spring-cloud-starter-netflix-eureka-client

dependency
groupIdorg.springframework.cloud/groupId
artifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency

2、在启动类上通过添加@EnableDiscoveryClient开启Eureka客户端发现功能

@SpringBootApplication
@EnableDiscoveryClient  #开启Eureka客户端发现功能
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

3、新增配置文件

server:
  port: 8080
spring:
  application:
    name: consumer-demo
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

4、修改Controller,用工具类DiscoveryClient类的方法,根据服务名称,获取服务实例。

import org.springframework.cloud.client.discovery.DiscoveryClient;

当注册服务有多个的时候,我们会用负载均衡的方式从ServerInstance的集合中取出一个,而现在是取第一个

5、重启consumer-demo 项目;然后再浏览器中再次访问 http://localhost:8080/consumer/8 ;在代码中debug跟进查看最终拼接要访问的URL:

服务提供方)服务注册

服务注册的条件:eureka.client.register-with-eureka=true

服务注册的方式:主机名注册或ip注册

服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-eureka=true 参数是否正确,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,EurekaServer会把这些信息保存到一个双层Map结构中

1、第一层Map的Key就是服务id,一般是配置中的 spring.application.name 属性

2、第二层Map的key是服务的实例id。一般host+ serviceId + port,例如: localhost:user-service:8081

3、值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

默认注册时使用的是主机名或者localhost,如果想用ip进行注册,可以在服务提供方user-service 中添加prefer-ip-address配置如下:(服务地址使用ip方式)

eureka:
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1

修改完后先后重启user-service 和 consumer-demo ;默认注册时使用的是主机名,将从eureka获取的实例的主机名改成ip地址,这样,在调用服务的时候就已经变成ip地址;

需要注意的是:不是在eureka中的控制台服务实例状态显示。即在控制台显示的仍然是主机名,只是在调用服务的时候将主机名改为了ip地址,如下所示:

服务提供方)服务续约(30s,90s)

在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew);

有两个重要参数可以修改服务续约的行为;可以在user-service (服务提供方)中添加如下配置项:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90 #默认90秒
    lease-renewal-interval-in-seconds: 30 #默认30秒

lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒

lease-expiration-duration-in-seconds:服务失效时间,默认值90秒

lease:租约,租约过期时间90秒,90秒之内没有续约就过期。租约续约时间30秒

也就是说,默认情况下每隔30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳(续约),EurekaServer就会认为该服务宕机,会定时(eureka.server.eviction-interval-timer-in-ms设定的时间)从服务列表中移除,这两个值在生产环境不要修改,默认即可

修改这两个参数的值如下进行测试:

设置好了之后,重启user-service,然后关闭user-service,按照配置,每隔5秒钟会去续约,如果不续约,再等15秒,如果还没有续约,则会允许剔除,即是可以剔除而不是一定会剔除。但是剔除是在eureka上面剔除的,eureka默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除而现在没有剔除,因为它认为有可能是网络故障造成的,会触发了Eureka的自我保护机制,只有设置了eureka.server.eviction-interval-timer-in-ms(失效剔除)才会真正剔除。

服务消费方)获取服务列表(30s)

获取服务列表的条件:eureka.client.fetch-registry=true

获取服务列表的频率:默认30s

当服务消费者启动时,会检测eureka.client.fetch-registry=true 参数的值,如果为true,则会从EurekaServer服务的列表拉取只读备份,然后缓存在本地。并且 每隔30秒会重新拉取并更新数据。可以在 consumer-demo项目中通过下面的参数来修改:(获取服务地址的频率)

eureka:
  client:
    registry-fetch-interval-seconds: 30

服务下线

当服务(提供方)进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态

EurekaServer)失效剔除(60s)

先判断心跳是否续约,如果未续约,则可能失效剔除。

有时我们的服务(提供方)可能由于内存溢出或网络故障等(非正常原因)使得服务不能正常的工作,而服务注册中心并未收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。

可以通过eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒

eureka:
  server:
    eviction-interval-timer-in-ms: 60000

EurekaServer)自我保护

先判断心跳是否续约,如果未续约,则可能自我保护。

我们关停一个服务,很可能会在Eureka面板看到一条警告:

这是触发了Eureka的自我保护机制。当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。

在开发阶段,启动自我保护模式是没有意义的,服务停止了就是停止了,不是因为网络的原因,因为在我们自己的机器上基本不可能出现网络的原因,建议开发是关闭自我保护模式

自我保护模式是默认打开的。

可以通过下面的配置来关停自我保护:

eureka:
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 60000

上述配置,先判断心跳是否续约,如果未续约,则会失效剔除,而不会自我保护,因为自我保护已关闭。

在生产环境,打开自我保护,而不是失效剔除。