目录
本文概览:介绍了eureka的集群搭建:单机房和双机房
1 Eureka单机房集群搭建
相对于单点部署这里需要修改配置文件。比如存在3个机器 host1,host2,host3。配置如下:
host1的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
server.port=8761 spring.application.name = eureka_server # 指定机器2 eureka.instance.hostname=host1 ============================================= ### 如下属性是相对于单点部署有变化的 ============================================= ## 指定另外两台机器 eureka.client.serviceUrl.defaultZone=http://host2:8761/eureka/,http://host3:8761/eureka/ ## 服务中心可以注册自己。在默认情况下为trure,所以这里可以不加这个配置。单点时候这里是false。 eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true |
host2的配置
1 2 3 4 5 6 7 8 9 10 11 12 |
server.port=8761 spring.application.name = eureka_server # 指定机器2 eureka.instance.hostname=host2 ============================================= ### 如下属性是相对于单点部署有变化的 ============================================= ## 指定另外两台机器 eureka.client.serviceUrl.defaultZone=http://host1:8761/eureka/,http://host3:8761/eureka/ ## 服务中心可以注册自己。在默认情况下为trure,所以这里可以不加这个配置。单点时候这里是false。 eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true |
host3的配置
1 2 3 4 5 6 7 8 9 10 11 12 |
server.port=8761 spring.application.name = eureka_server ## 指定机器3 eureka.instance.hostname=host3 ============================================= ### 如下属性是相对于单点部署有变化的 ============================================= ## 指定另外两台机器 eureka.client.serviceUrl.defaultZone=http://host1:8761/eureka/,http://host2:8761/eureka/ ## 服务中心可以注册自己。在默认情况下为trure,所以这里可以不加这个配置。单点时候这里是false。 eureka.client.registerWithEureka=true eureka.client.fetchRegistry=true |
这里有个偷懒的地方就是,就是三台机器的eureka.client.serviceUrl.defaultZone配置都指定三台机器,可能启动时候会报错,但是没影响。
1 |
eureka.client.serviceUrl.defaultZone=http://host1:8761/eureka/,http://host2:8761/eureka/,http://host3:8761/eureka/ |
当Eureka-Server由单点变为集群时,对于Eureka-Client的变更,就是在配置中增加如下配置
1 |
eureka.client.serviceUrl.defaultZone=http://host1:8761/eureka/,http://host2:8761/eureka/,http://host3:8761/eureka/ |
2 Eureka双机房集群搭建
为了保证服务稳定性,我们经常将服务划分为2个逻辑机房。通过Region和zone实现划分多机房和同机房访问。比如服务A调用服务B,可以实现A服务优先访问同机房的服务B,避免跨机房影响访问时间。如下涉及到6个服务,每个机房各有三个服务:注册中心eureka、网关gateway、客户端服务client。
2.1 配置信息
1、server配置
(1)server-zone1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
spring.application.name = registry-center server.port=8761 eureka.instance.hostname=localhost # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # beijing区域 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone1 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone1,zone2 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
(2)server-zon2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
spring.application.name = registry-center server.port=8762 eureka.instance.hostname=localhost # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone2 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone2,zone1 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
2、clinet配置
(1)client-zone1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server.port=8891 spring.application.name=service-prodvider1 eureka.instance.status-page-url=http://localhost:${server.port}/swagger-ui.html # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # beijing区域 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone1 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone1,zone2 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
(2) client-zone2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server.port=8892 spring.application.name=service-prodvider1 eureka.instance.status-page-url=http://localhost:${server.port}/swagger-ui.html # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone2 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone2,zone1 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server.port=8892 spring.application.name=service-prodvider1 eureka.instance.status-page-url=http://localhost:${server.port}/swagger-ui.html # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone2 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone2,zone1 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
3、gateway
(1)gateway-zone1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
server.port=8503 spring.application.name=gateway eureka.instance.status-page-url=http://localhost:${server.port}/swagger-ui.html management.security.enabled=false zuul.routes.service-prodvider1:/provider1/** # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # beijing区域 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone1 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone1,zone2 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
(2)gateway-zone2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
server.port=8502 spring.application.name=gateway eureka.instance.status-page-url=http://localhost:${server.port}/swagger-ui.html management.security.enabled=false zuul.routes.service-prodvider1:/provider1/** # 集群都需要设置为ture。注册到eureak eureka.client.register-with-eureka=true # 集群都需要设置为true.从eureka拉取信息 eureka.client.fetch-registry=true # true表示注册的是IP,false是机器名 eureka.instance.prefer-ip-address=true # 划分逻辑机房 # eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka/,http://localhost:8762/eureka/ eureka.client.region=beijing eureka.instance.metadataMap.zone=zone2 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone2,zone1 eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ eureka.prefer-same-zone-eureka=true |
4、运行服务之后
发现在一个机房注册中心页面上可以看到所有服务的实例,不仅仅是同机房的。如下:
(1) zone1的注册中心
(2) zone2的注册中心
2.2 测试代码
通过网关访问一个服务时,需要验证下网关否可以访问同机房的服务,而不会跨机房。
在client中增加如下代码
1 2 3 4 5 6 7 8 9 10 11 12 |
@Value("${eureka.instance.metadataMap.zone}") private String zone; @RequestMapping(value = "/zone") @ResponseBody public ViewVo zone() { ViewVo vo = new ViewVo(); vo.setName("provider"); vo.setDecription(zone); return vo; } |
通过zone1的网关服务访问provider服务,此时调用的provider服务也是zone1
通过zone2的网关服务访问provider服务,此时调用的provider服务也是zone2
2.3 多机房总结
1、一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
################# InstanceInfo.java ################# public static String getZone(String[] availZones, InstanceInfo myInfo) { // 配置的eureka.client.availability-zones.region不为空,则直接第一个。 String instanceZone = ((availZones == null || availZones.length == 0) ? "default" : availZones[0]); if (myInfo != null && myInfo.getDataCenterInfo().getName() == DataCenterInfo.Name.Amazon) { String awsInstanceZone = ((AmazonInfo) myInfo.getDataCenterInfo()) .get(AmazonInfo.MetaDataKey.availabilityZone); if (awsInstanceZone != null) { instanceZone = awsInstanceZone; } } return instanceZone; } |
2、在使用逻辑机房时,已经配置了eureka.client.service-url.zon1、eureka.client.service-url.zone2。是否还需要eureka.client.service-url.default?
(1)结论:只有在根据zone查找服务实例为空时,才会使用eureka.client.service-url.default。
有这种场景会用到,我们配置了这两个属性
1 2 3 |
eureka.instance.metadataMap.zone=zone2 # 一定要注意 availability-zones 的顺序,当前实例所属zone 写在最前面 eureka.client.availability-zones.beijing=zone2,zone1 |
但是并没有配置eureka.client.service-url.zone1和eureka.client.service-url.zone2,
1 2 |
eureka.client.service-url.zone1=http://127.0.0.1:8761/eureka/ eureka.client.service-url.zone2=http://127.0.0.1:8762/eureka/ |
只有一个defalut配置
1 |
eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka/,http://localhost:8762/eureka/ |
(2)分析:只有在根据zone查找服务实例为空时,才会使用eureka.client.service-url.default。
根据配文件加载注册中心地址的地址信息,会根据zone生成一个map。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
################### EndpointUtils.java ################## /** * Get the list of all eureka service urls from properties file for the eureka client to talk to. * * @param clientConfig the clientConfig to use * @param instanceZone The zone in which the client resides * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise * @return an (ordered) map of zone -> list of urls mappings, with the preferred zone first in iteration order */ public static Map<String, List<String>> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) { Map<String, List<String>> orderedUrls = new LinkedHashMap<>(); // String region = getRegion(clientConfig); // eureka.client.availability-zones.region 配置了哪些zone,这里就加载那些zone String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion()); if (availZones == null || availZones.length == 0) { availZones = new String[1]; availZones[0] = DEFAULT_ZONE; } logger.debug("The availability zone for the given region {} are {}", region, Arrays.toString(availZones)); int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones); String zone = availZones[myZoneOffset]; // 根据一个zone查找一实例地址 List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(zone); if (serviceUrls != null) { orderedUrls.put(zone, serviceUrls); } int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1); while (currentOffset != myZoneOffset) { zone = availZones[currentOffset]; // 根据一个zone查找一实例地址 serviceUrls = clientConfig.getEurekaServerServiceUrls(zone); if (serviceUrls != null) { orderedUrls.put(zone, serviceUrls); } if (currentOffset == (availZones.length - 1)) { currentOffset = 0; } else { currentOffset++; } } if (orderedUrls.size() < 1) { throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!"); } return orderedUrls; } |
getEurekaServerServiceUrls负责根据一个zone查找一实例地址,在如下代码中可以看到:当根据zone查找服务实例为空时,才会使用eureka.client.service-url.default。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
###################### EurekaClientConfigBean ##################### @Override public List<String> getEurekaServerServiceUrls(String myZone) { String serviceUrls = this.serviceUrl.get(myZone); if (serviceUrls == null || serviceUrls.isEmpty()) { // 在划分双机房时,设置了eureka.client.availability-zones.beijing=zone1,zone2 表示只会捞取这两个zone,但是根据一个zone如果查找实例为空,则此时会使用default serviceUrls = this.serviceUrl.get(DEFAULT_ZONE); } if (!StringUtils.isEmpty(serviceUrls)) { final String[] serviceUrlsSplit = StringUtils.commaDelimitedListToStringArray(serviceUrls); List<String> eurekaServiceUrls = new ArrayList<>(serviceUrlsSplit.length); for (String eurekaServiceUrl : serviceUrlsSplit) { if (!endsWithSlash(eurekaServiceUrl)) { eurekaServiceUrl += "/"; } eurekaServiceUrls.add(eurekaServiceUrl); } return eurekaServiceUrls; } return new ArrayList<>(); } |
3、启动多个注册中心后,节点均出现在unavailable-replicas
问题原因是:因为注册中心都在一台机器部署,分别部署到2台机器上就没有问题了
3 Eureka集群多机房属性
总结:
介绍了基于Eureka的微服务划分机房的两类属性,即 customer/注册中心同机房调用属性 和 customer/server同机房调用属性。通过这两种属性可以实现两种双机房策略。
3.1 划分机房的两类属性
1、配置客户端/注册中心同机房属性
获取eureka地址用到的属性。保证eureka注册中心区分机房,实现客户端和注册中心同机房,即保证客户方服务可以在服务注册、服务查询时,获取到同机房的eurka实例。如果不区分,一个机房网A络有问题了,机房B通过机房A的注册中心获取一个server的地址时,就报错了,所以同机房策略,可以保证客户端访问注册中心在同一个机房。
- 对于服务注册时,可以保证客户端服务调用到同一个zone的eureka注册中心实例来进行注册。
- 对于服务查询时,可以获取到同一个zone的eureka注册中心实例。
(1)availability-zones
为了保证服务注册到同一个 zone 的注册中心,一定要注意 availability-zones 的顺序,必须把同一 zone 写在最前面
(2) prefer-same-zone-eureka
如果 prefer-same-zone-eureka 为true,先通过 region 取 availability-zones 内的第一个zone,然后通过这个zone取 service-url 下的list,并向list内的第一个注册中心进行注册和维持心跳,不再向list内的其它的注册中心注册和维持心跳。
(3)service-url
1 2 |
service-url.zone-1: http://IP1:8761/eureka/ service-url.zone-2: http://IP2:8761/eureka/ |
2、customer/server同机房调用属性
customer查询server的路由策略的属性。 保证customer和Server区分机房,实现customer和server同机房访问
(1)eureka.instance.metadata-map.zone
可以保证customer和server属于同一个机房。
3.2 使用上面属性配置双机房策略
1、策略1只使用eureka.instance.metadata-map.zone 。使用默认的 service-url.default 属性
eureka地址不需要再划分机房了,只需要customer查询server的划分机房。这样可能存在问题是,如果一个机房网A络有问题了,可能调用A获取server的地址时,就报错了。
2、策略2 同时使用两种属性。使用eureka.instance.metadata-map.zone 和 service-url.zone1、eureka.client.availability-zones.xxx 属性
3.3 查询服务的划分机房流程
服务查询流程如下,其中划分逻辑机房会发生在“Customer查询EurekaServer” 和 “路由策略:选取一个实例”的两个过程中。
无论是zuul还是feign都是通过spring cloud ribbon来实现路由策略的。spring cloud ribbon 的策略使用了eureka.instance.metadataMap.zone这个属性,默认策略是customer获取到同机房的server实例。
(1)微服务通过feign来访问
(2)mvc老服务需要自定义路由策略
3.4 服务注册的划分机房流程流程
需要根据配置文件获取到一个注册中心的地址,然后向注册中心进行注册。
4 参考
比较好的参考如下
Eureka 中的 region 和 Zone : https://juejin.im/post/5d68b73af265da03b12061be
一个实例:https://blog.marcosbarbero.com/ha-and-zone-affinity-spring-cloud-eureka/