Skip to content
xiaoyang 的头像xiaoyang发布于 2024-05-176,887 字阅读约 23 分钟浏览加载中访客加载中

3.Spring Cloud Eureka:构建可靠的服务注册与发现 ​

Eureka一词源自古希腊词汇,表示"发现了"。在软件领域,Eureka是由Netflix开发的一款开源的服务注册与发现组件。Spring Cloud将Eureka与Netflix中的其他开源服务组件整合进Spring Cloud Netflix模块中,形成了Spring Cloud Netflix Eureka,用于实现Spring Cloud的服务注册与发现功能。

1. Eureka 两大组件 ​

Eureka采用了Client/Server(客户端/服务器)架构,包括两个主要组件:Eureka Server和Eureka Client。

  • Eureka Server:作为服务注册中心,用于提供服务注册功能。当微服务启动时,会将自己的服务注册到Eureka Server。Eureka Server维护了一个可用服务列表,包含了所有已注册到Eureka Server的可用服务的信息。
  • Eureka Client:表示微服务系统中的各个微服务,用于与Eureka Server进行交互。微服务启动后,Eureka Client会定期向Eureka Server发送心跳,以表明自己的存活状态。如果Eureka Server在多个心跳周期内没有收到某个Eureka Client的心跳,将把该服务从可用服务列表中移除。

2. Eureka的服务注册与发现 ​

Eureka实现了服务注册与发现的功能,具体流程如下图所示:

图1:Eureka服务注册与发现原理图

  • 服务注册中心(Register Service):作为Eureka Server,用于提供服务注册和发现功能。
  • 服务提供者(Provider Service):作为Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
  • 服务消费者(Consumer Service):也是Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,并调用所需的服务。
  1. Eureka的作用 Eureka在微服务架构中扮演着重要角色,具有以下功能:

    • 服务注册:服务提供者将自己的服务注册到Eureka Server,从而使服务消费者能够发现和调用该服务。
    • 服务发现:服务消费者从Eureka Server获取可用服务列表,以便选择合适的服务进行调用。
    • 服务健康检查:Eureka Client定期向Eureka Server发送心跳,用于确认自身的健康状态。如果Eureka Server连续多个心跳周期未收到某个服务的心跳,将剔除该服务实例。
    • 高可用性:通过互相注册,多个Eureka Server实例可以实现高可用性。当某个Eureka Server不可用时,其他可用的服务器可以接管其职责。

3. 代码示例 ​

下面,我们通过一个案例来展示下为什么使用 Eureka ?以及Eureka是如何实现服务注册与发现的。

3.1 搭建服务注册中心 ​

在主工程下创建一个名为 cloud-eureka-7001 的 Spring Boot Module 作为服务注册中心,可以直接选择eureka-server依赖快速构建,或者并在其 pom.xml 中引入以下依赖。

xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloud-eureka-7001</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-eureka-7001</name>
    <description>cloud-eureka-7001</description>
    <parent>
        <groupId>cn.ywenrou</groupId>
        <artifactId>spring-cloud-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>

然后在resource目录下创建application.yml,配置如下:

yml
server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #设置与eureka  server交互的地址和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/  #单机就是指向自己

最后在启动类上加上@EnableEurekaServer注解并启动:

java
package cn.ywenrou.cloudeureka7001;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class CloudEureka7001Application {

    public static void main(String[] args) {
        SpringApplication.run(CloudEureka7001Application.class, args);
    }

}

访问http://localhost:7001/,请注意此时注册服务为空:

3.2 注册服务 ​

在上一章中我们引出了存在的问题,当消费者需要调用生产者的API时,硬编码主机名和端口号是一种不灵活的做法。为了解决这个问题,Spring Cloud提供了Eureka作为服务注册中心,以便消费者能够动态地发现和调用生产者的服务。所以我们现在需要将上一张的单体服务中修改下配置。

首先模块cloud-service1和cloud-service2在pom.xml文件中引入下面依赖:

xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然后在application.yml新增eureka配置:

yml
server:
  port: xxx
#spring 配置
spring:
  application:
    name: cloud-servicex
#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #单机配置
      defaultZone: http://localhost:7001/eureka

最后在启动类上加入@EnableEurekaClient注解并启动项目,访问http://localhost:7001/,可以看到2个服务实例注册进来:

3.3 修改调用调用服务方式 ​

我们首先展示如何在不使用负载均衡的情况下,通过服务名进行服务调用。为此,我们需要在 config 包下创建一个配置类,以便将 RestTemplate 注入为 Spring 容器中的一个 bean。

3.3.1 配置 RestTemplate ​

java
package cn.ywenrou.cloudservice2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
    
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

3.3.2 修改消费者的服务调用方式 ​

在消费者服务中,通过 DiscoveryClient 获取服务实例列表,并选择第一个实例进行调用。在这里你会发现,虽然我们不再使用传统的硬编码主机名和端口号的方式,而是采用动态方式进行调用,但这个过程依然比较复杂。我们选择第一个实例进行调用是为了演示方便,但在实际项目中,一个服务通常会部署多个实例以提高系统的可靠性和可用性。仅使用第一个实例进行调用是简化后的示例,在真实场景中应使用更高级的负载均衡策略来分配请求。

java
package cn.ywenrou.cloudservice2.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
class ConsumerController {

    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value="/service2/hello")
    public String helloController() {
        // 获取服务实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-service1");
        if (instances == null || instances.isEmpty()) {
            throw new RuntimeException("No instances found for service: cloud-service1");
        }

        // 选择第一个实例进行调用
        ServiceInstance instance = instances.get(0);
        System.out.println("服务地址:" + instance.getUri());

        String url = instance.getUri().toString() + "/service1/hello";

        // 使用RestTemplate进行调用
        return restTemplate.getForObject(url, String.class);
    }
}

3.4 构建Eureka集群:实现负载均衡与故障容错 ​

在微服务架构中,高可用性是至关重要的。如果我们只搭建了Eureka的单机版,一旦该节点宕机,整个服务将无法使用。为了解决这个问题,我们需要搭建Eureka的集群版,通过实现负载均衡与故障容错来确保微服务的可靠性和稳定性。其实在微服务中每个单体都是可以作为消费者和生成者,实际上就是启动多个 Eureka 实例,多个 Eureka 实例之间,互相注册,互相同步数据,共同组成一个 Eureka 集群。

例如我们要搭建三个Eureka组成集群,我们只需要再创建cloud-eureka-7002 和cloud-eureka-7003步骤和之前一样,选择依赖可以快速构建:

首先修改电脑的host文件的主机映射:

修改这三个Eureka单体的application.yml,这里以cloud-eureka-7002为例:

yml
server:
  port: 7002
spring:
  application:
    name: eureka-server # eureka服务集群的name要相同
eureka:
  client:
    register-with-eureka: true # 将eureka服务器注册为服务
    fetch-registry: true # 获取注册信息
    service-url:
      defaultZone: 'http://eureka7001.com:7001/eureka,http://eureka7003.com:7003/eureka' # 将注册服务器地址写成另外两个集群的地址,此处用eureka7001.com和eureka7003.com代理localhost或127.0.0.1
  instance:
    hostname: eureka7002.com # 注意hostname不能为空

然后全部启动服务,这里以打开端口为7002的eureka服务为例http://localhost:7002/,可以看到集群已经搭建成功。

4. 总结 ​

Spring Cloud Eureka是Netflix开源的服务注册与发现组件,通过Eureka Server和Eureka Client的配合,可以实现微服务架构中的服务注册与发现功能。Eureka的存在简化了服务管理和调用过程,提供了可靠的服务注册与发现机制。在前面我们提到,一个服务通常会部署多个实例以提高系统的可靠性和可用性。传统方式调用一个服务需要先获取服务实例列表,再选择一个实例进行调用,这个过程既繁琐又复杂,尤其是在选择合适的实例时。为了简化这一过程,引出Ribbon。

DISCUSSION

评论区

欢迎分享你的思考、补充或问题。

评论加载中…

基于 VitePress 搭建