springcloud Eureka源码深入解析

725 阅读11分钟

springcloud和微服务的关系

提到springcloud我们很明显就会和微服务有关系,那么他们之间是什么关系呢? 微服务是一种架构方式,springcloud是对微服务架构的一种实现

为什么要很多企业采用微服务搭建自己的项目

  • 因为单体架构,做高并发比较困难

  • 各个模块耦合度比较大,开发维护困难

我们来看一下简单的架构图

我们可以看到每个模块都是独立的进程,将来做高并发和集群肯定是完爆单体项目 因为这是拆分开的项目,所有各个模块之间肯定没有任何耦合度 作为开发者来说,每个人只负责一个模块来说,肯定是方便很多 比如在单体架构中,如果某个功能宕机了,肯定整个系统都不能使用,但是在微服务架构中,如果仓库模块宕机了,订单模块也可以正常使用,各个模块不影响,比如如果一个模块宕机了可以降级操作,给个固定信息,XXX模块正在维护/升级,请稍等这样的信息

微服务的四大原则

  • 高内聚低耦合
  • 前后端分离
  • 无状态访问
  • RestFul风格

分布式和微服务的区别 分布式:单体项目项目支撑不了高并发的情景和基于无法提高性能优化来拆分下来 微服务:拆分成不同的模块,各个模块互不影响

相同点:都是部署在不同的物理机上面去的

可以说微服务是分布式不能说分布式是微服务

注册中心 Eureka

我们在user服务请求order服务的时候,order服务宕机了,user服务不知道还一直调用,会导致这个请求一直在阻塞一直等待返回,如果请求多了,是不是就会导致雪崩,为了解决这个问题就有了注册中心,那么Eureka就是解决这个问题

每个微服务在启动的时候都发布一个请求注册信息到注册中心

注册信息:ip地址,端口号,服务名称,服务id等

每个微服务启动的时候都把注册信息发送到Eureka服务器中,在Eureka底层都有一个Map<K,V> 这样的数据结构,这个数据结构在Eureka是非常复杂的,看Eureka底层就知道有多复杂了

各个服务调用就不需要去维护这些注册信息了,各个微服务就去注册中心去拿注册信息, 他们底层会定时去Eureka拿注册信息,如果服务直接将请求我们只需要去拿注册中心返回的地址去调用

很多人会说Eureka闭源了为什么还要去研究源码,这里告诉你们不要告诉别人,别人会笑话你们获取的消息太lower了,Eureka内部具体的情况是这样的Eureka1绝大部分开发者都在使用,Eureka内部决定开发一个框架叫Eureka2,由于内部原因,或者说人员安排和技术上的原因Eureka2放弃开发了,但是Eureka1依然在更新,包括springcloud它压根从来没有支持过Eureka2,它一直在使用Eureka1,Eureka2压根没有发布

Eureka和zookeeper区别

  • eureka遵从的是高可用,zookeeper是数据强一致性

  • eureka默认数据存储存在内存,zookeeper是存在磁盘中

我们现在讲一下Eureka底层实现

@EnableEurekaServer 就是一个标识,一个开关

现在我们说一下为什么注入这个类就可以把信息注册到注册中心 我们需要对Eureka的运行原理有清晰的理解

服务运行时各个服务会向注册中心注册信息,怎么注册,发送一个http请求,携带注册信息,eureka底层是通过拦截请求来拦截服务发送的信息,eureka底层是通过jersey,和springmvc一样的,也是遵从mvc架构,但是和springmvc有不一样的地方就是底层采用的过滤器拦截请求,和springmvc很像,springmvc底层是通过dispatcherservlet拦截器拦截,springmvc拦截之后是通过controller处理具体的请求,Eureka底层是通过Resource处理类处理

我们先从服务注册看起,先找到底层源码的服务注册的入口,查看源码可以看到是方法名addInstance,可以在这个方法里面打个断点,然后启动服务,然后进入断点,走下去,都是校验参数非空,校验参数之后会调用register方法,会进入服务注册,进入register会发现没有服务注册的逻辑,register是个空的方法只调用HandlerRegstration方法,因为真正服务注册的逻辑是交给父类来实现的,那么这个register方法做什么呢,现在我们点进去HandlerRegstration方法看一下,这个方法只发布了一个监听(事件),那么什么是监听,使用的是springboot里面的事件驱动器,这个主要是对观察者设计模式的优化,这个发布一个监听也就是说当Eureka接收到注册的请求的时候,它会发布一个注册的监听,告诉所有的监听者,我这边接收了服务注册请求了,那么谁来监听呢,自己来监听,你用Eureka就来监听,比如有这个需求,注册的时候需要把注册的信息添加到自己的数据库中,这时候可以自己定义一个监听,具体代码:

@Component
public class EurekaRegisterLinstener{
    
    @EnventListener
    public void register(EurekaInstanceRegisteredEvent e){
        System.out.println("接收到注册请求微服务名:"+e.getInstanceInfo.getAppName())
    }
}

这个方法的参数就是你需要监听的事件EurekaInstanceRegisteredEvent,@EnventListener这个注解声明它是这个监听者

接着往下走,这时候父类调用了一个PeerAwareInstanceRegisteryImpl,这个类依然没有做服务注册,这个类也是调用了register方法,这个类首先拿到系统的默认值,这个默认值是心跳的微服务的过期事件,默认值是90秒,什么是微服务的过期事件,因为各个微服务之间是需要心跳链接的,各个微服务每隔一段事件会发送一个心跳请求,所谓心跳请求也是一个http请求,这个心跳请求没什么意义,只是告诉Eureka我这个微服务还活着,因为这个微服务既然还可以发请求,也就证明我这个微服务还没有崩盘,默认30秒发一次心跳请求,但是这个默认时间是可以修改的,具体可以百度,必应,谷歌,接着往方法下面走,可以看不到调用了一个replitaceToPeers(),这个方法是为了集群信息同步的,Eureka底层有一个责任链模式,它不交给一个人做,因为这样代码可读性和可扩展性太差了,它每一个责任链的责任者只做一部分任务,其中的非主要的任务就交给子类来实现,他开发成一个接口,把注册这个事情开发成一个接口,每个责任链只做一部分事情,主要的事情是注册这个操作,其中非主要的事情交给其中的一些子类来实现,这就是责任链模式,比如PeerAwareInstanceRegisteryImpl这个类只做集群信息同步,但是你要做注册,你必须要经过这个类 接下我们继续往下走,可以看到真正做服务注册的这个类是AbstractInstanceRegistery

上面说到Eureka底层注册表存储注册注册信息,注册表是使用Map结构的,但是Eureka底层这个Map结构非常复杂,是采用concurrentHashMap<String,Map<String,Lease>>

进入这个方法,从这个Map获取服务, Eureka会检验服务名是否冲突,如果冲突了Eureka会根据活跃时间来判断到底覆盖哪一个 先获取先的节点注册的时间,然后和老的节点注册事件比较,如果老的节点注册时间更长,证明来的节点更活跃,则用老的节点,因为尽量要保证集群的可用性,微服务的可用性

接下来看到一个Lease<心跳续约对象> 封装的是注册时间,最后操作时间,注册事件,过期时间 然后往下走可以看到,把注册信息和过期时间放到Lease封装的对象里面,最后把心跳续约对象Lease put 到gMap里面去,gMap就是注册表,到这里为止服务注册已经走完了,后面的无非就是改变一下服务状态,还有一些数据的采集,打印一下数据,什么时候哪个时间哪个微服务哪个id注册了注册信息,注册信息就存在了gMap里面去,后续注册信息从gMap里面拿

折下来看一下心跳续约 renew 1.从注册表拿到一个微服务名字的实例,通过id拿到具体微服务的实例 2.判断这个微服务是否为空,如果为空,则返回false,controller会返回一个404,当客户端接收到404后会尝试从新注册这个服务 3.如果不为空,就把具体微服务信息拿出来,判断微服务状态是否是宕机的状态,如果不是宕机的状态会调用这个leanseToRenew.renew方法,调用这个节点的rener方法,进入这个方法可以看到也就是心跳续约就是把最后的操作时间续上,最后的操作时间=当前的系统时间+默认的过期时间 后期判断是否过期 当前时间-最后操作时间有没有大于过期时间

服务下架

就是从gMap删除就好了

服务剔除

服务过期之后,eureka会定时剔除这些服务,怎么剔除,找到EurekaServerInitializerConfiguration这个类,它会在Eureka上下文初始化环境的时候会初始化服务剔除的类,初始化一个定时器,找到Eureka初始化的一个节点,在postInit方法这里,会new一个定时器,进入这个定时器类会构建一个定时器,定时器启动的时候会调用evict()剔除方法,点进去剔除方法evict()会看到先判断是否打开自我保护机制

什么是自我保护机制

当Eureka短时间内大量剔除了微服务,那么它就会考虑打开自我保护机制

什么时候关闭自我保护机制

当微服务正常的时候,当网络正常的时候才会去关闭自我保护机制

如果没有打开自我保护机制 默认是15分钟85%的节点全部宕机,这是个默认值,可以配置,它就会打开自我保护机制

1.isLeaseExpirationEnable()判断是否打开自我保护机制 2.如果打开自我保护机制就不会剔除 3.如果没有打开自我保护机制,就剔除,先定义一个arrayList集合去保存剔除的节点,嵌套遍历注册表,最后遍历具体的节点,判断是否过期,如果满足服务剔除的节点并且不为空,则加到定义的集合里面 4.在剔除之前,先判断自我保护机制有没有触发,先获取当前注册表的大小,用注册表的大小*这个默认的常量是85%,然后用剩余节点的大小=注册表的大小-剔除节点的大小,然后用剩余节点的大小和剔除集合的大小比较,取最小值,拿到最小值,就可以遍历剔除,那么怎么知道剔除哪些呢,采用随机算法剔除,随机出下标之后就可以通过这个服务剔除方法internalCancel(),这个剔除方法无非就是通过你的服务名返回到gMap里然后通过你的id剔除节点 remove(id),从map里剔除

集群信息同步

所谓集群信息同步,就是把所有集训信息同步数据一致,都会致性naddInstance()方法,但是不同的是,会判断isReplication是否为ture,这个为了防止死循环,单纯的注册服务会把这个值设为false,可以回到那里判断是否集群,如果是ture则不同步,因为比如A-->B B-->A 就会死循环