Spring Cloud 教程第四章 Eureka 的原理与实现机制

JAVA herman 77浏览 0评论

前面的 3 章我们都在说 Eureka 是如何使用的,但是在面试中很少有人会问到你具体的如何配置和使用,而是深入原理问你 Eureka 是如何实现的?那么本文就结合前面的一些章节,来一起讨论讨论 Eureka 的实现原理与机制。

先来张图:

Eureka 集群

上面这张图,可能大家都看过。在实际的应用中,Eureka 是以集群的方式存在的。然后 Service Provider:服务提供方,将自身服务注册到 Eureka,从而使服务消费方能够找到。Service Consumer:服务消费方,从 Eureka 获取注册服务列表,从而能够消费服务。Eureka Server:提供服务注册和发现。消费方会缓存一些服务提供方,消费方获得服务列表后,直接调用服务提供方,而不是通过 Eureka Server 去调用。服务提供方和 Eureka Server 会实时通过心跳来保活,保证每个服务的可用性。

注意,上面只是一个逻辑图。实际的使用中,一个服务可能即是消费方又是提供方,同时可能还具备服务的发现功能。

下面再来看第二张图:

Eureka 的服务发现、注册、续约、下线

上面的这张图显示,Eureka 集群之间会进行数据同步。服务提供方会进行Register(服务注册)、Renew(服务续约)、Cancel(服务下线)等操作。Service Consumer 会向 Eureka Server 获取注册服务列表,并通过 HTTP Rest 远程调用的方式消费服务。

看懂这几张图后,我们可以深入到 Eureka 源码中去。Eureka 其实就是一个简单的 Servlet 应用,它使用 Jersey 框架实现自身的 RESTful HTTP接口,peer之间的同步与服务的注册全部通过 HTTP 协议实现,定时任务(发送心跳、定时清理过期服务、节点同步等)通过 JDK 自带的 Timer 实现,内存缓存使用 Google 的 guava 包实现。

大家可以自己到 github 上下载 Eureka 源码,自己编译测试测试。看我下载的其中一个版本的核心代码结构如下:

Eureka 源码结构

eureka-core 模块包含了功能的核心实现:

  • com.netflix.eureka.cluster – 与peer节点复制(replication)相关的功能 
  • com.netflix.eureka.lease – 即”租约”, 用来控制注册信息的生命周期(添加、清除、续约) 
  • com.netflix.eureka.registry – 存储、查询服务注册信息 
  • com.netflix.eureka.resources – RESTful风格中的”R”, 即资源。相当于SpringMVC中的Controller 
  • com.netflix.eureka.transport – 发送HTTP请求的客户端,如发送心跳 
  • com.netflix.eureka.aws – 与amazon AWS服务相关的类

通过 Octotree 查看 github 上的源码结构,Eureka 的源代码并没有多少。

Eureka 源码

由于是 Servlet 应用,所以 Eureka 需要通过 Servlet 的相关监听器 ServletContextListener 嵌入到 Servlet 的生命周期中。EurekaServerBootStrap 类实现了该接口,在 servlet 标准的 contextInitialized() 方法中完成了初始化工作:

@Override
public void contextInitialized(ServletContextEvent event) {
    try {
        // 读取配置信息
        initEurekaEnvironment(); 
        // 初始化Eureka Client(用来与其它节点进行同步)
        // 初始化server
        initEurekaServerContext(); 

        ServletContext sc = event.getServletContext();
        sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
    } catch (Throwable e) {
        logger.error("Cannot bootstrap eureka server :", e);
        throw new RuntimeException("Cannot bootstrap eureka server :", e);
    }
}

由于讲解源码需要花费大量的篇幅,我就不在细讲。后面整个 Spring Cloud 教程做完之后,如果有时间我在单独来讲。这里大家找到入口后,先尝试着自己去分析吧。

Register(服务注册)

服务注册一般是发生在服务启动的时候,后面如果服务自身检测认为 Down,也会来更新服务状态的。

Eureka 服务注册流程

注册的服务列表保存在一个嵌套的 hash map中:

private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry =
new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
  • 第一层hash map的key是app name,也就是应用名字
  • 第二层hash map的key是instance name,也就是实例名字

ApplicationResource 类接收 Http 服务请求,调用 PeerAwareInstanceRegistryImpl 的 register 方法。
PeerAwareInstanceRegistryImpl 完成服务注册后,调用 replicateToPeers 向其它 Eureka Server 节点(Peer)做状态同步(异步操作)。

Renew(服务续约)

Renew(服务续约)操作由 Service Provider 定期调用,类似于 heartbeat。主要是用来告诉 Eureka Server Service Provider 还活着,避免服务被剔除掉。

Eureka 服务续约流程

服务续约的过程与服务注册的过程基本一致:首先更新自身状态,再同步到其它Peer。

Cancel(服务下线)

Cancel(服务下线)一般在 Service Provider shut down 的时候调用,用来把自身的服务从 Eureka Server 中删除,以防客户端调用不存在的服务。

Eureka 服务下线流程

同样的先从主节点下线,然后向其它节点异步操作做状态同步。

说完这三个主要的流程后,相信大家对 Eureka 有了更深的认识。其实 Eureka 还有很多其它的知识点,我们没讲到。如某一个节点故障将发生哪些操作?再比如消费方的缓存更新策略等。这些限于时间关系,我们在整个 Spring Cloud 案例写完了后,再回过头来整理。

参考资料

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763,QQ2群:454796847,QQ3群:187424846。QQ群进群密码:xttblog,想加微信群的朋友,可以微信搜索:xmtxtt,备注:“xttblog”,添加助理微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » Spring Cloud 教程第四章 Eureka 的原理与实现机制