eureka集群基于DNS配置方式

最近在研究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

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

时间: 2024-11-04 13:49:17

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

eureka集群高可用配置

譬如eureka.client.register-with-eureka和fetch-registry是否要配置,配不配区别在哪里:eureka的客户端添加service-url时,是不是需要把所有的eureka的server地址都写上,还是只需要写一个server就可以了(因为server之间已经相互注册了)?如果写上了所有的server地址,那相当于将每个client服务都往所有的server都添加了一遍,那还配置server间的相互注册有什么意义? 上面的这些问题在多数讲eureka集群教

eureka 集群的实现方式?

注意,本文还是对上一篇博客的延续,需要的配置,在前面的博客里面可以找到. eureka集群版 (正宗的eureka!) 2.1.配置eureka的集群之前首先先配置HOSTNAME和IP的映射 具体的路径. // C:\Windows\System32\drivers\etc 在windows系统中修改hosts文件 /* 127.0.0.1(localhost) eureka01 127.0.0.1(localhost) eureka02 127.0.0.1(localhost) eureka

基于dns搭建eureka集群

eureka集群方案: 1.通常我们部署的eureka节点多于两个,根据实际需求,只需要将相邻节点进行相互注册(eureka节点形成环状),就达到了高可用性集群,任何一个eureka节点挂掉不会受到影响. 2.可能会有初学者和我一样,一开始的时候没有完全理解eureka集群的原理,直接把每个eureka节点的url写进配置文件,期望所有的eureka节点进行相互注册.实际上,节点间进行信息同步的时候,只会选取配置文件第一个eureka的url,除非发生url错误,才会依次选取有效url进行信息同

Linux高可用集群方案之配置heartbeat v2基于crm+hb_gui接口,配置ipvs高可用集群

  基于crm+hb_gui接口配置ipvs高可用集群    基于crm+hb_gui+ldirectored接口配置ipvs高可用集群 

Linux高可用集群方案之配置heartbeat v2基于crm+hb_gui接口,配置http+msyql+nfs高可用集群

  基于crm+hb_gui接口配置http+mysql+nfs高可用集群    基于crm+hb_gui接口配置ipvs高可用集群    基于crm+hb_gui+ldirectored接口配置ipvs高可用集群 

Linux高可用集群方案之配置heartbeat v2基于crm+hb_gui接口,配置高可用httpd,mysql,lvs

本章主要配置heartbeat v2基于crm+hb_gui接口,配置高可用httpd,mysql,lvs. 如何安装heartbeat v2.httpd.nfs.配置心跳连接.ssh密钥通信.同步时间.添加名称解析.配置yum源等请参照: >> Linux高可用集群方案之配置heartbeat v2基于haresources配置文件的httpd高可用集群 http://ccschan.blog.51cto.com/11854461/1922966  ll  本文导航    · 前期准备及相关

微服务架构:Eureka集群搭建

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 服务注册.发现是微服务架构的关键原理之一,由于微服务架构是由一系列职责单一的细粒度服务构成的网状结构,服务之间通过轻量机制进行通信,这就必然引入一个服务注册发现的问题,也就是说服务提供方要注册报告服务地址,服务调用方要能发现目标服务.在我们的微服务架构中我们采用了Eureka来完成微服务的注册与发现.微服务通过Eureka进行注册,服务调用方通过Eureka找到目标服务.由于服务提供方以集群方式提供服务,Eureka也采用集群的方式来

Spring-Cloud学习之Eureka集群搭建

一.为什么需要集群 上一篇文章讲解了如何搭建单个节点的eureka,这篇讲解如何搭建eureka集群,这里的集群还是本地不同的端口执行三个eureka,因为条件不要允许,没有三台电脑,所以大家将就一下吧,eureka集群的目的就是防止一个点故障导致,整个服务瘫痪的问题,成为单点故障,因为一个点出问题,还有另一个点顶上去,代替这个点工作,所以集群也实现了高可用.高性能 二.集群的原理 每一个注册点都配置有其它点的url,能够与其他server点进行数据的同步,当服务向一个点注册时,该店就会把该服务

教你如何利用分布式的思想处理集群的参数配置信息——spring的configurer妙用

引言 最近LZ的技术博文数量直线下降,实在是非常抱歉,之前LZ曾信誓旦旦的说一定要把<深入理解计算机系统>写完,现在看来,LZ似乎是在打自己脸了.尽管LZ内心一直没放弃,但从现状来看,需要等LZ的PM做的比较稳定,时间慢慢空闲出来的时候才有机会看了.短时间内,还是要以解决实际问题为主,而不是增加自己其它方面的实力. 因此,本着解决实际问题的目的,LZ就研究出一种解决当下问题的方案,可能文章的标题看起来挺牛B的,其实LZ就是简单的利用了一下分布式的思想,以及spring框架的特性,解决了当下的参