一、Spring Cloud Eureka简介

在微服务架构中往往会有一个注册中心,每个微服务都会向注册中心去注册自己的地址及端口信息,注册中心维护着服务名称与服务实例的对应关系。每个微服务都会定时从注册中心获取服务列表,同时汇报自己的运行情况,这样当有的服务需要调用其他服务时,就可以从自己获取到的服务列表中获取实例地址进行调用,Eureka实现了这套服务注册与发现机制。

Spring Cloud Eureka 是 Spring Cloud Netflix 子项目的核心组件之一,主要用于微服务架构中的服务治理。

Eureka 是一个基于 REST 的服务,主要在 AWS 云中使用,定位服务来进行中间层服务器的负载均衡和故障转移。

Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。

Eureka由两个组件组成:Eureka serverEureka client。Eureka server用作服务注册服务器。Eureka client是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。

最简单的微服务架构图:
006.webp

  • Eureka Server(注册中心): 提供服务注册和发现
  • Service Provider(服务提供方): 将自身服务注册到Eureka中心,从而使服务消费方能够找到
  • Service Consumer(服务消费方): 从Eureka注册中心获取注册服务列表,从而能够消费服务

二、Eureka注册中心

首先创建注册中心,具体流程如下:

  1. 创建一个eureka-server模块,并使用Spring Initializer初始化一个SpringBoot项目
  2. 在Dependencies中选择需要的SpringCloud组件进行创建,这里需要 Spring Cloud Discovery -> Eureka Server

相关依赖:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5. </dependency>
  6. </dependencies>

在启动类上添加@EnableEurekaServer注解来启用Euerka注册中心功能

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

在配置文件application.yml中添加Eureka注册中心的配置:

server:
  port: 8001 #指定运行端口
spring:
  application:
    name: eureka-server #指定服务名称
eureka:
  instance:
    hostname: ${eureka.instance.hostname} #指定主机地址
  client:
    fetch-registry: false #指定是否要从注册中心获取服务(注册中心不需要开启)
    register-with-eureka: false #指定是否要注册到注册中心(注册中心不需要开启)
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    enable-self-preservation: false #关闭保护模式

启动:
Snipaste_2021-01-16_19-31-29.png

三、Eureka客户端

Eureka客户端(eureka client)又称为服务提供者, 我们可以把服务注册到注册中心提供给消费者使用。

  1. 创建一个eureka-client模块,并使用Spring Initializer初始化一个SpringBoot项目
  2. 在Dependencies中选择需要的SpringCloud组件进行创建,这里需要 Web -> Spring Web 和 Spring Cloud Discovery -> Eureka Discovery Client

需要的依赖如下:

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

在启动类上添加@EnableDiscoveryClient注解表明是一个Eureka客户端

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

在配置文件application.yml中添加Eureka客户端的配置

server:
  port: 8101 #运行端口号
spring:
  application:
    name: eureka-client #服务名称
eureka:
  client:
    register-with-eureka: true #注册到Eureka的注册中心
    fetch-registry: true #获取注册实例列表
    service-url:
      defaultZone: http://localhost:8001/eureka/

在注册中心中看到:
004.png

如果要连接到多个Eureka注册中心,多个注册中心之间用逗号,分割:

eureka:
  client:
    register-with-eureka: true #注册到Eureka的注册中心
    fetch-registry: true #获取注册实例列表
    service-url:
      defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/,http://localhost:8003/eureka/ #同时注册到多个配置注册中心

四、Eureka注册中心集群

在一个分布式系统中,服务注册中心是最重要的基础部分,理应随时处于可以提供服务的状态。若是单点部署,如果注册中心出现故障,将会出现毁灭性的灾难。为了维持其可用性,使用集群是很好的解决方案。Eureka通过互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署。

新建集群配置

假设我们要部署三个节点的注册中心。

新建集群配置profiles: application-replica1.ymlapplication-replica2.ymlapplication-replica3.yml

首先在hosts中添加:

127.0.0.1       localhost replica1 replica2 replica3

application-replica1.yml 中:

server:
  port: 8001
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: replica1
  client:
    fetch-registry: false
    register-with-eureka: false
    serviceUrl:
      #设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka。多个地址可使用,分隔。
      defaultZone: http://replica2:8002/eureka/,http://replica3:8003/eureka/

application-replica2.yml 中:

server:
  port: 8002
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: replica2
  client:
    fetch-registry: false
    register-with-eureka: false
    serviceUrl:
      defaultZone: http://replica1:8001/eureka/,http://replica3:8003/eureka/

application-replica3.yml 中:

server:
  port: 8003
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: replica3
  client:
    fetch-registry: false
    register-with-eureka: false
    serviceUrl:
      defaultZone: http://replica1:8001/eureka/,http://replica2:8002/eureka/

在Idea中运行

在IDEA中我们可以通过使用不同的配置文件来启动同一个SpringBoot应用。

添加两个配置,分别以 application-replica1.ymlapplication-replica2.ymlapplication-replica3.yml 来启动eureka-server

将Active Profiles修改为配置文件的后缀即可,这里是replica1replica2replica3
002.png

打包运行

在eureka-server模块下的 pom.xml 中加入

<!-- eureka集群使用 Start-->
<profiles>
  <profile>
    <id>replica1</id>
    <properties>
      <profiles.active>replica1</profiles.active>
      <env>replica1</env>
    </properties>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
  </profile>
  <profile>
    <id>replica2</id>
    <properties>
      <profiles.active>replica2</profiles.active>
      <env>replica2</env>
    </properties>
  </profile>
  <profile>
    <id>replica3</id>
    <properties>
      <profiles.active>replica3</profiles.active>
      <env>replica3</env>
    </properties>
  </profile>
</profiles>
<!-- eureka集群使用 end-->

然后打包:

mvn clean package
mvn install

运行测试:

java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=replica1
java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=replica2
java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=replica3

通过 http://localhost:8001/ http://localhost:8002/ http://localhost:8003/ 可以看到:
003.png

可以在hosts中将 replica1 replica2 replica3 配置到 127.0.0.1, 这样就可以模拟使用域名进行访问。

五、添加登录授权模块

在eureka-server中添加登录授权模块依赖:

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

在配置中加入spring.security节点:

server:
  port: 8001
spring:
  application:
    name: eureka-server
  security: #配置SpringSecurity登录用户名和密码
    user:
      name: admin
      password: 123456

这样,访问eureka-server的时候就需要登录验证了:
005.png

添加WebSecurityConfig

默认情况下添加SpringSecurity依赖的应用每个请求都需要添加CSRF token才能访问,Eureka客户端注册时并不会添加,所以需要配置/eureka/**路径不需要CSRF token。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

客户端注册到有登录认证的注册中心

配置文件中需要修改注册中心地址格式

http://${username}:${password}@${hostname}:${port}/eureka/

修改客户端的配置文件,按格式修改用户名和密码

server:
  port: 8101
spring:
  application:
    name: eureka-client
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://admin:123456@localhost:8001/eureka/

六、Eureka的常用配置

eureka:
  client: #eureka客户端配置
    register-with-eureka: true #是否将自己注册到eureka服务端上去(注册中心不需要开启)
    fetch-registry: true #是否获取eureka服务端上注册的服务列表(注册中心不需要开启)
    service-url:
      defaultZone: http://localhost:8001/eureka/ #指定注册中心地址
    enabled: true #启用eureka客户端
    registry-fetch-interval-seconds: 30 #定义去eureka服务端获取服务列表的时间间隔
  instance: #eureka客户端实例配置
    lease-renewal-interval-in-seconds: 30 #定义服务多久去注册中心续约
    lease-expiration-duration-in-seconds: 90 #定义服务多久不去续约认为服务失效
    metadata-map:
      zone: yunnan #所在区域
    hostname: eureka-server #服务主机名称
    prefer-ip-address: false #是否优先使用ip来作为主机名
  server: #eureka服务端配置
    enable-self-preservation: false #关闭eureka服务端的保护机制

常见错误

com.netflix.discovery.shared.transport.TransportException: Retry limit reached; giving up on complet

  1. 客户端服务启动出错:

检查 eureka.client.serviceUrl.defaultZone 配置是否正确

  1. 服务端服务启动出错:

检查eureka是否配置防止自身注册:

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

参考:com.netflix.discovery.shared.transport.TransportException: Retry limit reached; giving up on complet

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

参考:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

Connect to localhost:8761 time out

没有设置defaultZone,如果是一个注册中心,需要注册到本身,完整的配置如下:

server:
  port: 8001
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: replica1
  client:
    fetch-registry: false
    register-with-eureka: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

参考:Eureka 注册中心一直报Connect to localhost:8761 time out 的问题

参考资料