Spring Cloud 微服务注册中心(Eureka)

Spring Cloud 微服务注册中心(Eureka)

Eureka,服务注册和发现,它提供了一个服务注册中心、服务发现的客户端,还有一个方便的查看所有注册服务的界面。

所有的服务使用Eureka的服务发现客户端来将自己注册到Eureka的服务器上。

一 基础架构

图1:角色解释:

  • Eureka Server:提供服务注册和发现
  • Service Provider:服务提供方,将自身服务注册到Eureka。服务注册、服务续约、服务下线。
  • Service Consumer:服务消费方,从Eureka获取注册服务列表,从而能够消费服务。

Eureka支持RegionZone的概念,其中一个Region可以包含多个Zone。Eureka在启动时需要指定一个Zone名,即当前Eureka属于哪个zone, 如果不指定则属于defaultZone。Eureka Client也需要指定Zone, Client(当与Ribbon配置使用时)在向Server获取注册列表时会优先向自己Zone的Eureka发请求,如果自己Zone中的Eureka全挂了才会尝试向其它Zone。当获取到远程服务列表后,Client也会优先向同一个Zone的服务发起远程调用。Region和Zone可以对应于现实中的大区和机房。

二 实战

2.1 Eureka单机示例

2.1.1 服务端

pom.xml: maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Camden.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

EurekaApplication.java: 启动函数

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

application.properties: 配置文件

server.port=9001
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.preferIpAddress=true
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.server.enableSelfPreservation=false

2.1.2 客户端

pom.properites: maven依赖

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

UserApplication.java: 启动函数

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

application.properties: 配置文件

spring.application.name=user-service
spring.cloud.config.profile=dev
spring.profiles.active=dev
eureka.client.serviceUrl.defaultZone=http://localhost:9001/eureka/

先后启动服务端和客户端,访问 http://localhost:9001/,可以在eureka界面看到,服务user-service注册成功如图。

2.2 高可用集群

注册中心单机,如果挂了,将导致整个服务不可用。生产环境不可能只有一个实例。 下图为官网给出的高可用集群架构图。

集群配置很简单,比如上图有3个节点us-east-1c,us-east-1d,us-east-1e。配置集群只需要把分别把自己作为客户端,其他两个节点当作服务端即可。 在2.1.1 的基础上,添加application-peer1.propertiesapplication-peer2.properties:

server.port=9011
eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:9111/eureka/
server.port=9111
eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone=http://peer1:9011/eureka/

修改hosts:

127.0.0.1 peer1
127.0.0.1 peer2

分别通过spring.profiles.active=peer1spring.profiles.active=peer2启动。

注意:如果使用localhost,将出现unavailable-replicas的情况。

三 原理分析

两个重要类:客户端:DiscoveryClient,服务端:ApplicationResourcs。 DiscoveryClient在Eureka-client(jar包)里。 提共一下功能:

  • Registering:注册
  • Renewal:续约
  • Cancellation:取消
  • Querying:查询已注册的服务列表

3.1 注册续约

客户端 DiscoveryClient通过initScheduledTasks()初始化定时任务。

  • shouldRegisterWithEureka:注册判断

  • renewalIntervalInSecs : 续约间隔(即心跳间隔,默认30s)等其他客户端和实例配置信息

  • 心跳定时任务;注册instanceInfoReplicator.run()

  • register()方法中发起http请求注册,传入instanceInfo实体。

    httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
    

    涉及配置:

    # 续租间隔
    eureka.instance.lease-renewal-interval-in-seconds=30
    # 服务失效时间。默认是90秒
    eureka.instance.lease-expiration-duration-in-seconds=90
    

    其实注册和续约是一个逻辑。

服务端

  • ApplicationResource类接收Http服务请求,调用PeerAwareInstanceRegistryImpl的register方法

  • PeerAwareInstanceRegistryImpl完成服务注册后,调用replicateToPeers向其它Eureka Server节点(Peer)做状态同步(异步操作) 注册的服务列表保存在一个嵌套的hash map中:

  • 第一层hash map的key是app name,也就是应用名字

  • 第二层hash map的key是instance name,也就是实例名字 USER-SERVICE就是app name,192.168.1.106:user-service:9002就是instance name。

下图为renew服务端实现:

3.2 获取服务列表

客户端 和注册在DiscoveryClient的方法initScheduledTasks()中定义。

  1. shouldFetchRegistry:
  2. 设置配置。
  3. 定时任务CacheRefreshThread()

涉及配置:

# 是否获取服务列表
eureka.client.fetch-registry=true
# 更新间隔
eureka.client.registry-fetch-interval-seconds=30

3.3 peer 节点通讯

Eureka Server在启动后会调用EurekaClientConfig.getEurekaServerServiceUrls来获取所有的Peer节点,并且会定期更新。定期更新频率可以通过eureka.server.peerEurekaNodesUpdateIntervalMs配置。 这个方法的默认实现是从配置文件读取,所以如果Eureka Server节点相对固定的话,可以通过在配置文件中配置来实现。 如果希望能更灵活的控制Eureka Server节点,比如动态扩容/缩容,那么可以override getEurekaServerServiceUrls方法,比如从数据库读取Eureka Server列表。下图为一个服务节点的初始化过程。


参考文档: [1].http://nobodyiam.com/2016/06/25/dive-into-eureka/

源码部分参考此文档

CONTENTS