https://www.cnblogs.com/relinson/p/eureka_ha_use_dns.html

 

最近在研究spring cloud eureka集群配置的时候碰到问题:多台eureka server如果需要互相注册,需要在配置文件中将其他服务器地址配置写死.同样客户端启用服务发现功能(eureka client)也需要配置服务端地址列表(其实eureka server与其他eureka server通信也是用的是eureka client组件).按照官方案例提供3台server,如果现在需要增加第四台,第五台...那么问题就来了,所有eureka client的serverUrls列表是否都得更新(修改配置文件)?

一句话总结如上问题就是:eureka集群有什么办法能支持动态集群(集群数量可增减客户端不需要改动任何内容)?

经过寻找发现spring cloud eureka client提供一个eureka.client.useDnsForFetchingServiceUrls选项,使用Dns获取服务地址.

经过各种了解,明确了该配置项就是启用dns来存储eureka server列表的,可以实现动态eureka server集群的功能.但是问题又来了,相关属性还有那些?dns又该如何配置呢?

相关属性好找,有网友提供的例子,dns这块没有比较明确的说明,为了弄明白这块自己尝试着看了看源码,结果找到了DNS里面关于具体如何使用DNS结果的相关代码.代码如下:

getDiscoveryServiceUrls的作用是获取所有eureka service urls,该方法首先判断是否需要从DNS获取服务列表,图中红框部分就是从DNS获取服务列表,继续往下分析:

在分析之前,插播一个知识点:eureka集群与region和zone这几个概念的关系.http://www.vccoo.com/v/bqq4vj

继续看代码:

 1 /**
2 * Get the list of all eureka service urls from DNS for the eureka client to
3 * talk to. The client picks up the service url from its zone and then fails over to
4 * other zones randomly. If there are multiple servers in the same zone, the client once
5 * again picks one randomly. This way the traffic will be distributed in the case of failures.
6 *
7 * @param clientConfig the clientConfig to use
8 * @param instanceZone The zone in which the client resides.
9 * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise.
10 * @param randomizer a randomizer to randomized returned urls
11 *
12 * @return The list of all eureka service urls for the eureka client to talk to.
13 */
14 public static List<String> getServiceUrlsFromDNS(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone, ServiceUrlRandomizer randomizer) {
15 String region = getRegion(clientConfig);
16 // Get zone-specific DNS names for the given region so that we can get a
17 // list of available zones
18 Map<String, List<String>> zoneDnsNamesMap = getZoneBasedDiscoveryUrlsFromRegion(clientConfig, region);
19 Set<String> availableZones = zoneDnsNamesMap.keySet();
20 List<String> zones = new ArrayList<String>(availableZones);
21 if (zones.isEmpty()) {
22 throw new RuntimeException("No available zones configured for the instanceZone " + instanceZone);
23 }
24 int zoneIndex = 0;
25 boolean zoneFound = false;
26 for (String zone : zones) {
27 logger.debug("Checking if the instance zone {} is the same as the zone from DNS {}", instanceZone, zone);
28 if (preferSameZone) {
29 if (instanceZone.equalsIgnoreCase(zone)) {
30 zoneFound = true;
31 }
32 } else {
33 if (!instanceZone.equalsIgnoreCase(zone)) {
34 zoneFound = true;
35 }
36 }
37 if (zoneFound) {
38 Object[] args = {zones, instanceZone, zoneIndex};
39 logger.debug("The zone index from the list {} that matches the instance zone {} is {}", args);
40 break;
41 }
42 zoneIndex++;
43 }
44 if (zoneIndex >= zones.size()) {
45 logger.warn("No match for the zone {} in the list of available zones {}",
46 instanceZone, Arrays.toString(zones.toArray()));
47 } else {
48 // Rearrange the zones with the instance zone first
49 for (int i = 0; i < zoneIndex; i++) {
50 String zone = zones.remove(0);
51 zones.add(zone);
52 }
53 }
54
55 // Now get the eureka urls for all the zones in the order and return it
56 List<String> serviceUrls = new ArrayList<String>();
57 for (String zone : zones) {
58 for (String zoneCname : zoneDnsNamesMap.get(zone)) {
59 List<String> ec2Urls = new ArrayList<String>(getEC2DiscoveryUrlsFromZone(zoneCname, DiscoveryUrlType.CNAME));
60 // Rearrange the list to distribute the load in case of
61 // multiple servers
62 if (ec2Urls.size() > 1) {
63 randomizer.randomize(ec2Urls);
64 }
65 for (String ec2Url : ec2Urls) {
66 String serviceUrl = "http://" + ec2Url + ":"
67 + clientConfig.getEurekaServerPort()
68 + "/" + clientConfig.getEurekaServerURLContext()
69 + "/";
70 logger.debug("The EC2 url is {}", serviceUrl);
71 serviceUrls.add(serviceUrl);
72 }
73 }
74 }
75 // Rearrange the fail over server list to distribute the load
76 String primaryServiceUrl = serviceUrls.remove(0);
77 randomizer.randomize(serviceUrls);
78 serviceUrls.add(0, primaryServiceUrl);
79
80 logger.debug("This client will talk to the following serviceUrls in order : {} ",
81 Arrays.toString(serviceUrls.toArray()));
82 return serviceUrls;
83 }

从代码中可以看到,首先获取当前eureka-client所在的region,然后根据region获取所有zone以及对应的域名信息,然后循环所有zone域名信息获取eureka-server地址,拼接成完整的serviceUrl并加入serviceUrls列表中.

拼接的serviceUrl格式为:"http://" + ec2Url + ":" + clientConfig.getEurekaServerPort() + "/" + clientConfig.getEurekaServerURLContext() + "/";

另外在代码中有标出两处重点,红色背景的是根据region获取zone的具体方法,蓝色背景的是根据zone具体地址获取eureka地址列表的方法.重点就在这两个方法中.

首先看第一个,获取zone的逻辑:

 1     /**
2 * Get the zone based CNAMES that are bound to a region.
3 *
4 * @param region
5 * - The region for which the zone names need to be retrieved
6 * @return - The list of CNAMES from which the zone-related information can
7 * be retrieved
8 */
9 public static Map<String, List<String>> getZoneBasedDiscoveryUrlsFromRegion(EurekaClientConfig clientConfig, String region) {
10 String discoveryDnsName = null;
11 try {
12 discoveryDnsName = "txt." + region + "." + clientConfig.getEurekaServerDNSName();
13
14 logger.debug("The region url to be looked up is {} :", discoveryDnsName);
15 Set<String> zoneCnamesForRegion = new TreeSet<String>(DnsResolver.getCNamesFromTxtRecord(discoveryDnsName));
16 Map<String, List<String>> zoneCnameMapForRegion = new TreeMap<String, List<String>>();
17 for (String zoneCname : zoneCnamesForRegion) {
18 String zone = null;
19 if (isEC2Url(zoneCname)) {
20 throw new RuntimeException(
21 "Cannot find the right DNS entry for "
22 + discoveryDnsName
23 + ". "
24 + "Expected mapping of the format <aws_zone>.<domain_name>");
25 } else {
26 String[] cnameTokens = zoneCname.split("\\.");
27 zone = cnameTokens[0];
28 logger.debug("The zoneName mapped to region {} is {}", region, zone);
29 }
30 List<String> zoneCnamesSet = zoneCnameMapForRegion.get(zone);
31 if (zoneCnamesSet == null) {
32 zoneCnamesSet = new ArrayList<String>();
33 zoneCnameMapForRegion.put(zone, zoneCnamesSet);
34 }
35 zoneCnamesSet.add(zoneCname);
36 }
37 return zoneCnameMapForRegion;
38 } catch (Throwable e) {
39 throw new RuntimeException("Cannot get cnames bound to the region:" + discoveryDnsName, e);
40 }
41 }

12行是请求dns中的地址格式:"txt." + region + "." + clientConfig.getEurekaServerDNSName(),例如:txt.region1.baidu.com,txt.region1.163.com,txt.region2.163.com等

17,27,35行是对返回结果的解析逻辑,可以看出返回值应当是多条记录并且以空格分开(在15行方法内),每条记录都应当是一个域名并且第一个.之前的部分作为zone名称,最终按照zone组织成zone:List<区域地址>的结果返回.

再看第二个,获取eureka server地址逻辑:

 1     /**
2 * Get the list of EC2 URLs given the zone name.
3 *
4 * @param dnsName The dns name of the zone-specific CNAME
5 * @param type CNAME or EIP that needs to be retrieved
6 * @return The list of EC2 URLs associated with the dns name
7 */
8 public static Set<String> getEC2DiscoveryUrlsFromZone(String dnsName, DiscoveryUrlType type) {
9 Set<String> eipsForZone = null;
10 try {
11 dnsName = "txt." + dnsName;
12 logger.debug("The zone url to be looked up is {} :", dnsName);
13 Set<String> ec2UrlsForZone = DnsResolver.getCNamesFromTxtRecord(dnsName);
14 for (String ec2Url : ec2UrlsForZone) {
15 logger.debug("The eureka url for the dns name {} is {}", dnsName, ec2Url);
16 ec2UrlsForZone.add(ec2Url);
17 }
18 if (DiscoveryUrlType.CNAME.equals(type)) {
19 return ec2UrlsForZone;
20 }
21 eipsForZone = new TreeSet<String>();
22 for (String cname : ec2UrlsForZone) {
23 String[] tokens = cname.split("\\.");
24 String ec2HostName = tokens[0];
25 String[] ips = ec2HostName.split("-");
26 StringBuilder eipBuffer = new StringBuilder();
27 for (int ipCtr = 1; ipCtr < 5; ipCtr++) {
28 eipBuffer.append(ips[ipCtr]);
29 if (ipCtr < 4) {
30 eipBuffer.append(".");
31 }
32 }
33 eipsForZone.add(eipBuffer.toString());
34 }
35 logger.debug("The EIPS for {} is {} :", dnsName, eipsForZone);
36 } catch (Throwable e) {
37 throw new RuntimeException("Cannot get cnames bound to the region:" + dnsName, e);
38 }
39 return eipsForZone;
40 }

11行代码明确了请求格式为:"txt." + dnsName,例如:txt.zone1.163.com,txt.zone2.baidu.com等

而返回结果则比较复杂,我们只关心cnametype的,从getServiceUrlsFromDNS方法中可以得知,只需要返回服务器地址或域名就可以了(返回多组同样用空格隔开).

看到这里对dns获取eureka地址的过程已经明白了.然后就是配置了

eureka:
client:
eureka-server-d-n-s-name: relinson.com
use-dns-for-fetching-service-urls: true
region: region1
eureka-server-u-r-l-context: eureka
eureka-server-port: 9999
prefer-same-zone-eureka: true

启动后可以看到这种方式配置还算是成功的

——————————————————————————————————————————————————————

eureka集群的两种配置方式:配置文件方式与DNS方式

 

eureka client获取serviceUrls(eureka server地址)列表的过程:

  • 1. 根据use-dns-for-fetching-service-urls属性判断是从dns还是从config获取region和zone以及serviceUrl相关信息
  • 2. 获取过程首先从配置中获取应用所在region,通过region属性设置
  • 3. 根据region获取所有zone信息,dns与config获取方式不一样
    • 3.1. dns会请求dns服务器获取指定region的所有zone列表,如果没有查询到则抛出异常.使用该方式需要设定 eureka-server-d-n-s-name,eureka-server-u-r-l-context,eureka-server-port属性,具体配置可参考另一篇文章《eureka集群基于DNS配置方式
    • 3.2. config则从配置文件的availabilityZones列表获取,如果没有配置,则返回defaultZone.使用该方式需要配置availability-zones(Map<region:zone>),service-url(Map<zone:serviceUrls>)
  • 4. 获取zone列表成功后需要对列表进行排序,排序根据prefer-same-zone-eureka而不同,该属性意为是否将相同名称的zone作为注册首选
    • 4.0. 这里有一个客户端zone的概念,该zone为availabilityZones的第一个zone,如果没有设置则为defaultZone
    • 4.1. 如果设置true,则查询zone列表,找到与客户端zone同名的元素,并将其之前元素转移到列表最后
    • 4.2. 如果设置false,则查找zone列表,找到第一个与客户端zone不同名的zone,并将其之前的元素转移到列表最后
  • 5. 然后循环获取所有zone对应的serviceUrls并按顺序追加到一个列表中
    • 5.1. 如果是配置方式,获取某个zone的serviceUrls为空则返回defaultZone的serviceUrl,而DNS方式不会进行任何处理

通过以上步骤可以获取到region对应的zone的serviceUrls.

这里出现了region,zone,serviceUrl这几个概念,可以简单理解为region包含多个zone,zone包含多个serviceUrl.但需要注意的是,zone可以出现在多个region中,serviceUrl可以出现在多个zone中,也就是说他们三个概念两两之间的关系是多对多而非一对多.

#基于DNS的配置
eureka:
client:
#DNS域名,获取其他信息将以该域名为根域名
eureka-server-d-n-s-name: relinson.com
#开启DNS方式获取serviceUrl,默认为false
use-dns-for-fetching-service-urls: true
#当前应用所在区域,默认为us-east-1
region: region1
#eureka服务根目录
eureka-server-u-r-l-context: eureka
#服务所在端口
eureka-server-port: 9999
#获取serviceUrl时候是否优先获取相同zone的列表(如果获取为空则获取所在region第一个zone),如果为false则优先获取不在相同zone的列表
prefer-same-zone-eureka: true
#是否获取注册信息到本地
fetch-registry: true
#是否将自己注册到eureka
register-with-eureka: true
#基于CONFIG的配置
eureka:
client:
#开启DNS方式获取serviceUrl,默认为false
use-dns-for-fetching-service-urls: false
#当前应用所在区域,默认为us-east-1
region: region1
#获取serviceUrl时候是否优先获取相同zone的列表(如果获取为空则获取所在region第一个zone),如果为false则优先获取不在相同zone的列表
#client所在zone为availabilityZones的第一个zone,如果未配置,则为defaultZone
prefer-same-zone-eureka: true
#是否获取注册信息到本地
fetch-registry: true
#是否将自己注册到eureka
register-with-eureka: true
#与DNS获取的方式相同,这里需要手工配置包含哪些region以及zone(Map类型),如果没有给相关的region配置zone,则默认返回defaultZone
availability-zones:
region1: zone1-2,zone1-2,zone2-2
region2: zone2-2,zone2-2,zone2-3
#与DNS获取数据方式类似,这里需要手工配置每个zone包含哪些URL,如果应用所在区域没有zone,则默认返回defaultZone的数据
service-url:
zone1-1: http://xxx,http://xxx2
zone1-2: http://xxx,http://xxx2
zone2-1: http://xxx,http://xxx2
zone2-2: http://xxx,http://xxx2

eureka集群基于DNS配置方式的更多相关文章

  1. eureka集群高可用配置

    譬如eureka.client.register-with-eureka和fetch-registry是否要配置,配不配区别在哪里:eureka的客户端添加service-url时,是不是需要把所有的 ...

  2. eureka集群高可用配置,亲测成功配置(转)

    转自大神的文章:https://blog.csdn.net/tianyaleixiaowu/article/details/78184793 网上讲这个东西的很多,抄来抄去的,大部分类似,多数没讲明白 ...

  3. Azure上搭建ActiveMQ集群-基于ZooKeeper配置ActiveMQ高可用性集群

    ActiveMQ从5.9.0版本开始,集群实现方式取消了传统的Master-Slave方式,增加了基于ZooKeeper+LevelDB的实现方式. 本文主要介绍了在Windows环境下配置基于Zoo ...

  4. Eureka 集群高可用配置.

    SERVER:1 server: port: 1111 eureka: instance: hostname: ${spring.cloud.client.ip-address} instance-i ...

  5. 部署hadoop2.7.2 集群 基于zookeeper配置HDFS HA+Federation

    转自:http://www.2cto.com/os/201605/510489.html hadoop1的核心组成是两部分,即HDFS和MapReduce.在hadoop2中变为HDFS和Yarn.新 ...

  6. 微服务架构eureka集群高可用配置

    工具:idea 环境:java8.maven3 版本:spring boot 1.5.15.RELEASE 1.搭建spring boot eureka项目 2. pom.xml添加相应依赖,如下: ...

  7. eureka集群的两种配置方式:配置文件方式与DNS方式

    eureka client获取serviceUrls(eureka server地址)列表的过程: 1. 根据use-dns-for-fetching-service-urls属性判断是从dns还是从 ...

  8. eureka 集群的实现方式?

    注意,本文还是对上一篇博客的延续,需要的配置,在前面的博客里面可以找到. eureka集群版 (正宗的eureka!) 2.1.配置eureka的集群之前首先先配置HOSTNAME和IP的映射 具体的 ...

  9. 基于dns搭建eureka集群

    eureka集群方案: 1.通常我们部署的eureka节点多于两个,根据实际需求,只需要将相邻节点进行相互注册(eureka节点形成环状),就达到了高可用性集群,任何一个eureka节点挂掉不会受到影 ...

随机推荐

  1. [转帖]linux 清空history以及记录原理

    linux 清空history以及记录原理 自己的linux 里面总是一堆 乱七八槽输错的命令 用这个办法 可以清空 linux的内容. 清爽一些. 1.当前session执行的命令,放置缓存中,执行 ...

  2. LLVM的安装

    1. 官网下载 llvm 2. 官网下载cmake 3. configure 执行 llvm 发现报错 4. 解压缩 cmake 5.将cmake 下面的bin 目录放到环境变量里面去 6. 创建一个 ...

  3. mysql参数优化记录

    服务器参数16G内存,4核CPUvim /etc/my.cnf 原: back_log=170 max_connections=600 max_user_connections=0 thread_co ...

  4. SSM框架整合系列——第一步

    环境: JDK8 idea2018.2 maven3.5 spring和springMVC是天然集成,所以只需要解决mybatis和spring的整合问题,重点整合mybatis和spring的两个东 ...

  5. WSS Process On Causal LTI System

    Consider a real LTI system with a WSS process $x(t)$ as input and WSS process $y(t)$ as output. Base ...

  6. python数学库math模块

    函数 数学表示 含义 .pi 圆周率π π的近似值,15位小数 .e 自然常数 e e的近似值,15位小数 ceil(x) [x] 对浮点数向上取整 floor(x) [x] 对浮点数向下取整 pow ...

  7. Codeforces Round #443 Div. 1

    A:考虑每一位的改变情况,分为强制变为1.强制变为0.不变.反转四种,得到这个之后and一发or一发xor一发就行了. #include<iostream> #include<cst ...

  8. Android ProgressDialog 简单实用

    ProgressDialog progressDialog; @SuppressLint("HandlerLeak") Handler handler1 = new Handler ...

  9. win10系统同时安装python2.7和python3.6

    我是先在本机上安装的python3.6.5,因为要学习一个框架,但是这个框架只支持python2,所以我又安装了python2.7.15,并且配置到系统环境变量 环境变量配置了python3.6.5的 ...

  10. Golden Eggs HDU - 3820(最小割)

    Golden Eggs Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...