Spring 当第一个 eureka 服务器关闭时,Boot App 没有启动
Spring Boot App doesn't start when the first eureka server is down
我有多个服务器的 Eureka 集群:
#ENV
EUREKA_URL=http://host1:8761/eureka/,http://host2:8761/eureka/,http://host3:8761/eureka/
#boostrap.yml
eureka:
client:
registryFetchIntervalSeconds: 5
serviceUrl:
defaultZone: ${EUREKA_URL:http://127.0.0.1:8761/eureka/}
万一当 host1 出现故障时,我的应用程序不会以异常启动:
13410 2021-01-14 11:28:34,994 ERROR [ main ] o.s.b.SpringApplication | Application run failed
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://host1:8761/eureka/apps/": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:748)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583)
at org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient.getApplicationsInternal(RestTemplateEurekaHttpClient.java:154)
at org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient.getApplications(RestTemplateEurekaHttpClient.java:142)
at org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapConfiguration.lambda$eurekaConfigServerInstanceProvider[=12=](EurekaConfigServerBootstrapConfiguration.java:112)
at org.springframework.cloud.config.client.ConfigServerInstanceProvider.getConfigServerInstances(ConfigServerInstanceProvider.java:50)
at org.springframework.cloud.config.client.ConfigServerInstanceProvider$$FastClassBySpringCGLIB$$facbf882.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at
这对我来说很奇怪,因为我预计尤里卡会尝试连接到其他服务器,但事实并非如此。我在 spring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar
:
中找到了它
// EurekaConfigServerBootstrapConfiguration.java
private String getEurekaUrl(EurekaClientConfigBean config) {
List<String> urls = EndpointUtils.getServiceUrlsFromConfig(config,
EurekaClientConfigBean.DEFAULT_ZONE, true);
return urls.get(0);
}
和
// DefaultEurekaClientConfig.java
@Override
public List<String> getEurekaServerServiceUrls(String myZone) {
String serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + "." + myZone, null).get();
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", null).get();
}
if (serviceUrls != null) {
return Arrays.asList(serviceUrls.split(URL_SEPARATOR));
}
return new ArrayList<String>();
}
最后:
// RestTemplateEurekaHttpClient.java
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath,
String[] regions) {
String url = serviceUrl + urlPath;
if (regions != null && regions.length > 0) {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions="
+ StringUtil.join(regions);
}
// There is no exception handling here and above!
ResponseEntity<EurekaApplications> response = restTemplate.exchange(url,
HttpMethod.GET, null, EurekaApplications.class);
return anEurekaHttpResponse(response.getStatusCodeValue(),
response.getStatusCode().value() == HttpStatus.OK.value()
&& response.hasBody() ? (Applications) response.getBody() : null)
.headers(headersOf(response)).build();
}
我有 java11
、springBootVersion = '2.3.4.RELEASE'
、spring-cloud-netflix-eureka-client-2.2.3.RELEASE
、eureka-client-1.9.21
。
如何让eureka在第一台服务器注册失败的情况下,继续在后面的服务器上尝试?有什么想法吗?
我找到了解决方法。
我只是覆盖 bean:
@Slf4j
@Configuration
public class EurekaMultiClientBoostrapConfig {
@Bean
@Primary
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config) {
List<String> urls = EndpointUtils.getServiceUrlsFromConfig(config,
EurekaClientConfigBean.DEFAULT_ZONE, true);
for (String url : urls) {
try {
RestTemplateEurekaHttpClient client = (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory()
.newClient(new DefaultEndpoint(url));
client.getApplications(config.getRegion());
log.info("Registered on Eureka host '{}' is successful.", url);
return client;
} catch (Exception e) {
log.warn("Eureka host '{}' is unavailable(reason: {})", url, e.getMessage());
}
}
throw new IllegalStateException("Failed to register on any eureka host.");
}
P.S。由于此配置属于 Bootstrap 配置,请不要忘记将以下参数添加到 resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=your.app.package.EurekaMultiClientBoostrapConfig
我有多个服务器的 Eureka 集群:
#ENV
EUREKA_URL=http://host1:8761/eureka/,http://host2:8761/eureka/,http://host3:8761/eureka/
#boostrap.yml
eureka:
client:
registryFetchIntervalSeconds: 5
serviceUrl:
defaultZone: ${EUREKA_URL:http://127.0.0.1:8761/eureka/}
万一当 host1 出现故障时,我的应用程序不会以异常启动:
13410 2021-01-14 11:28:34,994 ERROR [ main ] o.s.b.SpringApplication | Application run failed
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://host1:8761/eureka/apps/": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:748)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583)
at org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient.getApplicationsInternal(RestTemplateEurekaHttpClient.java:154)
at org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient.getApplications(RestTemplateEurekaHttpClient.java:142)
at org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapConfiguration.lambda$eurekaConfigServerInstanceProvider[=12=](EurekaConfigServerBootstrapConfiguration.java:112)
at org.springframework.cloud.config.client.ConfigServerInstanceProvider.getConfigServerInstances(ConfigServerInstanceProvider.java:50)
at org.springframework.cloud.config.client.ConfigServerInstanceProvider$$FastClassBySpringCGLIB$$facbf882.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at
这对我来说很奇怪,因为我预计尤里卡会尝试连接到其他服务器,但事实并非如此。我在 spring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar
:
// EurekaConfigServerBootstrapConfiguration.java
private String getEurekaUrl(EurekaClientConfigBean config) {
List<String> urls = EndpointUtils.getServiceUrlsFromConfig(config,
EurekaClientConfigBean.DEFAULT_ZONE, true);
return urls.get(0);
}
和
// DefaultEurekaClientConfig.java
@Override
public List<String> getEurekaServerServiceUrls(String myZone) {
String serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + "." + myZone, null).get();
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", null).get();
}
if (serviceUrls != null) {
return Arrays.asList(serviceUrls.split(URL_SEPARATOR));
}
return new ArrayList<String>();
}
最后:
// RestTemplateEurekaHttpClient.java
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath,
String[] regions) {
String url = serviceUrl + urlPath;
if (regions != null && regions.length > 0) {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions="
+ StringUtil.join(regions);
}
// There is no exception handling here and above!
ResponseEntity<EurekaApplications> response = restTemplate.exchange(url,
HttpMethod.GET, null, EurekaApplications.class);
return anEurekaHttpResponse(response.getStatusCodeValue(),
response.getStatusCode().value() == HttpStatus.OK.value()
&& response.hasBody() ? (Applications) response.getBody() : null)
.headers(headersOf(response)).build();
}
我有 java11
、springBootVersion = '2.3.4.RELEASE'
、spring-cloud-netflix-eureka-client-2.2.3.RELEASE
、eureka-client-1.9.21
。
如何让eureka在第一台服务器注册失败的情况下,继续在后面的服务器上尝试?有什么想法吗?
我找到了解决方法。 我只是覆盖 bean:
@Slf4j
@Configuration
public class EurekaMultiClientBoostrapConfig {
@Bean
@Primary
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config) {
List<String> urls = EndpointUtils.getServiceUrlsFromConfig(config,
EurekaClientConfigBean.DEFAULT_ZONE, true);
for (String url : urls) {
try {
RestTemplateEurekaHttpClient client = (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory()
.newClient(new DefaultEndpoint(url));
client.getApplications(config.getRegion());
log.info("Registered on Eureka host '{}' is successful.", url);
return client;
} catch (Exception e) {
log.warn("Eureka host '{}' is unavailable(reason: {})", url, e.getMessage());
}
}
throw new IllegalStateException("Failed to register on any eureka host.");
}
P.S。由于此配置属于 Bootstrap 配置,请不要忘记将以下参数添加到 resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=your.app.package.EurekaMultiClientBoostrapConfig