Eureka:解决服务地址的管理。自身不提供服务(不注册自己到Eureka中,如果是集群则要注册,因为其他Eureka服务器也要发现这台Eureka)也不消费服务(不来取服务)。默认情况下要注册和拉取的
在刚才的案例中,user-service对外提供服务,需要对外暴露自己的地址。而consumer-demo(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目可能会拆分出十几,甚至几十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。
Eureka负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。(即发现是通过服务提供方的名字获取实例集合,然后从实例集合中取出一个实例,再从实例中获取IP和端口)
同时,服务提供方与Eureka之间通过 “心跳” 机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。
这就实现了服务的自动注册、发现、状态监控。
Eureka:就是服务注册中心(Eureka Server可以是一个集群),对外(服务提供方和服务消费方)暴露自己的地址
提供者:启动后向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>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-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>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-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>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-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的集合中取出一个(Eureka-client中已经集成了Ribbon),而现在是取第一个。
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,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。
(Eureka Server)失效剔除(60s)
先判断心跳是否续约,如果未续约,则可能失效剔除。
有时我们的服务(提供方)可能由于内存溢出或网络故障等(非正常原因)使得服务不能正常的工作,而服务注册中心并未收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。
可以通过 eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒。
eureka: server: eviction-interval-timer-in-ms: 60000
(Eureka Server)自我保护
先判断心跳是否续约,如果未续约,则可能自我保护。
我们关停一个服务,很可能会在Eureka面板看到一条警告:
这是触发了Eureka的自我保护机制。当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。
在开发阶段,启动自我保护模式是没有意义的,服务停止了就是停止了,不是因为网络的原因,因为在我们自己的机器上基本不可能出现网络的原因,建议开发是关闭自我保护模式。
自我保护模式是默认打开的。
可以通过下面的配置来关停自我保护:
eureka:
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 60000
上述配置,先判断心跳是否续约,如果未续约,则会失效剔除,而不会自我保护,因为自我保护已关闭。
在生产环境,打开自我保护,而不是失效剔除。