目录 [隐藏]
本文概览:介绍了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/