1.概念
Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的 需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。同时,服务提供方与Eureka之间通过“心跳” 机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。这就实现了服务的自动注册、发现、状态监控。
原理图
Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
服务提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
服务消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定 期更新
心跳(续约):微服务定期通过http方式向Eureka刷新自己的状态
Eureka主要包含两个组件:EurekaServer和EurekaClient
EurekaServer提供服务注册,各个微服务节点通过配置启动后,会在EurekaServer 中进行注册。这样EurekaServer 中的服务注册表 将会存储所有可用服务的节点信息,服务节点信息可以在eureka控制面板上看得到。
EurekaClient通过注册中心进行访问
通过EurekaClient这个客户端,我们可以和EurekaServer进行交互。EurekaClient内部内置了一个默认 使用轮询算法的负载均衡器。在应用启动之后,将会向EurekaServer发送心跳(默认时间周期是30秒)。 如果EurekaServer在多个心跳周期内没有收到某个节点的心跳,EurekaServer将会从服务注册表中把 这个服务节点移除,默认时间是90秒。
2.搭建Eureka中心
首先在pom文件中导入依赖
<dependencies>
<!--eureka服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--web启动器-->
<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>
</dependencies>
编写application.yml文件
server:
port: 7001
# 配置eureka服务端
eureka:
client:
register-with-eureka: false # 禁止自己注册自己
fetch-registry: false # 禁止抓取注册中心中的服务信息
service-url:
defaultZone: http://localhost:7001/eureka/ # eureka服务端的地址
编写启动类
@SpringBootApplication
@EnableEurekaServer //标识当前服务是eureka服务端
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
现在启动项目,访问7001端口,查看eureka服务端是否搭建成功:
我们访问到了eureka注册中心的控制台面板,说明我们的eureka注册中心搭建成功。
接下来就是对相应的服务提供方和服务消费方进行相应的配置。
首先是服务消费方
在pom文件中加上
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client artifactId>
<dependency>
在它的启动类上添加开启服务发现的注解:@EnableDiscoverClient
yml文件中要要加入
eureka:
client:
# 表示当前微服务是否向Eureka注册中心注册自己,设置为true即开启注册功能
register-with-eureka: true
# 表示客户端是否从Eureka注册中心获取服务注册列表,设置为true可获取
fetch-registry: true
service-url:
# defaultZone指定Eureka注册中心的地址,这里设置为本地7001端口的Eureka服务地址
defaultZone: http://localhost:7001/eureka/
接下来是服务消费方
导入依赖
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client artifactId>
<dependency>
然后在它的启动类上添加开启服务发现的注解:@EnableDiscoverClient
yml上加入
eureka:
client:
# 表示当前微服务是否向Eureka注册中心注册自己,设置为true即开启注册功能
register-with-eureka: true
# 表示客户端是否从Eureka注册中心获取服务注册列表,设置为true可获取
fetch-registry: true
service-url:
# defaultZone指定Eureka注册中心的地址,这里设置为本地7001端口的Eureka服务地址
defaultZone: http://localhost:7001/eureka/
之后启动项目,注册中心就会多了两个
3.eureka集群
Eureka Server即服务的注册中心,在刚才的案例中,我们只有一个EurekaServer,但是如果这个 EurekaServer挂掉了会影响整个应用。事实上EurekaServer也可以是一个集群,形成高可用的Eureka中 心,一个Eureka服务中心挂掉了还有其他的服务注册中心。
多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时, 该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
现在我们动手搭建高可用的EurekaServer,Eureka 集群的原理,就是 相互注册,互相守望。
和之前的搭建一样,我们只要把端口改了就行,这里我们改成7002
但是两个注册中心的defaultZone要改为对方的
就是进行互相注册
然后服务消费方和提供方的配置文件也要进行修改,也就是defaultZone要把两个注册中心的URL都写上就行了,用逗号隔开
4.服务集群
对服务提供方进行集群搭建
我们的微服务目前也只有一台服务,如果在生产环境中挂掉了,也不能对外继续提供服务,所以我们的服 务也可以进行集群。在这里我们以服务提供方为例。搭建两台服务提供方,另一台服务提供方的端口号为 8002。
yml部分内容
server:
port: 8001
spring:
application:
name: service-provider # 配置服务名称
控制面板如下
对服务消费方进行集群搭建
当我们有了两台服务提供方以后,我们需要改造服务消费端的服务调用方式。那么现在问题来了,我们应 该访问两台服务提供方的哪一台服务呢?以前在消费方指定访问的是8001端口的微服务。现在我们需要 修改访问提供方的访问规则,规则就是通过服务提供方的名称进行访问,不再通过服务提供方的ip+端口 进行访问。
我们修改服务消费方的控制器层代码:
// 表明这是一个 RESTful 风格的控制器,用于处理 HTTP 请求并返回 JSON 或 XML 等数据
@RestController
// 定义该控制器处理的请求的基础路径,所有该控制器下的请求都会以 "consumer" 开头
@RequestMapping("consumer")
// 抑制所有编译器警告,通常不建议滥用,仅在特定场景下使用
@SuppressWarnings("all")
public class PaymentController {
// 使用 Spring 的依赖注入机制,将 RestTemplate 对象注入到当前控制器中
// RestTemplate 是 Spring 提供的用于发送 HTTP 请求的工具类
@Autowired
RestTemplate restTemplate;
/**
* 根据 ID 查询支付信息的方法
* @param id 要查询的支付信息的 ID
* @return 包含支付信息的结果对象
*/
@RequestMapping("findById/{id}")
public Result<Payment> findById(@PathVariable("id") Long id) {
// 通过服务实例名称进行访问,构建请求的 URL
// 这里的 "SERVICE-PROVIDER" 是服务提供者的实例名称,在服务注册中心中注册的名称
// 拼接具体的请求路径和参数
String url = "http://SERVICE-PROVIDER/provider/findById?id=" + id;
// 使用 RestTemplate 发送 GET 请求到指定的 URL,并将响应结果映射为 Result 类型的对象
Result result = restTemplate.getForObject(url, Result.class);
// 返回查询结果
return result;
}
}
但此时必不能直接启动,因为仅仅根据代码中的url是不能知道调用哪台服务提供方的机器,所以我们要进行负载均衡的实现,实现它就是要实现负载均衡器。
@Configuration
publicclassMyConfig {
@Bean
@LoadBalanced
开启负载均衡的访问
publicRestTemplaterestTemplate(){
returnnewRestTemplate();
}
}
6.服务发现
有的时候,我们需要盘点一下我们eureka注册中心上到底有哪些服务实例,我们需要获取这些详细服务的 各种细节(比如IP地址 服务端口号等),这个时候我们就需要使用一个新的注解来开启服务发现功能,这个 注解就是EnableDiscoveryClient。对于注册进eureka里面的微服务,可以通过服务发现来获得该服务 的信息
首先:在微服务的启动类上面添加EnableDiscoveryClient注解。开启服务发现功能。
然后,消费方的控制器中定义如下逻辑
// 标识该类为 REST 风格的控制器,会将方法的返回值直接作为 HTTP 响应体返回
@RestController
// 为该控制器下的所有请求映射添加公共的请求路径前缀 "consumer"
@RequestMapping("consumer")
// 使用 Lombok 提供的 @Slf4j 注解,自动生成一个日志记录器对象 log,方便进行日志记录
@Slf4j
// 抑制编译器产生的所有警告信息,使用时需谨慎,可能会掩盖一些潜在问题
@SuppressWarnings("all")
public class PaymentController {
// 使用 @Resource 注解注入 DiscoveryClient 对象
// DiscoveryClient 是 Spring Cloud 提供的用于与服务注册中心交互的客户端,可用于获取服务注册信息
@Resource
private DiscoveryClient discoveryClient;
/**
* 获取服务发现信息的接口
* @return 返回 DiscoveryClient 对象,可用于前端进一步了解服务发现的情况
*/
@GetMapping("/customer/discovery")
public Object discovery() {
// 通过 DiscoveryClient 获取服务注册中心中所有已注册的服务名称列表
List<String> services = discoveryClient.getServices();
// 遍历服务名称列表,并使用日志记录每个服务的名称
for (String service : services) {
log.info("service:" + service);
}
// 根据服务提供方的名称获取对应的服务实例信息
// 这里假定服务提供方在服务注册中心注册的名称是 "service-provider"
List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
// 遍历服务实例列表,获取每个服务实例的详细信息
for (ServiceInstance instance : instances) {
// 获取服务实例的主机地址
String host = instance.getHost();
// 获取服务实例的端口号
int port = instance.getPort();
// 获取服务实例的服务 ID
String serviceId = instance.getServiceId();
// 使用日志记录每个服务实例的主机地址、端口号和服务 ID
log.info("host:" + host + " port:" + port + " serviceId:" + serviceId);
}
// 返回 DiscoveryClient 对象,前端可以根据该对象获取更多服务发现相关的信息
return this.discoveryClient;
}
}
7.Eureka的自我保护机制
Eureka服务端会检查最近15分钟内所有Eureka 实例正常心跳占比,如果低于85%就会触发自我保护机 制。触发了保护机制,Eureka将暂时把这些失效的服务保护起来,不让其过期,但这些服务也并不是永远 不会过期。Eureka在启动完成后,每隔60秒会检查一次服务健康状态,如果这些被保护起来失效的服务 过一段时间后(默认90秒)还是没有恢复,就会把这些服务剔除。如果在此期间服务恢复了并且实例心跳 占比高于85%时,就会自动关闭自我保护机制。
为什么会有自我保护机制? Eureka服务端为了防止Eureka客户端本身是可以正常访问的,但是由于网路通信故障等原因,造成 Eureka服务端失去于客户端的连接,从而形成的不可用。
因为网络通信是可能恢复的,但是Eureka客户端只会在启动时才去服务端注册。如果因为网络的原因而剔 除了客户端,将造成客户端无法再注册到服务端。
如何选择关闭还是开启自我保护机制?Eureka服务端默认情况下是会开启自我保护机制的。但我们在不同 环境应该选择是否开启保护机制。一般情况下,我们会选择在开发环境下关闭自我保护机制,而在生产环 境下启动自我保护机制。
开发环境下,我们启动的服务数量较少而且会经常修改重启。如果开启自我保护机制,很容易触发 Eureka客户端心跳占比低于85%的情况。使得Eureka不会剔除我们的服务,从而在我们访问的时候,会 访问到可能已经失效的服务,导致请求失败,影响我们的开发。
在生产环境下,我们启动的服务多且不会反复启动修改。环境也相对稳定,影响服务正常运行的人为情况 较少。适合开启自我保护机制,让Eureka进行管理。
如何关闭自我保护机制:
在Eureka服务中心进行配置:
eureka:
server:
#服务端是否开启自我保护机制(默认true)
enable-self-preservation: false
#扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒
eviction-interval-timer-in-ms: 2000
在Eureka客户端进行配置:
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka, http//localhost:7002/eureka
instance:
#客户端向注册中心发送心跳的时间间隔,(默认30秒)
lease-renewal-interval-in-seconds: 1
#Eureka注册中心(服务端)在收到客户端心跳之后,等待下一次心跳的超时时间,如果在这个时间内没有收到下次心跳,则移除该客户端。(默认90秒)
lease-expiration-duration-in-seconds: 2