Eureka--3、高可用注册中心的第一步,通过defaultZone深入理解zone和serviceUrl

defaultZone与serviceUrl的获取

我们平时都是用defaultZone,这个defaultZone到底是什么,为什么一定要用defaultZone,换个别的行不行。网上很多文章都是入门的,不会讲到这一点。

 

default-zone != defaultZone

我是用idea来写application.yml,里面对于属性的提示值不是采用骆驼命名法,而是使用短横线"-"隔开。

本来配置属性都有自动提示的,会发现defaultZone并不会提示,我天真的以为写成default-zone也可以,其实eureka并不认识这个。其它的属性使用短横线隔开和骆驼命名,是提前做了处理。而serviceUrl是真正的属性,service-url的配置内容是map,map的内容是可以自己发挥的,只要是key: value格式就行(注意有个空格)。有很多map类型的配置,比如availabilityZones,metadata-map都是的,内容虽然是key: value格式,但是都是可以自由发挥。其中metadata-map比较特殊,里面有个zone的属性,会被eureka client解析,当成客户端默认指向的zone来处理。(目前没有尝试出来效果)

其实写作default-zone的时候,eureka经过一系列的解析,最终认定,这个客户端没有配置有效的seviceUrls。。。然后自己加一个defaultZone,默认使用http://localhost:8761/eureka/作为地址,而我恰好也用8761作为地址,造成了很多误会。

源码解读:

我们来看service-url是如何获取的。

Eureka Client的属性都在EurekaClientConfig类接口中定义处理方法。

查看实现会有两个实现,我们用的是spring的,我们主要看getAvailabilityZones()和getEurekaServerServiceUrls()两个方法的实现。

// 根据region,从availabilityZones这个Map中获取key值对应的配置。
// availabilityZones是通过eureka.client.availability-zones配置的,

// 可以配置多个availabilityZones,key表示region,value表region对应的zones
// 假设有多个zones,用逗号隔开。
// region通过eureka.client.region来设置,假设不设置,默认为"us-east-1c"。
public String[] getAvailabilityZones(String region) {
    String value = (String)this.availabilityZones.get(region);
    if (value == null) {
        value = "defaultZone";
    }

    return value.split(",");
}

// 根据zone来获取serviceUrls,假设用逗号隔开会拆分成数组。
public List<String> getEurekaServerServiceUrls(String myZone) {
    String serviceUrls = (String)this.serviceUrl.get(myZone);
    if (serviceUrls == null || serviceUrls.isEmpty()) {
        serviceUrls = (String)this.serviceUrl.get("defaultZone");
    }

    if (!StringUtils.isEmpty(serviceUrls)) {
        String[] serviceUrlsSplit = StringUtils.commaDelimitedListToStringArray(serviceUrls);
        List<String> eurekaServiceUrls = new ArrayList(serviceUrlsSplit.length);
        String[] var5 = serviceUrlsSplit;
        int var6 = serviceUrlsSplit.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            String eurekaServiceUrl = var5[var7];
            if (!this.endsWithSlash(eurekaServiceUrl)) {
                eurekaServiceUrl = eurekaServiceUrl + "/";
            }

            eurekaServiceUrls.add(eurekaServiceUrl.trim());
        }

        return eurekaServiceUrls;
    } else {
        return new ArrayList();
    }
}

Eureka会先获取region,如果我们没有设置region,region默认值为"us-east-1c",然后会根据这个region去获取对应的availabilityZones。

然后循环availabilityZones的值,使用每个zone来调用getEurekaServerServiceUrls,获取serviceUrl,最终形成一个zone为key,serviceUrls(因为可能一个zone有多个serviceUrl)列表为value的map。

在使用serviceUrl的时候,使用两重循环:

会按照顺序,逐个取availabilityZones的zone,从map中获取serviceUrls,在逐个循环serviceUrls,来判断是否可达,如果任意serviceUrl可达则停止,否则一致尝试到最后。

如果availabilityZones为空,或者根据它的值取不到serviceUrls,则会返回key为defaultZone的serviceUrls。如果我们也指定了defaultZone对应的serviceUrls,会覆盖原来的defaultZone,否则使用默认的http:// localhost:8761/eureka。

那么问题来了,如果想用自己的zone,该如何设置。

首先要设置region,不设置也可以,不设置就按照"us-east-lc"来组织后面的设置。并且根据region一定要能获取availabilityZones的zone值,如果没有就会默认使用defaultZone。我们假设自己设置region为china。然后需要设值availabilityZones,假设有两个js:nj1,nj2。然后在serviceUrls中设置nj1: xxxx 换行nj2: xxxx,分别指向两个服务,我们故意设置nj2在前,nj1在后面,然后先放任这两个服务之间不互为peer。来试验一下效果。

坑:注意getAvailabilityZones这个方法不会trim(),所以逗号后面不要有空格,不然用带空格的key会找不到serviceUrl,这个实现实在有点掉价。而getEurekaServerServiceUrls方法是加了trim()方法的 ,逗号后面可以有空格。

 

eureka client配置

bootstrap.yml配置

spring:
  application:
    name: demo
  cloud:
    config:
      #指向配置中心地址
      uri: http://localhost:8888/
      profile: dev

application.yml配置

server:
  port: 8080
eureka:
  instance:
    hostname: localhost
  client:
    # 假设指定region为江苏
    region: js
    availability-zones:
      #假设江苏下面有南京的两个region
      js: nj2,nj1
    service-url:
      nj1: http://eureka-server1:8761/eureka
      nj2: http://eureka-server2:8762/eureka

注意,我们把nj2设置在了availability-zones:的前面

 

模拟多台机器的eureka-server

我们准备了两个serverUrl,所以需要把server改成能启动两个的

先去hosts下添加hostName

127.0.0.1 eureka-server1

127.0.0.1 eureka-server2

准备两个yml

application-server1.yml

spring:
  application:
    name: eureka-server
server:
  #指定服务端口
  port: 8761
eureka:
  #指定主机名称
  instance:
    hostname: eureka-server1
  #server一定程度上也是client,互为client,
  client:
    #由于自己就是服务器,不需要注册到自己
    register-with-eureka: false
    #由于自己就是服务器,不需要从服务器获取注册信息
    fetch-registry: false
    #服务地址s
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

 

application-server2.yml

spring:
  application:
    name: eureka-server
server:
  #指定服务端口
  port: 8762
eureka:
  #指定主机名称
  instance:
    hostname: eureka-server2
  #server一定程度上也是client,互为client,
  client:
    #由于自己就是服务器,不需要注册到自己
    register-with-eureka: false
    #由于自己就是服务器,不需要从服务器获取注册信息
    fetch-registry: false
    #服务地址
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

准备两个启动项(可以在idea中复制)

分别指定Program arguments

指定为--spring.profiles.active=server1和--spring.profiles.active=server2

启动一下看看

 

因为前面设置availability-zones设置了nj2在前面,对应的是eureka-server2:8762的服务。我们设置availability-zones改为nj1在前面(nj1,nj2),然后需要重启两个eureka-server。会发现注册到了eureka-server1:8761下面了。

 

弄不明白的eureka.instance.metadata-map.zone

按照官网的说法,可以使用eureka.instance.metadata-map.zone来指定属于哪个zone的。我的理解是,假设配置了metadataMap.zone,那么会优先取这个zone对应的serviceUrls。其实并不是这样的。我查看源码也没看到跟metadataMap有关的地方。

将availability-zones改为nj1,nj2,设置eureka.instance.metadata-map.zone为nj2看看,同样三个服务都需要重启。

理想情况是服务注册到8762上,测试了一下,并不是这样的。仍然按照service-url来配置。说明我对官网那段话的理解是错的。

并且本身就有矛盾,client经过一系列的逻辑,最终会通过availabilityZones和serviceUrls结合来获取第一个能连接的Eureka Server,也就意味着期望以这个serviceUrl所属于的zone作为自身的zone。那修改metadataMap.zone还有什么意义呢。假设metadataMap.zone和serviceUrl所属的zone不同,难道所属一个zone,又故意从另一个zone中获取注册信息?从效率上讲肯定不合适。

等再往后看,看看能不能理解这个用处。

 

 

  • 11
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值