赞
踩
我在启动了3个EurekaServer服务后,在dashboard的General Info中发现,集群中的节点均出现在unavailable-replicas下,没有出现在available-replicas中,这样虽然我的集群是可用的,但是总感觉不踏实,最后通过源码发现,是自己的配置不对。
那么需要怎么解决呢?通过多次试验,主要是以下几个配置:
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的值要一样
基本上就是以上两三点。
我访问的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尽心比较。
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中的服务。
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的请求次数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。