博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
eureka集群基于DNS配置方式
阅读量:6153 次
发布时间:2019-06-21

本文共 12332 字,大约阅读时间需要 41 分钟。

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 urls11      *12      * @return The list of all eureka service urls for the eureka client to talk to.13      */14     public static List
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 a17 // list of available zones18 Map
> zoneDnsNamesMap = getZoneBasedDiscoveryUrlsFromRegion(clientConfig, region);19 Set
availableZones = zoneDnsNamesMap.keySet();20 List
zones = new ArrayList
(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 first49 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 it56 List
serviceUrls = new ArrayList
();57 for (String zone : zones) {58 for (String zoneCname : zoneDnsNamesMap.get(zone)) {59 List
ec2Urls = new ArrayList
(getEC2DiscoveryUrlsFromZone(zoneCname, DiscoveryUrlType.CNAME));60 // Rearrange the list to distribute the load in case of61 // multiple servers62 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 load76 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
> 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
zoneCnamesForRegion = new TreeSet
(DnsResolver.getCNamesFromTxtRecord(discoveryDnsName));16 Map
> zoneCnameMapForRegion = new TreeMap
>();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 + discoveryDnsName23 + ". "24 + "Expected mapping of the format
.
");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
zoneCnamesSet = zoneCnameMapForRegion.get(zone);31 if (zoneCnamesSet == null) {32 zoneCnamesSet = new ArrayList
();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
getEC2DiscoveryUrlsFromZone(String dnsName, DiscoveryUrlType type) { 9 Set
eipsForZone = null;10 try {11 dnsName = "txt." + dnsName;12 logger.debug("The zone url to be looked up is {} :", dnsName);13 Set
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
();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 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属性,具体配置可参考另一篇文章《》
    • 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

 

你可能感兴趣的文章
HTTP缓存应用
查看>>
KubeEdge向左,K3S向右
查看>>
DTCC2013:基于网络监听数据库安全审计
查看>>
CCNA考试要点大搜集(二)
查看>>
ajax查询数据库时数据无法更新的问题
查看>>
Kickstart 无人职守安装,终于搞定了。
查看>>
linux开源万岁
查看>>
linux/CentOS6忘记root密码解决办法
查看>>
25个常用的Linux iptables规则
查看>>
集中管理系统--puppet
查看>>
分布式事务最终一致性常用方案
查看>>
Exchange 2013 PowerShell配置文件
查看>>
JavaAPI详解系列(1):String类(1)
查看>>
HTML条件注释判断IE<!--[if IE]><!--[if lt IE 9]>
查看>>
发布和逸出-构造过程中使this引用逸出
查看>>
Oracle执行计划发生过变化的SQL语句脚本
查看>>
使用SanLock建立简单的HA服务
查看>>
发现一个叫阿尔法城的小站(以后此贴为我记录日常常用网址的帖子了)
查看>>
Subversion使用Redmine帐户验证简单应用、高级应用以及优化
查看>>
Javascript Ajax 异步请求
查看>>