MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Eureka 实现微服务服务发现

2023-07-175.7k 阅读

1. 微服务架构中的服务发现概述

在微服务架构中,服务实例的数量和位置可能会动态变化。例如,为了应对高流量,一个微服务可能会启动多个实例,而在流量较低时,一些实例又可能被关闭。同时,新的微服务实例可能在不同的服务器或容器中启动,其IP地址和端口号也会有所不同。在这种复杂的环境下,服务之间如何找到彼此并进行通信就成为了一个关键问题。

服务发现就是解决这一问题的关键机制。它允许微服务在运行时动态地注册自己的位置信息,并让其他微服务能够查询到这些信息,从而实现服务之间的相互调用。服务发现主要包含两个核心组件:服务注册中心和服务提供者、消费者。

服务注册中心负责维护服务实例的注册表,记录每个服务的名称、地址、端口等信息。服务提供者在启动时,会将自身的相关信息注册到服务注册中心。而服务消费者在调用其他服务时,会向服务注册中心查询目标服务的实例地址,然后根据这些地址发起实际的调用。

2. Eureka简介

Eureka 是 Netflix 开源的一款服务发现组件,现在是 Spring Cloud Netflix 项目中的重要部分。它基于 RESTful 接口,提供了简单易用的服务发现功能,在微服务架构中被广泛应用。

Eureka 包含两个核心组件:Eureka Server 和 Eureka Client。Eureka Server 作为服务注册中心,负责接收、存储和管理服务实例的注册信息。Eureka Client 则是运行在各个微服务实例中的客户端组件,它负责与 Eureka Server 进行交互,包括服务注册、心跳检测以及获取服务实例列表等操作。

Eureka 的设计理念是去中心化的,它没有主从之分,多个 Eureka Server 之间可以相互注册,形成一个对等的集群。这样可以提高服务注册中心的可用性和容错性,即使部分 Eureka Server 实例出现故障,其他实例仍然可以继续提供服务发现功能。

3. Eureka Server 的搭建

3.1 创建Spring Boot项目

首先,我们需要创建一个Spring Boot项目来搭建Eureka Server。可以通过Spring Initializr(https://start.spring.io/)来快速创建项目。在创建项目时,选择Spring Web和Eureka Server依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

3.2 配置Eureka Server

application.yml文件中进行Eureka Server的配置:

server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

上述配置中,server.port指定了Eureka Server的端口为8761。eureka.instance.hostname设置了实例的主机名为localhosteureka.client.register-with-eurekaeureka.client.fetch-registry都设置为false,这是因为该实例本身就是Eureka Server,不需要向其他Eureka Server注册自己,也不需要从其他Eureka Server获取服务注册信息。eureka.client.service-url.defaultZone定义了Eureka Server的服务地址。

3.3 启用Eureka Server

在Spring Boot的主类上添加@EnableEurekaServer注解来启用Eureka Server功能:

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

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

启动项目后,访问http://localhost:8761/,可以看到Eureka Server的管理界面。此时,Eureka Server已经搭建完成,但还没有任何服务注册进来。

4. 微服务作为Eureka Client注册到Eureka Server

4.1 创建微服务项目

同样通过Spring Initializr创建一个Spring Boot微服务项目,添加Spring Web和Eureka Client依赖:

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

4.2 配置Eureka Client

application.yml文件中配置Eureka Client相关信息:

server:
  port: 8081
spring:
  application:
    name: user-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

这里server.port指定了微服务的端口为8081,spring.application.name设置了微服务的名称为user-serviceeureka.client.service-url.defaultZone指定了Eureka Server的地址。

4.3 启用Eureka Client

在微服务的主类上添加@EnableEurekaClient注解,使该微服务能够作为Eureka Client向Eureka Server注册:

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

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

启动微服务后,在Eureka Server的管理界面中可以看到user-service已经注册成功,并且显示了该服务实例的相关信息,如IP地址、端口等。

5. Eureka的服务发现机制原理

5.1 服务注册

当Eureka Client启动时,它会通过HTTP请求向Eureka Server发送自身的注册信息,包括服务名称、IP地址、端口、健康检查URL等。Eureka Server接收到注册请求后,会将这些信息存储在其内部的注册表中。

Eureka Client会定期(默认30秒)向Eureka Server发送心跳请求,以表明自己仍然存活。如果Eureka Server在一定时间(默认90秒)内没有收到某个服务实例的心跳,就会认为该实例已经失效,并将其从注册表中移除。

5.2 服务发现

Eureka Client在启动时,会从Eureka Server获取所有服务的实例列表,并缓存在本地。当Eureka Client需要调用其他服务时,它会首先从本地缓存中查找目标服务的实例列表。如果本地缓存中的实例列表过期(默认30秒),Eureka Client会再次从Eureka Server获取最新的实例列表。

Eureka Server之间会通过Peer-to-Peer复制机制来同步注册表信息。当一个Eureka Server接收到新的服务注册或实例状态变更时,它会将这些信息同步给其他Eureka Server,以保证所有Eureka Server的注册表信息一致。

5.3 自我保护机制

Eureka有一个自我保护机制,旨在防止因网络分区等原因导致Eureka Server误判服务实例已失效而将其从注册表中移除。当Eureka Server在一定时间内没有收到足够数量的心跳时,会进入自我保护模式。

在自我保护模式下,Eureka Server不会轻易移除任何服务实例,即使这些实例可能已经失效。这是为了避免在网络不稳定或短暂故障期间,将正常运行的服务实例误删除,从而保证服务的可用性。当网络恢复正常,Eureka Server接收到足够数量的心跳后,会自动退出自我保护模式。

6. 基于Eureka的服务调用

6.1 使用RestTemplate进行服务调用

在微服务中,可以使用Spring的RestTemplate来调用其他已注册到Eureka Server的服务。首先,在配置类中创建RestTemplate的Bean,并开启负载均衡功能:

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

@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在需要调用其他服务的业务代码中,可以通过服务名称来调用:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/user")
    public String getUser() {
        String url = "http://order-service/order";
        return restTemplate.getForObject(url, String.class);
    }
}

这里通过http://order-service/order来调用名为order-service的微服务的/order接口。Eureka Client会根据服务名称从Eureka Server获取order-service的实例列表,并通过负载均衡算法选择一个实例进行调用。

6.2 Ribbon负载均衡

Ribbon是一个客户端负载均衡器,它与Eureka紧密集成。当Eureka Client从Eureka Server获取到服务实例列表后,Ribbon会根据一定的负载均衡算法(如轮询、随机等)选择一个实例进行调用。

可以通过在配置文件中指定Ribbon的负载均衡策略。例如,使用随机策略:

order-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

7. Eureka的高级配置与优化

7.1 配置Eureka Server集群

为了提高Eureka Server的可用性,通常会搭建Eureka Server集群。假设我们有两个Eureka Server实例,分别运行在8761和8762端口。

对于第一个Eureka Server(端口8761)的application.yml配置如下:

server:
  port: 8761
eureka:
  instance:
    hostname: eureka1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka2:8762/eureka/

对于第二个Eureka Server(端口8762)的application.yml配置如下:

server:
  port: 8762
eureka:
  instance:
    hostname: eureka2
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka1:8761/eureka/

同时,需要在本地hosts文件中添加如下映射:

127.0.0.1 eureka1
127.0.0.1 eureka2

这样两个Eureka Server实例就可以相互注册,形成一个集群。微服务在注册时,可以将多个Eureka Server的地址都配置到eureka.client.service-url.defaultZone中,以提高注册的可靠性。

7.2 调整Eureka的心跳和过期时间

可以根据实际业务场景调整Eureka的心跳和过期时间。例如,缩短心跳间隔和过期时间,以更快地检测到服务实例的故障:

eureka:
  instance:
    lease-renewal-interval-in-seconds: 10
    lease-expiration-duration-in-seconds: 20

上述配置将心跳间隔设置为10秒,过期时间设置为20秒。但需要注意的是,缩短这些时间可能会增加网络流量和系统开销,在生产环境中需要谨慎调整。

7.3 配置Eureka的安全认证

在生产环境中,为了保证Eureka Server的安全性,可以配置安全认证。可以使用Spring Security来实现Eureka Server的基本认证。

首先添加Spring Security依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后在application.yml中配置用户名和密码:

spring:
  security:
    user:
      name: admin
      password: password

接着在配置类中配置Spring Security:

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 SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
          .authorizeRequests()
          .anyRequest().authenticated()
          .and()
          .httpBasic();
    }
}

对于Eureka Client,也需要相应地配置认证信息:

eureka:
  client:
    service-url:
      defaultZone: http://admin:password@eureka1:8761/eureka/,http://admin:password@eureka2:8762/eureka/

8. Eureka在实际项目中的应用案例

假设我们正在开发一个电商系统,其中包含用户服务、订单服务、商品服务等多个微服务。

用户服务需要调用订单服务来获取用户的订单信息,订单服务需要调用商品服务来获取商品的详细信息。通过将这些微服务都注册到Eureka Server,它们可以实现动态的服务发现和相互调用。

例如,当用户在前端发起查询订单请求时,用户服务会从Eureka Server获取订单服务的实例列表,并通过负载均衡选择一个订单服务实例进行调用。订单服务在处理请求时,又会从Eureka Server获取商品服务的实例列表,调用商品服务获取商品信息,然后将订单信息和商品信息整合返回给用户服务,最终用户服务将结果返回给前端。

在这个过程中,Eureka Server保证了各个微服务之间能够准确地发现彼此,并且通过负载均衡和自我保护等机制,提高了整个系统的可用性和稳定性。同时,通过Eureka Server的管理界面,运维人员可以方便地监控各个微服务的运行状态,及时发现和解决问题。

在实际项目中,还可以结合其他组件,如断路器(Hystrix)、配置中心(Spring Cloud Config)等,进一步完善微服务架构的功能和性能,构建出健壮、可扩展的分布式系统。

通过以上对Eureka实现微服务服务发现的详细介绍,包括搭建Eureka Server、微服务注册与发现、服务调用、高级配置以及实际应用案例等方面,相信读者对Eureka在微服务架构中的作用和使用方法有了较为深入的理解,可以在自己的项目中更好地应用Eureka来实现高效的服务发现功能。