SpringCloud之Eureka的常见问题及配置优化

1.EurekaServer集群中节点均出现在unavailable-replicas下

1.问题描述

我在启动了3个EurekaServer服务后,在dashboard的General Info中发现,集群中的节点均出现在unavailable-replicas下,没有出现在available-replicas中,这样虽然我的集群是可用的,但是总感觉不踏实,最后通过源码发现,是自己的配置不对。
在这里插入图片描述

2.解决方式

那么需要怎么解决呢?通过多次试验,主要是以下几个配置:
1.

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
eureka.client. service-url.defaultZone中的url要用hostname,要使用域名,DNS解析请自行配置,不能使用localhost或者ip,并且这个hostname要与eureka.instance.hostname的值是一样的;

spring.application.name的值要一样
基本上就是以上两三点。
在这里插入图片描述

3.原因

我访问的dashboard的页面主要是通过EurekaController下的这status的get请求得到的,如下:

@RequestMapping(
    method = {RequestMethod.GET}
)
public String status(HttpServletRequest request, Map<String, Object> model) {
    this.populateBase(request, model);
    this.populateApps(model);

    StatusInfo statusInfo;
    try {
         //获取集群中peer节点的状态信息
        statusInfo = (new StatusResource()).getStatusInfo();
    } catch (Exception var5) {
        statusInfo = Builder.newBuilder().isHealthy(false).build();
    }

    model.put("statusInfo", statusInfo);
    this.populateInstanceInfo(model, statusInfo);
    this.filterReplicas(model, statusInfo);
    return "eureka/status";
}

下面我就看下getStatusInfo()这个方法:

@GET
public StatusInfo getStatusInfo() {
    return this.statusUtil.getStatusInfo();
}

再往下跟,我们看下statusUtil.getStatusInfo()这个方法:

public StatusInfo getStatusInfo() {
        Builder builder = Builder.newBuilder();
        int upReplicasCount = 0;
        StringBuilder upReplicas = new StringBuilder();
        StringBuilder downReplicas = new StringBuilder();
        StringBuilder replicaHostNames = new StringBuilder();
        Iterator var6 = this.peerEurekaNodes.getPeerEurekaNodes().iterator();

        while(var6.hasNext()) {
            PeerEurekaNode node = (PeerEurekaNode)var6.next();
            if (replicaHostNames.length() > 0) {
                replicaHostNames.append(", ");
            }

            replicaHostNames.append(node.getServiceUrl());
            //判断是否能够加入到available-replicas中
            if (this.isReplicaAvailable(node.getServiceUrl())) {
                upReplicas.append(node.getServiceUrl()).append(',');
                ++upReplicasCount;
            } else {
                downReplicas.append(node.getServiceUrl()).append(',');
            }
        }

        builder.add("registered-replicas", replicaHostNames.toString());
        builder.add("available-replicas", upReplicas.toString());
        builder.add("unavailable-replicas", downReplicas.toString());
        if (this.peerEurekaNodes.getMinNumberOfAvailablePeers() > -1) {
            builder.isHealthy(upReplicasCount >= this.peerEurekaNodes.getMinNumberOfAvailablePeers());
        }

        builder.withInstanceInfo(this.instanceInfo);
        return builder.build();
    }

下面我们再往下跟,主要看下isReplicaAvailable(node.getServiceUrl())这个判断的方法:

private boolean isReplicaAvailable(String url) {
        try {
            //从其他节点中获取当前节点的注册信息,即当前的服务端要注册到其他节点,如果没注册,这里的app就会返回null
            //而控制能否注册的参数就是eureka.client.register-with-eureka
            //然后就是还要能够拉取,如果不能及时拉取,就只能等其它节点向我同步了,会产生延迟或者数据不同步(一致)
            //除此之外,还要注意一点,就是registry.getApplication(this.myAppName, false);这个方法中myAppName,这就 
            //说集群中的每个server的名字要一样,即spring.application.name的值要一样
            if (app == null) {
            Application app = this.registry.getApplication(this.myAppName, false);
            if (app == null) {
                return false;
            }

            Iterator var3 = app.getInstances().iterator();
            //循环判断peerEurekaNodes中的hostname是否与注册到我这个服务的节点的hostname一致
            while(var3.hasNext()) {
                InstanceInfo info = (InstanceInfo)var3.next();
                //这个函数比较重要,就是判断hostname是否一致,下面我们看下
                if (this.peerEurekaNodes.isInstanceURL(url, info)) {
                    return true;
                }
            }
        } catch (Throwable var5) {
            logger.error("Could not determine if the replica is available ", var5);
        }

        return false;
    }

下面我们看下isInstanceURL()这个方法:

public boolean isInstanceURL(String url, InstanceInfo instance) {
    String hostName = hostFromUrl(url);
    String myInfoComparator = instance.getHostName();
    if (this.clientConfig.getTransportConfig().applicationsResolverUseIp()) {
        myInfoComparator = instance.getIPAddr();
    }

    return hostName != null && hostName.equals(myInfoComparator);
}

其实这个方法很简答, 就是从url中抽取hostname与注册进来的节点的hostname尽心比较。

2. Eureka的Server端的参数配置优化

eureka:
  client:
    # 开启注册表的拉取
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://eureka-8700:8700/eureka/,http://eureka-8701:8700/eureka/,http://eureka-8702:8702/eureka/
  server:
    # 自我保护,看服务的多少,如果服务很多,就开启,服务较少,就关闭
    enable-self-preservation: false
    # 自我保护的阈值
    renewal-percent-threshold: 0.85
    # 剔除服务的间隔时间
    eviction-interval-timer-in-ms: 1000
    # 关闭从readOnly读注册表
    use-read-only-response-cache: false
    # readwrite与readOnly同步的时间间隔
    response-cache-update-interval-ms: 1000
    # 从其他peer拉取注册表重试的次数(最多拉取的次数)
    registry-sync-retries: 2

这样做的目的主要有两个:
1.减少服务上下线的延迟;
2.自我保护的开启或者关闭,需要看网络和服务的多少
3.服务更新的时候,要先停止服务,再发送下线请求。因为如果先发送下线请求,再停止服务的话,由于服务有续约的定时任务,会导致下线之后,服务又自动续约了,那就只等到server来进行剔除服务了。这里的服务指的是注册到EurekaServer中的服务。

3.Eureka的Client端的参数配置优化

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8700/eureka,http://localhost:8701/eureka,http://localhost:8702/eureka,
    prefer-same-zone-eureka:
    ## 开启注册表的拉取
    fetch-registry: true
    ## 说明自己是一个客户端
    enabled: true
    ## 拉取注册表的时间间隔
    registry-fetch-interval-seconds: 5
    ## 开启想eurekaServer注册
    register-with-eureka: true
  instance:
    ## 续约的时间间隔
    lease-renewal-interval-in-seconds: 10
    ## 缺少心跳的过期时间
    lease-expiration-duration-in-seconds: 10

除此之外,还有一个小技巧,我们在生产中配置eureka.client.service-url.defaultZone的时候,各个client端的配置尽量要随机一下,即打乱一下defaultZone中url的顺序,这是因为在拉取注册表的时候,默认从第一个url开始拉取,拉取不到才从下一个拉取,并且最多只能拉取3个;同时,在注册的时候,只会注册到第一个url,不会注册到下面的url。所以我们打乱了顺序以后,就减少了对某一个server的依赖,也降低了对某一个server的请求次数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值