什么是eurake
是spring-cloud提供的服务注册的一个组件。主要的功能就是提供了服务注册和发现的功能。
如何启动
从三个角色去分析(服务注册中心,服务提供者和服务消费者)
eurake-server:服务注册中心
引用相应的依赖,然后在启动类里加上@EnableEurakeServer注解,然后加上几个配置信息
Eurake.instance-id //instance id
Eurake.client.register-with-eurake=false //表示是否需要注册在eurake服务注册中心里,本身就是注册中心了一般不
用,但是如果是集群模式 就要改成true,可以让其他服务注册中心能发现你
Eurake.client.fetch-register=false //表示是否需要从服务注册中心拉去注册表的信息
eurake.instance.hostname // 实例的hostname
Eurake.client.serviceUrl.defaultZone=http://${eurake.instance.hostname}:${server.port}/eruake/ //
//默认的zone
这样就可以启动一个服务注册中心了
eurake-client: 服务提供者和服务消费者
引用相应的依赖,然后在启动类加上@EnableEurakeClient注解,然后加上几个配置
Spring.application.name
Eurake.client.serviceUrl.defaultZone
Eurake.client.register-with-eurake=true
Eurake.client.fetch-register=true
PS:有上面2个配置其实就已经可以了,但是还有个高可用的方式,把自己注册到注册中心,并把自己的defaultZone设置成其他同样服务的对应接口上的地址,就可以实现集群的高可用模式
简单的原理
从三个角色去分析(服务注册中心,服务提供者和服务消费者)
服务提供者
* 服务注册 服务提供者在启动的时候回发送一个REST请求到服务注册中心去,带上自己的一些元数据信息,并在服务中心注册进去,服务注册中心在收到这个请求的时候会把这些元数据存储在一个嵌套的map集合中,第一层的key是服务的名称,就是hostname。第二层是实例id就是instanceId,所以说一个服务会对应多个实例。并且这份数据同样会同步到服务提供者和服务消费者
* 服务同步 ,如果是高可用模式,就是不同的节点注册到不同的注册中心的时候,他们的信息分别被多个注册中心所维护,如果这几个注册中心有互相注册,那么在服务提供者注册的时候还会转发给这几个注册中心,已达到同步的目的
* 服务续约 服务注册完成后,服务提供者会定时的发送一个信息给注册中心表示自己还活着默认是30秒(eurake.instance.lease-renewal-internal-in-seconds)。已达到续约的目的。如果服务注册中心90秒(默认 eurake.instance.lease-expiration-duration-in-seconds)没有收到这个续约的信息就会把这个服务给删掉
服务消费者
* 服务获取 服务消费者在启动的时候会发送一个服务获取的REST请求给注册中心。获取到注册中心的那个map信息,并且会缓存起来。并且每隔30秒(eurake.client.register-fetch-interval-second)就会重新获取然后刷新这个缓存 .开启这个原理需要配置 eurake.client.fetch-register=true
* 服务调用 根据服务获取功能活得到的信息来调用服务,具体调用哪个实例,是根据region和zone来判断的。这个spring-cloud通过ribon组件来优化这些,已达到负载均衡的目的
* 服务下线 服务会发送一个服务下线的REST请求给到注册中心,注册中心就会把这个状态设成down的状态,并把事件广播出去
服务注册中心
* 失效剔除 其实就是配合服务提供者的服务续约功能,这是每60秒就会去检查一次是否有超过90秒没有服务续约的服务提供者
* 自我保护 如果服务失效剔除的比例大于15% 注册中心就会保护剩下的实例,不让他失效剔除,可以通过eurake.server.enable-self-preservation=false给关掉,因为不关掉会让没有实例被调用到而让调用者报错。最好还是调用者自己有个容错机制
源码分析
注册表的结构是一个ConcurrentHashMap。key是appName。value是一个map。这个map的key是instanceId,value是一个lease<InstanceInfo>
服务注册
client发送一个register的REST请求,会被server端路由到addInstance的方法。这里首先会校验各种属性。然后就会判断是不是从云上面发送的请求,一般都是本地,本地的话就直接走registe。首先会发布一个事件,我们可以自己开发一个监听器监听注册的事件。然后会获取服务的持续时间,这个可以在client配置。默认是90S。然后就是把这个服务放到server的注册表里。如果是集群里发出的register的REST请求,就会在Server端发出一个类似于Client发出的register的REST请求,去完成集群的同步
服务剔除
在server端实例化容器的时候回启动一个定时任务去剔除已经过期的服务。他会有一个保护机制。比如你一共有100个服务注册在这个server。然后你发现有30个服务需要被剔除,但是他一次只会剔除100-100*0.85个 也就是15个剩下的15个如果没有其他意外的话就是在后面几轮的剔除中被剔除
自我保护机制
也是在服务剔除这个定时任务里进行实现的。根据配置是否关闭了自我保护机制,
什么时候保护机制会启动呢。首先配置要为true并且服务心跳的次数大于他的阈值
这个阈值就是根据服务一分钟应该接受到的心跳次数*0.85
服务发现
client发送一个服务发现的REST请求,会被server端路由到getContains的方法
eureka有个3级缓存的概念,这样可以提高eureka的性能,也能防止读写的操作带来的并发问题
1级就是注册表那个
2级就是一个有guava实现的一个读写缓存,而且他会有个过期时间
3级就是一个concurrentHashMap的只读缓存
在服务发现的时候会先去读取只读缓存,如果有就直接返回,如果没有就去读取读写缓存,要是有就会返回这个并更新只读缓存。要是还没有就去读取注册表,
那这3个缓存什么时候会变化呢
注册表只有在服务注册的时候会有改变,并且服务注册的时候也会吧读写缓存给清空,但不改变只读缓存。
服务下线的时候也会更新注册表,
读写缓存除了在服务注册的时候会被清空,服务下线的时候也会清空这个缓存。还有一个线程会一直去更新这个表的数据根据注册表,默认是30S
只读缓存 改变是服务发现的时候只读缓存没有,而读写缓存有,这时候就会更新只读缓存,除了这个场景还有和读写缓存一样的有个线程回去定时的同步数据,也是30S他这个定时同步是根据读写缓存来的。还有就是当发现读写缓存被清空的时候,只读缓存也会清空自己,