1、本节概要

根据前文我们对Eureka Server 有了一定的了解,本节我们主要学习Eureka Client 与 Eureka Server 如何通讯的及相关通信机制是什么,本文会弄清楚一下几个问题:

  • @EnableDiscoveryClient 和 @EnableEurekaClient的区别

  • Eureka Client 启动时做了什么事情(初始化工作)

  • Eureka Client 怎么注册到 Eureka Server(服务注册)

  • 怎么获取 Eureka Server 上的服务的(服务获取)

  • 怎么保证 Eureka Client 本地的服务列表与Eureka Server 上的服务列表保持一致的(服务同步)

2、@EnableDiscoveryClient 和@EnableEurekaClient 的区别

当我们使用服务发现的注解时,却发现了两种注解,经过尝试发现两者的使用效果是一样的,那么他们的区别是什么呢,通过查询官方文档是这样解释的:

Spring Cloud Commons provides the @EnableDiscoveryClient annotation. This looks for implementations of the DiscoveryClient interface with META-INF/spring.factories. Implementations of the Discovery Client add a configuration class to spring.factories under the org.springframework.cloud.client.discovery.EnableDiscoveryClient key. Examples of DiscoveryClient implementations include Spring Cloud Netflix Eureka, Spring Cloud Consul Discovery, and Spring Cloud Zookeeper Discovery.

Spring Cloud Commons 提供 @EnableDiscoveryClient 注解。这将使用META-INF / spring.factories查找DiscoveryClient接口的实现。Discovery Client的实现将配置类添加到org.springframework.cloud.client.discovery.EnableDiscoveryClient项下的spring.factories。比如 DiscoveryClient 实现包括Spring Cloud Netflix Eureka,Spring Cloud Consul Discovery和Spring Cloud Zookeeper Discovery。
By default, implementations of DiscoveryClient auto-register the local Spring Boot server with the remote discovery server. This behavior can be disabled by setting autoRegister=false in @EnableDiscoveryClient.

默认情况下,DiscoveryClient 的实现会使用远程发现服务器自动注册本地Spring Boot服务器。可以通过在@EnableDiscoveryClient中设置autoRegister = false来禁用此行为。

简而言之 spring cloud 中服务发现有多种实现(eureka、consul、zookeeper等等),@EnableDiscoveryClient 基于 spring-cloud-commons, @EnableEurekaClient 基于 spring-cloud-netflix。

如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。

我们看一下这两个注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient { /**
* 如果为true,本服务奖自动注册到指定的服务中心,默认是启用的,
可以通过设置为 false 来禁用自动注册
*/
boolean autoRegister() default true;
} @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableEurekaClient { }

3、Eureka Client 启动时做了什么

我们根据注解 @EnableEurekaClient 找到该注解所在工程 spring-cloud-netflix-eureka-client-2.0.1.RELEASE.jar 下的 META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

本文主要看 org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,其它的主要是客户端查找配置中心及客户端负载均衡(Ribbon)的引导服务,在后续文章的学习总结中再做介绍。

下面我们就从 EurekaClientAutoConfiguration 的源码着手,看看都有哪些操作,先看一下该配置类的注解有哪些

@Configuration
# 加载配置文件解析
@EnableConfigurationProperties
# 加载client配置项
@ConditionalOnClass(EurekaClientConfig.class)
# 加载 DiscoveryClientOptionalArgsConfiguration 到容器
@Import(DiscoveryClientOptionalArgsConfiguration.class)
# 客户端启用引导标记
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
# 启用eureka client,默认启用
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
# 当前配置类EurekaClientAutoConfiguration 加载完毕后的后续加载操作
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {
}

3.1 @ConditionalOnClass(EurekaClientConfig.class)

@ImplementedBy(DefaultEurekaClientConfig.class)
public interface EurekaClientConfig {}

查看默认实现 DefaultEurekaClientConfig,eureka client 默认值加载

@Singleton
@ProvidedBy(DefaultEurekaClientConfigProvider.class)
public class DefaultEurekaClientConfig implements EurekaClientConfig {
/** @deprecated */
@Deprecated
public static final String DEFAULT_NAMESPACE = "eureka.";
public static final String DEFAULT_ZONE = "defaultZone";
private final String namespace;
private final DynamicPropertyFactory configInstance;
private final EurekaTransportConfig transportConfig; public DefaultEurekaClientConfig() {
this("eureka");
} public DefaultEurekaClientConfig(String namespace) {
this.namespace = namespace.endsWith(".") ? namespace : namespace + ".";
this.configInstance = Archaius1Utils.initConfig("eureka-client");
this.transportConfig = new DefaultEurekaTransportConfig(namespace, this.configInstance);
} public int getRegistryFetchIntervalSeconds() {
return this.configInstance.getIntProperty(this.namespace + "client.refresh.interval", 30).get();
} public int getInstanceInfoReplicationIntervalSeconds() {
return this.configInstance.getIntProperty(this.namespace + "appinfo.replicate.interval", 30).get();
} public int getInitialInstanceInfoReplicationIntervalSeconds() {
return this.configInstance.getIntProperty(this.namespace + "appinfo.initial.replicate.time", 40).get();
} public int getEurekaServiceUrlPollIntervalSeconds() {
return this.configInstance.getIntProperty(this.namespace + "serviceUrlPollIntervalMs", 300000).get() / 1000;
} public String getProxyHost() {
return this.configInstance.getStringProperty(this.namespace + "eurekaServer.proxyHost", (String)null).get();
} ........................eureka client 默认值加载............................ }

3.2 @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
public class EurekaDiscoveryClientConfiguration { class Marker {} @Bean
public Marker eurekaDiscoverClientMarker() {
return new Marker();
} # 具体配置刷新事件的监听器
@Configuration
@ConditionalOnClass(RefreshScopeRefreshedEvent.class)
protected static class EurekaClientConfigurationRefresher { @Autowired(required = false)
private EurekaClient eurekaClient; @Autowired(required = false)
private EurekaAutoServiceRegistration autoRegistration; @EventListener(RefreshScopeRefreshedEvent.class)
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
//This will force the creation of the EurkaClient bean if not already created
//to make sure the client will be reregistered after a refresh event
if(eurekaClient != null) {
eurekaClient.getApplications();
}
if (autoRegistration != null) {
// register in case meta data changed
this.autoRegistration.stop();
this.autoRegistration.start();
}
}
} # 健康检查配置
@Configuration
@ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
protected static class EurekaHealthCheckHandlerConfiguration { @Autowired(required = false)
private HealthAggregator healthAggregator = new OrderedHealthAggregator(); @Bean
@ConditionalOnMissingBean(HealthCheckHandler.class)
public EurekaHealthCheckHandler eurekaHealthCheckHandler() {
return new EurekaHealthCheckHandler(this.healthAggregator);
}
}
}

该配置文件主要做了两件事情,一个是监听RefreshScopeRefreshedEvent事件,配置文件动态刷新时触发。另一个是配置健康检查处理程序。

3.3、 EurekaClientAutoConfiguration

根据源码可以看到这里创建了EurekaClientConfigBean、EurekaInstanceConfigBean两个基本配置,以及EurekaServiceRegistry

@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
  • InstanceInfo

使用EurekaInstanceConfig,通过new InstanceInfoFactory().create(config)创建

  • ApplicationInfoManager

使用InstanceInfo以及EurekaInstanceConfig创建:new ApplicationInfoManager(config, instanceInfo)

  • EurekaClient
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}

使用ApplicationInfoManager、EurekaClientConfig创建:new CloudEurekaClient(manager, config, this.optionalArgs,this.context)

  • DiscoveryClient
@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
return new EurekaDiscoveryClient(config, client);
}

通过EurekaInstanceConfig、EurekaClient创建:new EurekaDiscoveryClient(config, client)

  • EurekaRegistration
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
CloudEurekaInstanceConfig instanceConfig,
ApplicationInfoManager applicationInfoManager,
@Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
}

通过EurekaClient、CloudEurekaInstanceConfig、ApplicationInfoManager来创建

  • EurekaAutoServiceRegistration
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry,
EurekaRegistration registration) {
return new EurekaAutoServiceRegistration(context, registry, registration);
}

通过EurekaServiceRegistry、EurekaRegistration来创建

3.4、 EurekaAutoServiceRegistration

该类实现了接口 SmartLifecycle 的方法。SmartLifecycle 是一个接口。当Spring容器加载所有bean并完成初始化之后,会接着回调实现该接口的类中对应的方法。

public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered {

	private static final Log log = LogFactory.getLog(EurekaAutoServiceRegistration.class);

	private AtomicBoolean running = new AtomicBoolean(false);

	private int order = 0;

	private AtomicInteger port = new AtomicInteger(0);

	private ApplicationContext context;

	private EurekaServiceRegistry serviceRegistry;

	private EurekaRegistration registration;

	public EurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry serviceRegistry, EurekaRegistration registration) {
this.context = context;
this.serviceRegistry = serviceRegistry;
this.registration = registration;
} @Override
public void start() {
..................
}
@Override
public void stop() {
this.serviceRegistry.deregister(this.registration);
this.running.set(false);
} @Override
public boolean isRunning() {
return this.running.get();
} @Override
public int getPhase() {
return 0;
} @Override
public boolean isAutoStartup() {
return true;
} @Override
public void stop(Runnable callback) {
stop();
callback.run();
} @Override
public int getOrder() {
return this.order;
} @EventListener(WebServerInitializedEvent.class)
public void onApplicationEvent(WebServerInitializedEvent event) {
..................
} @EventListener(ContextClosedEvent.class)
public void onApplicationEvent(ContextClosedEvent event) {
if( event.getApplicationContext() == context ) {
stop();
}
} }
3.4.1 把自身应用实例的信息注册到eureka server中(start)
@Override
public void start() {
// 设置端口号
if (this.port.get() != 0) {
if (this.registration.getNonSecurePort() == 0) {
this.registration.setNonSecurePort(this.port.get());
} if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
this.registration.setSecurePort(this.port.get());
}
} // 判断端口号是否被占用
if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
//注册自身实例服务到注册中心
this.serviceRegistry.register(this.registration);
//触发服务注册时间
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
this.running.set(true);
}
}
3.4.2 服务主动下线 stop()
@Override
public void stop() {
this.serviceRegistry.deregister(this.registration);
this.running.set(false);
}

调用this.serviceRegistry.deregister(this.registration)方法,告知eureka server自身服务要下线

3.5、 EurekaServiceRegistry

根据源码可知 EurekaAutoServiceRegistration 中的 start()、stop() 方法分别调用了 EurekaServiceRegistry 的 register()、deregister() 方法实现了服务注册和服务主动下线。下面我们就看一下这两个方法的实现。

3.5.1 服务注册 register

调用ApplicationInfoManager的setInstanceStatus方法来更改状态

@Override
public void register(EurekaRegistration reg) {
maybeInitializeClient(reg); if (log.isInfoEnabled()) {
log.info("Registering application " + reg.getInstanceConfig().getAppname()
+ " with eureka with status "
+ reg.getInstanceConfig().getInitialStatus());
} reg.getApplicationInfoManager()
.setInstanceStatus(reg.getInstanceConfig().getInitialStatus()); reg.getHealthCheckHandler().ifAvailable(healthCheckHandler ->
reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
} private void maybeInitializeClient(EurekaRegistration reg) {
// force initialization of possibly scoped proxies
reg.getApplicationInfoManager().getInfo();
reg.getEurekaClient().getApplications();
}
3.5.2 服务自动下线 deregister

调用ApplicationInfoManager的setInstanceStatus方法来将状态设置为InstanceInfo.InstanceStatus.DOWN

@Override
public void deregister(EurekaRegistration reg) {
if (reg.getApplicationInfoManager().getInfo() != null) { if (log.isInfoEnabled()) {
log.info("Unregistering application " + reg.getInstanceConfig().getAppname()
+ " with eureka with status DOWN");
} reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN); //shutdown of eureka client should happen with EurekaRegistration.close()
//auto registration will create a bean which will be properly disposed
//manual registrations will need to call close()
}
}

3.6、 ApplicationInfoManager

设置服务实例状态并发布StatusChangeEvent事件,通知所有该事件监听者

/**
* 设置此实例的状态。 应用程序可以使用它来标识它是否已准备好接收流量。 在此处设置状态还会通知所有已注册的监听器状态更改事件。
*/
public synchronized void setInstanceStatus(InstanceStatus status) {
//当前服务实例状态
InstanceStatus next = instanceStatusMapper.map(status);
if (next == null) {
return;
}
//当前服务实例之前的状态
InstanceStatus prev = instanceInfo.setStatus(next);
if (prev != null) {
for (StatusChangeListener listener : listeners.values()) {
try {
//状态变化事件通知所有监听者
listener.notify(new StatusChangeEvent(prev, next));
} catch (Exception e) {
logger.warn("failed to notify listener: {}", listener.getId(), e);
}
}
}
}

3.7、DiscoveryClient -> initScheduledTasks()

这里注册了StatusChangeListener,之后触发instanceInfoReplicator.onDemandUpdate()

 /**
* Initializes all scheduled tasks.
*/
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// 缓存刷新定时器
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
} if (clientConfig.shouldRegisterWithEureka()) {
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); // 心跳定时器
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS); // InstanceInfo replicator
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2); // burstSize
//状态变更监听器
statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
@Override
public String getId() {
return "statusChangeListener";
} @Override
public void notify(StatusChangeEvent statusChangeEvent) {
if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
// log at warn level if DOWN was involved
logger.warn("Saw local status change event {}", statusChangeEvent);
} else {
logger.info("Saw local status change event {}", statusChangeEvent);
}
instanceInfoReplicator.onDemandUpdate();
}
}; if (clientConfig.shouldOnDemandUpdateStatusChange()) {
applicationInfoManager.registerStatusChangeListener(statusChangeListener);
} instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
} else {
logger.info("Not registering with Eureka server per configuration");
}
}

3.8、InstanceInfoReplicator -> onDemandUpdate

这里的onDemandUpdate()方法主要是执行InstanceInfoReplicator.this.run() 而这个run方法主要是判断是否dirty,如果是则调用discoveryClient.register()

 public boolean onDemandUpdate() {
if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) {
if (!scheduler.isShutdown()) {
scheduler.submit(new Runnable() {
@Override
public void run() {
logger.debug("Executing on-demand update of local InstanceInfo"); Future latestPeriodic = scheduledPeriodicRef.get();
if (latestPeriodic != null && !latestPeriodic.isDone()) {
logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update");
latestPeriodic.cancel(false);
} InstanceInfoReplicator.this.run();
}
});
return true;
} else {
logger.warn("Ignoring onDemand update due to stopped scheduler");
return false;
}
} else {
logger.warn("Ignoring onDemand update due to rate limiter");
return false;
}
} public void run() {
try {
discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info replicator", t);
} finally {
Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}

3.9、DiscoveryClient -> register

最后终于找到了服务注册的具体真正实现,register() 才是真正去与远程的Eureka Server交互,注册服务的操作

 /**
* 通过REST调用来注册eureka服务。
*/
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}

最后总结一下:

EurekaClientAutoConfiguration 构造了 EurekaClientConfigBean、EurekaInstanceConfigBean 以及 EurekaServiceRegistry,接着在这几个对象的基础上进一步构建了 ApplicationInfoManager、CloudEurekaClient等。

其中ApplicationInfoManager 负责变更实例状态并发布 StatusChangeEvent事件,而CloudEurekaClient继承了com.netflix.discovery.DiscoveryClient 包含了statusChangeListener 用于响应S tatusChangeEvent,最后触发的是DiscoveryClient.register方法,与远程的Eureka Server通信,同步实例状态。

Spring Cloud Eureka(六):Eureka Client 如何注册到Eureka Server的更多相关文章

  1. Spring Cloud(一):服务注册中心Eureka

    Spring Cloud 基于 Netflix 的几个开源项目进行了封装,提供包括服务注册与发现(Eureka),智能路由(Zuul),熔断器(Hystrix),客户端负载均衡(Ribbon)等在内的 ...

  2. Spring Cloud(二):服务注册与发现 Eureka【Finchley 版】

    Spring Cloud(二):服务注册与发现 Eureka[Finchley 版]  发表于 2018-04-15 |  更新于 2018-05-07 |  上一篇主要介绍了相关理论,这一篇开始我们 ...

  3. Spring Cloud(六):Hystrix 监控数据聚合 Turbine【Finchley 版】

    Spring Cloud(六):Hystrix 监控数据聚合 Turbine[Finchley 版]  发表于 2018-04-17 |  更新于 2018-05-07 |  上一篇我们介绍了使用 H ...

  4. 一起来学Spring Cloud | 第六章:服务网关 ( Zuul)

    本章节,我们讲解springcloud重要组件:微服务网关Zuul.如果有同学从第一章看到本章的,会发现我们已经讲解了大部分微服务常用的基本组件. 已经讲解过的: 一起来学Spring Cloud | ...

  5. Alibaba Nacos 学习(三):Spring Cloud Nacos Discovery - FeignClient,Nacos 服务注册与发现

    Alibaba Nacos 学习(一):Nacos介绍与安装 Alibaba Nacos 学习(二):Spring Cloud Nacos Config Alibaba Nacos 学习(三):Spr ...

  6. Spring Cloud(一)服务的注册与发现(Eureka)

    Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集 ...

  7. spring cloud之docker微服务客户端注册eureka问题

    正常我们起一个微服务注册到eureka他的实例id是默认这样的主机名称:服务名称:服务端口号, 如果配置eureka.instance.prefer-ip-address=true则实例id为主机Ip ...

  8. Spring Cloud官方文档中文版-服务发现:Eureka服务端

    官方文档地址为:http://cloud.spring.io/spring-cloud-static/Dalston.SR3/#spring-cloud-eureka-server 文中例子我做了一些 ...

  9. Spring Cloud微服务系列文,Hystrix与Eureka的整合

    和Ribbon等组件一样,在项目中,Hystrix一般不会单独出现,而是会和Eureka等组件配套出现.在Hystrix和Eureka整合后的框架里,一般会用到Hystrix的断路器以及合并请求等特性 ...

随机推荐

  1. HelenOS

    HelenOS 来源 http://www.helenos.org/ 关于HELENOS HelenOS是一种基于便携式微内核的多服务器操作系统,从头开始设计和实现.它将关键操作系统功能(如文件系统, ...

  2. ArrayList和CopyOnWriteArrayList(转载)

    这篇文章的目的如下: 了解一下ArrayList和CopyOnWriteArrayList的增删改查实现原理 看看为什么说ArrayList查询快而增删慢? CopyOnWriteArrayList为 ...

  3. Linux学习笔记:split切分文件并按规律命名及添加拓展名

    基础知识 功能:使用 shell 的 split 可以将一个大文件分割成很多个小文件,有时文件太大处理起来不方便就需要使用到了. 在默认情况下将按照每1000行切割成一个小文件. 语法: split ...

  4. 一个简单的window.onscroll实例

    鉴于better-scroll实现这个效果很复杂,想用最原生的效果来实现吸顶效果 一个简单的window.onscroll实例,可以应用于移动端 demo 一个简单的window.onscroll实例 ...

  5. maccms代码审计

    由于工作原因,分析了很多的cms也都写过文章,不过觉得好像没什么骚操作都是网上的基本操作,所以也就没发表在网站上,都保存在本地.最近突然发现自己博客中实战的东西太少了,决定将以前写的一些文章搬过来,由 ...

  6. CentOS7安装CDH 第五章:CDH的安装和部署-CDH5.7.0

    相关文章链接 CentOS7安装CDH 第一章:CentOS7系统安装 CentOS7安装CDH 第二章:CentOS7各个软件安装和启动 CentOS7安装CDH 第三章:CDH中的问题和解决方法 ...

  7. linux 基础8-shell script

    1. 什么是shell script 1.1 介绍: 什么是 shell script (程序化脚本) 呢?就字面上的意义,我们将他分为两部份. 在『 shell 』部分,我们在bash当中已经提过了 ...

  8. sh make.sh fatal error: opencv2/opencv.hpp: No such file or directory

    问题: sh make.sh fatal error: opencv2/opencv.hpp: No such file or directory 解决: sudo apt-get install l ...

  9. python3使用ConfigParser从配置文件中获取列表

    使用python3使用ConfigParser从配置文件中获取列表 testConfig.py #!/usr/bin/env python # coding=utf- __author__ = 'St ...

  10. U盘装manjaro 18报Python error in job “initcpio”

    更新: 还是不行,大家再找别的方法. 以下内容仅做参考,意义不大. U盘安装 用rufus做U盘,写入选DD模式,比ISO模式可靠点. 错误解决 报错信息: Error installing Manj ...