eureka集群基于DNS配置方式
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配置方式的更多相关文章
- eureka集群高可用配置
譬如eureka.client.register-with-eureka和fetch-registry是否要配置,配不配区别在哪里:eureka的客户端添加service-url时,是不是需要把所有的 ...
- eureka集群高可用配置,亲测成功配置(转)
转自大神的文章:https://blog.csdn.net/tianyaleixiaowu/article/details/78184793 网上讲这个东西的很多,抄来抄去的,大部分类似,多数没讲明白 ...
- Azure上搭建ActiveMQ集群-基于ZooKeeper配置ActiveMQ高可用性集群
ActiveMQ从5.9.0版本开始,集群实现方式取消了传统的Master-Slave方式,增加了基于ZooKeeper+LevelDB的实现方式. 本文主要介绍了在Windows环境下配置基于Zoo ...
- Eureka 集群高可用配置.
SERVER:1 server: port: 1111 eureka: instance: hostname: ${spring.cloud.client.ip-address} instance-i ...
- 部署hadoop2.7.2 集群 基于zookeeper配置HDFS HA+Federation
转自:http://www.2cto.com/os/201605/510489.html hadoop1的核心组成是两部分,即HDFS和MapReduce.在hadoop2中变为HDFS和Yarn.新 ...
- 微服务架构eureka集群高可用配置
工具:idea 环境:java8.maven3 版本:spring boot 1.5.15.RELEASE 1.搭建spring boot eureka项目 2. pom.xml添加相应依赖,如下: ...
- eureka集群的两种配置方式:配置文件方式与DNS方式
eureka client获取serviceUrls(eureka server地址)列表的过程: 1. 根据use-dns-for-fetching-service-urls属性判断是从dns还是从 ...
- eureka 集群的实现方式?
注意,本文还是对上一篇博客的延续,需要的配置,在前面的博客里面可以找到. eureka集群版 (正宗的eureka!) 2.1.配置eureka的集群之前首先先配置HOSTNAME和IP的映射 具体的 ...
- 基于dns搭建eureka集群
eureka集群方案: 1.通常我们部署的eureka节点多于两个,根据实际需求,只需要将相邻节点进行相互注册(eureka节点形成环状),就达到了高可用性集群,任何一个eureka节点挂掉不会受到影 ...
随机推荐
- [转帖]cmd批处理常用符号详解
cmd批处理常用符号详解 https://www.jb51.net/article/32866.htm 很多符号 还是不清楚的.. 批处理能够极大的提高 工作效率 需要加强深入学习. 1.@一般在 ...
- 428.x的n次幂
实现 pow(x,n) 不用担心精度,当答案和标准输出差绝对值小于1e-3时都算正确 样例 Pow(2.1, 3) = 9.261 Pow(0, 1) = 0 Pow(1, 0) = 1 挑战 O(l ...
- 【开讲啦】20181029 oracle教学笔记
--创建表空间 create tablespace waterboss--表空间名称 datafile 'd:\waterboss.dbf'--用于设置物理文件名称 size 100m--用于设置表空 ...
- javap -v没有显示LocalVaribleTable
时隔多日,终于找到为什么javap -v .class文件没有LocalVariableTable出现 因为默认的javac编译没有生成相关的调试信息,这里我们可以通过javac -help查看指令帮 ...
- Spark 数据倾斜
Spark 数据倾斜解决方案 2017年03月29日 17:09:58 阅读数:382 现象 当你的应用程序发生以下情况时你该考虑下数据倾斜的问题了: 绝大多数task都可以愉快的执行,总 ...
- jQ append 添加html 及字符串拼接
如图,我要拼接这样一段html: 点击下边添加按钮,不断添加这段Html html: <div class="x_addtable"> <div class=&q ...
- LR 场景选项配置--笔记
1 tools-options --设置关系到loadgenerator行为应用于一个场景中的所有的load generator 这些设置用于未来所有运行的场景并且通常只需要设置一次 2 expert ...
- 数据驱动-参数化(Parameters)
在录制程序运行的过程中,Vugen(脚本生成器)自动生成了脚本以及录制过程中实际用到的数据.在这个时候,脚本和数据是混在一起的. 在登录操作中,很明显xpj与123123是填入的数据,如果Contro ...
- flask 保存文件到 七牛云
上篇文章队长讲述了如何把前端上传的文件保存到本地项目目录 本篇 讲述一下把前端上传的文件保存到 第三方存储(七牛云) 七牛云相关步骤思路: 首先 进去七牛云官网,注册并实名认证来获取一个七牛云账号和存 ...
- thymeleaf 简易使用范例
thymeleaf 范例: <!DOCTYPE html> <html lang="en" xmlns:th="http://www.w3.org/19 ...