文章目录
四、Netflix Eureka 注册中心
4.1 注册中心概述
注册中心主要涉及到三大角色:
- 服务提供者
- 服务消费者
- 注册中心
它们之间的关系大致如下:
- 各个微服务在启动时,将自己的网络地址等信息注册到注册中心,注册中心存储这些数据。
- 服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。
- 各个微服务与注册中心使用一定机制(例如心跳)通信。如果注册中心与某微服务长时间无法通信,就会注销该实例。
- 微服务网络地址发送变化(例如实例增加或IP变动等)时,会重新注册到注册中心。这样,服务消费者就无需人工修改提供者的网络地址了。
4.2 Netlfix Eureka 概述
Eureka 是 Netflix 开发的服务注册框架,SpringCloud 将它集成在自己的子项目spring-cloud-netflix 中,实现 SpringCloud 的服务发现功能。Eureka包含两个组件: Eureka Server
和 Eureka Client
。
Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client 是一个 java 客户端,用于简化与 Eureka Server的交互,客户端中有一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
4.2.1 Eureka 提供的功能
Eureka提供的最主要的功能就是服务注册于发现功能了,除本身核心功能外,Eureka还提供了许多其他的功能;
4.2.1.1 心跳检查
在应用启动后,将会向Eureka Server 发送心跳,默认周期为 30 秒,如果 Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认 90 秒)。
4.2.1.2 同步复制
在Eureka集群环境时,每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步;
4.2.1.3 缓存机制
Eureka提供了客户端缓存机制,即使所有的 Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的 API。
综上,Eureka通过心跳检查、复制同步、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
4.3 Eureka 快速入门
4.3.1 搭建Eureka Server
1)创建eureka_server模块
2)引入依赖
<?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>parent</artifactId>
<groupId>com.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka_server</artifactId>
<dependencies>
<!--eureka服务器端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
3)编写 application.yml
server:
port: 10101
spring:
application:
name: eureka-server # 微服名称
eureka:
client:
register-with-eureka: false # 是否将该服务注册到eureka服务端
fetch-registry: false # 是否从eureka服务端获取其他服务实例
service-url: # eureka的注册地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
instance:
hostname: localhost # 主机名
register-with-eureka
:是否将该服务注册到eureka服务端,默认为true,集群环境下使用
fetch-registry
:是否从eureka服务端获取其他服务实例,默认为true,集群环境下使用
4)编写启动类
package com.cloud.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务器端
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class);
}
}
5)访问:http://localhost:10101
4.3.2 搭建Eureka Client
4.3.2.1 注册Item服务
1)引入Eureka Client依赖
<!--引入eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)编写application.yml
server:
port: 9000
spring:
application:
name: item-service # 微服务名称
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10101/eureka # 注册到Eureka
instance:
prefer-ip-address: true # 使用ip地址注册服务(默认情况下是以主机名注册)
instance-id: 127.0.0.1:${server.port} # 实例id
prefer-ip-address:以ip地址注册服务
instance-id:实例名称(id),名字随意;一般情况下是ip+端口;
3)修改引导类
package com.cloud.item;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现功能
@EnableEurekaClient // 开启Eureka客户端
public class ItemApplication {
public static void main(String[] args) {
SpringApplication.run(ItemApplication.class);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
从
Spring Cloud Edgware
版本之后(包括Edgware版本), 加上EurekaClient相关依赖之后,并进行相应配置,即可将微服务注册到服务发现组件上。@EnableDiscoveryClient和@EnableEurekaClient可不加;
4.3.2.2 注册Order服务
参考Item服务注册步骤;
4.3.4 服务消费
4.3.4.1 DisconverClient
DiscoveryClient可以用来获取服务的一些注册信息;如实例id、服务名称、主机名、端口、状态、ip地址等信息
package com.cloud.demo;
import com.cloud.item.ItemApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootTest(classes = ItemApplication.class)
@RunWith(SpringRunner.class)
public class Demo02_DiscoveryClient {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Test
public void test() {
// 根据服务名获取实例
List<ServiceInstance> instances = discoveryClient.getInstances("order-service");
ServiceInstance instance = instances.get(0);
System.out.println("服务实例id: "+instance.getInstanceId());
System.out.println("服务名称: "+instance.getServiceId());
System.out.println("主机ip: "+instance.getHost());
System.out.println("端口: "+instance.getPort());
System.out.println("协议: "+instance.getScheme());
/*
[
EurekaDiscoveryClient.EurekaServiceInstance@630febd7 instance = InstanceInfo
[
instanceId = 192.168.18.1:9001,
appName = ORDER-SERVICE,
hostName = 192.168.18.1,
status = UP,
ipAddr = 192.168.18.1,
port = 9001,
securePort = 443,
dataCenterInfo = com.netflix.appinfo.MyDataCenterInfo@45c2c396
]
*/
System.out.println(instance);
}
@Test
public void test1() {
// 根据服务名获取实例
List<ServiceInstance> instances = discoveryClient.getInstances("order-service");
ServiceInstance instance = instances.get(0);
System.out.println(instance);
// 使用discoveryClient获取服务元数据进行服务消费(解耦)
Map resultMap = restTemplate.getForObject("http://" + instance.getHost() + ":" + instance.getPort() + "/order/110", Map.class);
System.out.println(resultMap);
}
}
4.3.4.2 LoadBalancerClient
LoadBalancerClient和DisconverClient一样,都是用来获取服务的元数据的;
- 测试类:
@Autowired
private LoadBalancerClient loadBalancerClient;
@Test
public void test2() {
// 根据id获取服务实例(当有多个重名服务时采用轮询的负载均衡策略)
ServiceInstance instance = loadBalancerClient.choose("order-service");
System.out.println(instance);
for (int i = 0; i < 10; i++) {
Map resultMap = restTemplate.getForObject("http://" + instance.getHost() + ":" + instance.getPort() + "/order/110", Map.class);
System.out.println(resultMap);
}
}
4.3.4.3 服务负载均衡
1)修改引导类的RestTemplate:
@Bean
@LoadBalanced // 负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
2)测试
@Test
public void test3() {
// 根据服务名获取实例
List<ServiceInstance> instances = discoveryClient.getInstances("order-service");
ServiceInstance instance = instances.get(0);
System.out.println(instance);
// 循环调用接口十次
for (int i = 0; i < 10; i++) {
// 使用服务名调用接口
Map resultMap = restTemplate.getForObject("http://" + instance.getServiceId() + "/order/110", Map.class);
System.out.println(resultMap);
}
}
@Test
public void test4() {
// 获取服务实例
ServiceInstance instance = loadBalancerClient.choose("order-service");
// 循环调用接口十次
for (int i = 0; i < 10; i++) {
// 使用服务名调用接口
Map resultMap = restTemplate.getForObject("http://" + instance.getServiceId() + "/order/110", Map.class);
System.out.println(resultMap);
}
}
注意:当添加@LoadBalanced注解后,只能通过服务名来调用接口,不能使用主机+端口的方式调用;
4.4 Eureka自我保护
4.4.1 自我保护
如果在 Eureka Server 的首页看到以下这段提示,则说明 Eureka已经进入了保护模式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a68dy5Z2-1647867525103)(media/185.png)]
默认情况下,如果Eureka Server在一定时间内没有接收到某个Eureka Client的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络故障发生时,Eureka Client与Eureka Server之间无法正常通信,这就可能变得非常危险了,因为微服务本身是健康的,此时本不应该注销这个微服务。
Eureka Server通过“自我保护模式”来解决这个问题:当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式,让Eureka集群更加的健壮、稳定。
Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%;如果出现低于的情况下,Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。
4.4.2 错误信息呈现
默认情况下,Eureka符合自我保护条件时,并不会将信息呈现到web页面,而是默认等待5分钟之后再将自我保护信息呈现到web页面,因为服务刚开始启动时必定是符合自我保护条件的,即心跳肯定是低于85%;
我们可以通过如下参数调整呈现信息的时间:
eureka:
server:
wait-time-in-ms-when-sync-empty: 1
wait-time-in-ms-when-sync-empty:当有服务还未注册时,等待的时长;默认5分钟,单位秒;
4.4.3 关闭自我保护
我们前面提到过,Eureka的自我保护机制可以保证在网络出现故障时,暂时无法发送心跳的Eureka Client不会被剔除,保护了Eureka Client;使我们的Eureka集群更加的健壮、稳定。
但是,需要注意的是,我们肯定希望被保护的Eureka Client是==正常的==,万一在保护期间某个Eureka Client下线了呢?(非正常下线)Eureka Server的保护机制也会将其”保护”起来,那么此时服务消费者就将消费一个无效的服务;
对于这个问题在Spring Cloud中也有对应解决方案,如服务熔断、重试、降级等等。我们也可以选择将自我保护关闭;
关闭Eureka Server的自我保护:
eureka:
server:
enable-self-preservation: false # 关闭自我保护机制
Eureka Client正常下线会给Eureka Server发送一个Cancel请求,告诉Eureka Server,我正常下线了,我们主要讨论的是非正常情况下线;
如:程序崩溃、端口被删除、服务器宕机、断电等;
4.7 搭建 Eureka 集群
4.7.1 Eureka 集群概述
我们刚刚使用的是Eureka单机版,如果Eureka Server宕机,就可能会影响到微服务的调用,甚至影响到整个应用系统的高可用。因此,在生成环境中,通常会部署一个高可用的Eureka Server集群。
在Eureka集群环境中,多个Server相互注册进行数据的同步
4.7.2 搭建Eureka 集群
1)准备两个工程
2)Eureka01-application.yml
server:
port: 10101
spring:
application:
name: eureka-server #微服名称
eureka:
client:
register-with-eureka: true # 是否将该服务注册到eureka服务端
fetch-registry: true # 是否从eureka服务端获取其他服务实例
service-url: # eureka的注册地址
defaultZone: http://${spring.cloud.client.ip-address}:10102/eureka # 暴露的注册地址
instance:
hostname: localhost # 主机名
prefer-ip-address: true # 使用ip地址注册
instance-id: 127.0.0.1:10101 # 实例id(默认的实例名是微服务名称)
3)Eureka02-application.yml
server:
port: 10102
spring:
application:
name: eureka02-server #微服名称
eureka:
client:
register-with-eureka: true # 是否将该服务注册到eureka服务端
fetch-registry: true # 是否从eureka服务端获取其他服务实例
service-url: # eureka的注册地址
defaultZone: http://${spring.cloud.client.ip-address}:10101/eureka
instance:
hostname: localhost
prefer-ip-address: true
instance-id: 127.0.0.1:10102
4)同时启动两台Eureka Server
访问:http://localhost:10101
访问:http://localhost:10102
4.7.3 注册服务到Eureka集群
1)修改item工程的application.yml:
server:
port: 9000
spring:
application:
name: item-service # 微服务名称
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10101/eureka,http://127.0.0.1:10102/eureka # 注册到Eureka
instance:
prefer-ip-address: true # 使用ip地址注册服务(默认情况下是以主机注册)
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例id
2)修改order工程的application.yml:
server:
port: 9001
spring:
application:
name: order-service # 微服务名称
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10101/eureka,http://127.0.0.1:10102/eureka # 注册到Eureka
instance:
prefer-ip-address: true # 使用ip地址注册服务(默认情况下是以主机注册)
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例id
尝试关闭一台Eureka Server,在item服务发起远程调用,发现依旧可以调用成功,提高了Eureka的高可用;
4.8 Eureka CAP 策略
CAP:即Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性);在一个分布式系统中,最多只能满足两个,我们在选择上必定有所舍取;
Eureka正是采用AP策略,即保证服务的可用性以及分区容错性,降低了服务的一致性;
Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务,而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
4.9 Eureka 生命周期
4.9.1 Eureka 注册/调用过程
搭建完Eureka集群后,Eureka Server之间做复制(Replicate
)操作同步数据;当有Eureka Client启动时,注册(Register
)到Eureka Server中,期间不断向Eureka Server发送心跳进行续约(renew
)操作,Eureka Server将Client的信息维护在自身的注册表中,供Client间的抓取(Get Registry
)与调用(Make Remote Call
),当Client正常下线时,会向Server发送一个剔除指令(Cancel
),Eureka Server接收到后将服务从注册表中剔除;
Register
:EurekaClient注册(Http请求)到EurekaServer,EurekaClient会发送自己元数据(ip,port,主页等),EurekaServer会将其添加到服务注册列表里(Map集合)Renew
:EurekaClient默认每30秒发送心跳(timer定时任务,发送Http请求)到EurekaServer续约,向EurekaServer证明其活性,EurekaServer将EurekaClient心跳中的时间戳参数与已有服务列表中对应的该服务的时间戳进行比较,不相等就更新对应的服务列表;如果EurekaServer 90秒都没收到某个EurekaClient的续约,并且没有进入保护模式,就会将该服务从服务列表将其剔除(Eviction)Get Registry
:Eureka Client默认每30秒从Eureka Server获取服务注册列表,并且会与自身本地已经缓存过的服务列表进行比较合并Cancel
:当EurekaClient正常下线时,向EurekaServer发送Cancel,EurekaServer将其从服务列表中剔除Make Remote Call
:EurekaClient服务间进行远程调用,比如通过RestTemplate+Ribbon或FeginReplicate
:EurekaServer集群节点之间数据(主要是服务注册列表信息)同步
我们可以启动一台Eureka Client观察Server端控制台日志:
4.9.2 Actuator 监控
我们可以使用Spring Boot Actuator来帮我们管理Eureka Client的下线操作;
引入Actuator依赖:
<!--actuator依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
开放所有端点:
management:
endpoints:
web:
exposure:
include: '*' # 开放所有端点
启动Order服务,访问:http://localhost:9001/actuator:
强制开放shutdown端点,实现手动停机:
management:
endpoints:
web:
exposure:
include: '*' # 开放所有端点
endpoint:
shutdown:
enabled: true # 开启shutdown 实现手动停机
重启Order服务,访问:http://localhost:9001/actuator:
打开Postman使用Post请求发送:http://localhost:9001/actuator/shutdown
4.10 Eureka 安全认证
在通常情况下我们的Eureka是不能运行任意的人来访问的,也不能运行任意的访问进行注册,我们必须提供一系列的认证,Eureka 与Spring Security进行了无缝集成,可以很方便的帮助我们对Eureka的连接进行认证;
4.10.1 配置Eureka Server认证
引入Security启动器:
<!--security依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
在两台Eureka Server添加认证配置:
spring:
security:
user:
name: root # 配置用户名
password: admin # 配置密码
编写WebSecurityConfig.java
类,对EurekaClient发送的注册请求进行放行;
package com.cloud.eureka;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 安全配置类
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().ignoringAntMatchers("/eureka/**");
}
}
启动Eureka Server,访问:http://localhost:10101和http://localhost:10102
4.10.2 配置Eureka Client认证
Eureka Server都配置了用户名和密码,Eureka Client在注册时应该指定Server端的用户名和密码,修改Eureka Server:
eureka:
client:
register-with-eureka: true # 是否将该服务注册到eureka服务端
fetch-registry: true # 是否从eureka服务端获取其他服务实例
service-url: # eureka的注册地址
defaultZone: http://root:admin@127.0.0.1:10101/eureka # 配置用户名和密码
instance:
prefer-ip-address: true # 使用ip地址注册
instance-id: 127.0.0.1:10102
修改Eureka Client:
server:
port: 9000
spring:
application:
name: item-service # 微服务名称
eureka:
client:
service-url:
defaultZone: http://root:admin@127.0.0.1:10101/eureka,http://root:admin@127.0.0.1:10102/eureka # 注册到Eureka
instance:
prefer-ip-address: true # 使用ip地址注册服务(默认情况下是以主机注册)
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例id
Order服务的注册中心配置和Item服务一致
分别访问两台Eureka Server查看访问是否注册成为成功:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131749.html