spring cloud之eureka高可用集群和服务分区解析
目录
- 准备
- 搭建
- 验证
- 解释
准备
1.首先,在C:\WINDOWS\System32\drivers\etc\hosts文件里面添加一下映射,如果不添加也没关系,只是如果是单机环境,在eureka首页中的replicas那一项看到的其它注册中心都是localhost,我这里为了方便理解就添加了映射。
2.为了方便理解,我这里是单个application用一个module,没有采用通过多个profile开启多个application的做法,而且这样做一会儿验证起来也比较清晰。
3.必要的一些概念
先看官方的这张图:
springcloud中eureka的默认region是us-east-1,一个region下可以有多个zone。
比如zone1内有服务A,zone2内也有服务A,消费者A(这里指调用服务A的client)如果属于zone1,他就会优先调用zone1,如果zone1内的服务都不可用了,就会调用zone2中的服务A,这种调用方式就是服务分区。
分区其实就是建立在集群之上,zone1和zone2构成了集群,但是消费者A调用zone1的时候可能开销会小一些,所以可以让他优先调用zone1内的服务A。
搭建
2个注册中心
- region1-zone1(region1区域内的zone1),region1-zone2(region1区域内的zone2)
region1-zone1:
application.properties:
#端口 server.port=8761 #主机名 eureka.instance.hostname=region1-zone1 #应用名称 spring.application.name=eureka-server #是否注册自身到eureka服务器 eureka.client.register-with-eureka=true #是否获取eureka服务器注册表上的注册信息 eureka.client.fetch-registry=true #false表示在此eureka服务器中关闭自我保护模式,所谓自我保护模式,默认true eureka.server.enableSelfPreservation=false #配置这个eureka server注册中心所属的region,默认值us-east-1 eureka.client.region=region1 #region1内的所有zone,提取第一个作为自己的zone eureka.client.availability-zones.region1=region1-zone1,region1-zone2 #region1内的所有其它zone的注册中心 eureka.client.service-url.region1-zone2=http://region1-zone2:8764/eureka/
启动类:
@SpringBootApplication @EnableEurekaServer public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
region1-zone2:
server.port=8764 eureka.instance.hostname=region1-zone2 spring.application.name=eureka-server eureka.client.register-with-eureka=true eureka.client.fetch-registry=true eureka.server.enableSelfPreservation=false eureka.client.region=region1 eureka.client.availability-zones.region1=region1-zone2,region1-zone1 eureka.client.service-url.region1-zone1=http://region1-zone1:8761/eureka/
启动类:同region1-zone1
3个service-hi服务
我这里用前缀用来区分他们分别属于哪个zone。
region1-zone1-service-hi-1:
server.port=8762 spring.application.name=test-zone eureka.client.region=region1 eureka.client.availability-zones.region1=region1-zone1,region1-zone2 eureka.client.service-url.region1-zone1=http://region1-zone1:8761/eureka/ eureka.client.service-url.region1-zone2=http://region1-zone2:8764/eureka/ eureka.client.prefer-same-zone-eureka=true eureka.instance.prefer-ip-address=true #属于哪一个zone eureka.instance.metadata-map.zone=region1-zone1 #自定义信息,验证的时候用 info=region1-zone1-service-hi-1
启动类:
@EnableEurekaClient @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
controller:
@RestController public class HelloController { @Value("${info}") private String info; @RequestMapping(value="/hi") public String hi() { return info; } }
region1-zone1-service-hi-2:
server.port=8763 spring.application.name=test-zone eureka.client.region=region1 eureka.client.availability-zones.region1=region1-zone1,region1-zone2 eureka.client.service-url.region1-zone1=http://region1-zone1:8761/eureka/ eureka.client.service-url.region1-zone2=http://region1-zone2:8764/eureka/ eureka.client.prefer-same-zone-eureka=true eureka.instance.prefer-ip-address=true eureka.instance.metadata-map.zone=region1-zone1 info=region1-zone1-service-hi-2
启动类、controller都和region1-zone1-service-hi-1相同
region1-zone2-service-hi-1:
server.port=8765 spring.application.name=test-zone eureka.client.region=region1 eureka.client.availability-zones.region1=region1-zone2,region1-zone1 eureka.client.service-url.region1-zone1=http://region1-zone1:8761/eureka/ eureka.client.service-url.region1-zone2=http://region1-zone2:8764/eureka/ eureka.client.prefer-same-zone-eureka=true eureka.instance.prefer-ip-address=true eureka.instance.metadata-map.zone=region1-zone2 info=region1-zone2-service-hi-1
启动类、controller都和region1-zone1-service-hi-1相同
1个consumer
application.properties:
server.port=8888 spring.application.name=region1-consumer eureka.client.region=region1 eureka.client.availability-zones.region1=region1-zone1,region1-zone2 eureka.client.service-url.region1-zone1=http://region1-zone1:8761/eureka/ eureka.client.service-url.region1-zone2=http://region1-zone2:8764/eureka/ eureka.client.prefer-same-zone-eureka=true eureka.instance.prefer-ip-address=true eureka.instance.metadata-map.zone=region1-zone1 logging.level.root=debug
controller:
@RestController public class HiController { @Autowired private RestTemplate restTemplate; @RequestMapping(value="/hello") public String hi() { return restTemplate.getForObject("http://test-zone/hi", String.class); } }
验证
先启动两个注册中心:eureka-server-region1-zone1、eureka-server-region1-zone2。
然后将4个服务启动。
再启动消费者服务。
访问zone1的注册中心:
访问zone2的注册中心:
可以看到每个注册中心都有4个服务,这说明,3个service-hi和一个consumer注册到了集群中所有注册中心上。
打开浏览器访问:http://ip:8888/hello,ip自行改成consumer服务的ip即可,连续访问这个url若干次,可以看到consumer调用的service的始终是zone1中的service-hi-1和service-hi-2,并且由于ribbon的默认负载均衡规则,service-hi-1和service-hi-2被轮询调用。
然后我们关闭zone1中的service-hi-1,再访问若干次,可以发现此时始终只有service-hi-2提供服务了。
然后我们再关闭zone1中的service-hi-2,再访问若干次url,此时zone1中的注册中心已经没有服务提供者可以给consumer消费了,zone2中的service-hi-1开始给consumer提供服务。
解释
1.有的人可能会发现在关闭服务后立即调用会报错,这一点下面做下解释:
打开debug
logging.level.root=debug
这里我截取了一部分心跳日志:
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8765, 192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: [192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8762]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone1=[192.168.5.1:8762, 192.168.5.1:8763]}
[192.168.5.1:8765, 192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: [192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8762, 192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8762]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8763]
2018-06-24 14:06:03.684 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone1=[192.168.5.1:8762, 192.168.5.1:8763]}
2.此时region1-zone1-service-hi-1已经下线,但是region1-zone1-service-hi-2和region1-zone2-service-hi-1还存在于ribbon的服务列表中。由于服务分区的原因,ribbon只会使用region1-zone1-service-hi-2服务。
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8765, 192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: [192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : zoneAffinity is overriden. blackOutServerPercentage: 0.0, activeReqeustsPerServer: 0.0, availableServers: 1
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8763]
2018-06-24 14:07:03.688 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone1=[192.168.5.1:8763]}
[192.168.5.1:8765, 192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: [192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : zoneAffinity is overriden. blackOutServerPercentage: 0.0, activeReqeustsPerServer: 0.0, availableServers: 1
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8763]
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:07:03.687 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8763]
2018-06-24 14:07:03.688 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone1=[192.168.5.1:8763]}
3.此时zone1中的服务:region1-zone1-service-hi-1和region1-zone1-service-hi-2都已经下线,ribbon只能使用region1-zone2-service-hi-1服务
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: []
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : zoneAffinity is overriden. blackOutServerPercentage: NaN, activeReqeustsPerServer: 0.0, availableServers: 0
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone2=[192.168.5.1:8765]}
[192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : Determining if zone affinity should be enabled with given server list: []
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.ZoneAffinityServerListFilter : zoneAffinity is overriden. blackOutServerPercentage: NaN, activeReqeustsPerServer: 0.0, availableServers: 0
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Filtered List of Servers for test-zone obtained from Discovery client: [192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: clearing server list (SET op)
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer [test-zone]: addServer [192.168.5.1:8765]
2018-06-24 14:08:03.692 DEBUG 2260 --- [erListUpdater-0] c.n.l.DynamicServerListLoadBalancer : Setting server list for zones: {region1-zone2=[192.168.5.1:8765]}
因为存在心跳机制,默认情况下,服务的超时时间是90s,服务和注册中心的心跳间隔是30s
我们可以看下spring cloud的文档:
eureka.instance.lease-expiration-duration-in-seconds |
90 |
Indicates the time in seconds that the eureka server waits since it received thelast heartbeat before it can remove this instance from its view and there bydisallowing traffic to this instance. Setting this value too long could mean that the traffic could be routed to theinstance even though the instance is not alive. Setting this value too small couldmean, the instance may be taken out of traffic because of temporary networkglitches.This value to be set to atleast higher than the value specified inleaseRenewalIntervalInSeconds. |
大概意思是:
eureka将等待一定的时间(默认90秒)保持实例的存活,过了这个时间如果没有收到最新的心跳,那么eureka将会把这个实例从自己的视图中移除,并且拒绝他的流量。
eureka.instance.lease-renewal-interval-in-seconds |
30 |
Indicates how often (in seconds) the eureka client needs to send heartbeats to eureka server to indicate that it is still alive. If the heartbeats are not received for the period specified in leaseExpirationDurationInSeconds, eurekaserver will remove the instance from its view, there by disallowing traffic to thisinstance. Note that the instance could still not take traffic if it implementsHealthCheckCallback and then decides to make itself unavailable. |
这个是在讲心跳的时间间隔:指示eureka client隔多少时间发送心跳给server,过了这个时间如果没收到心跳,eureka server就会移除超时的eureka client。
所以说,当一个服务下线,eureka server要90s后才能知道这个服务已经不存在了。
为什么我这里调试的时候要禁用保护模式?
当保护模式被触发的时候,实例就永远不会过期,如果zone1中的服务都下线了,由于这个保护模式,eureka并不会让这些实例过期,因此,zone2中的服务就永远调用不到了。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。