目錄 [隱藏]
本文概覽:介紹了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/