当我们使用@DiscoveryClient注解的时候,会不会有如下疑问:它为什么会进行注册服务的操作,它不是应该用作服务发现的吗?下面我们就来深入的探究一下其源码。

一、Springframework的LifeCycle接口

  要搞明白这个问题我们需要了解一下这个重要的接口:

/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.context; /**
* A common interface defining methods for start/stop lifecycle control.
* The typical use case for this is to control asynchronous processing.
* <b>NOTE: This interface does not imply specific auto-startup semantics.
* Consider implementing {@link SmartLifecycle} for that purpose.</b>
*
* <p>Can be implemented by both components (typically a Spring bean defined in a
* Spring context) and containers (typically a Spring {@link ApplicationContext}
* itself). Containers will propagate start/stop signals to all components that
* apply within each container, e.g. for a stop/restart scenario at runtime.
*
* <p>Can be used for direct invocations or for management operations via JMX.
* In the latter case, the {@link org.springframework.jmx.export.MBeanExporter}
* will typically be defined with an
* {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},
* restricting the visibility of activity-controlled components to the Lifecycle
* interface.
*
* <p>Note that the Lifecycle interface is only supported on <b>top-level singleton
* beans</b>. On any other component, the Lifecycle interface will remain undetected
* and hence ignored. Also, note that the extended {@link SmartLifecycle} interface
* provides integration with the application context's startup and shutdown phases.
*
* @author Juergen Hoeller
* @since 2.0
* @see SmartLifecycle
* @see ConfigurableApplicationContext
* @see org.springframework.jms.listener.AbstractMessageListenerContainer
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean
*/
public interface Lifecycle { /**
* Start this component.
* <p>Should not throw an exception if the component is already running.
* <p>In the case of a container, this will propagate the start signal to all
* components that apply.
* @see SmartLifecycle#isAutoStartup()
*/
void start(); /**
* Stop this component, typically in a synchronous fashion, such that the component is
* fully stopped upon return of this method. Consider implementing {@link SmartLifecycle}
* and its {@code stop(Runnable)} variant when asynchronous stop behavior is necessary.
* <p>Note that this stop notification is not guaranteed to come before destruction: On
* regular shutdown, {@code Lifecycle} beans will first receive a stop notification before
* the general destruction callbacks are being propagated; however, on hot refresh during a
* context's lifetime or on aborted refresh attempts, only destroy methods will be called.
* <p>Should not throw an exception if the component isn't started yet.
* <p>In the case of a container, this will propagate the stop signal to all components
* that apply.
* @see SmartLifecycle#stop(Runnable)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
void stop(); /**
* Check whether this component is currently running.
* <p>In the case of a container, this will return {@code true} only if <i>all</i>
* components that apply are currently running.
* @return whether the component is currently running
*/
boolean isRunning(); }

  该接口定义启动/停止生命周期控制方法,当spring ioc容器启动或停止时将发送一个启动或者停止的信号通知到各个组件,因此我们可以在对应的方法里做我们想要的事情。我们可以通过类图发现我们常用的ClasspathXmlApplicationContext类就实现了该接口

  下面我们来简单演示一下案例,创建类MyLifeCycle:

package org.hzgj.spring.study.context;

import org.springframework.context.SmartLifecycle;

public class MyLifeCycle implements SmartLifecycle {
@Override
public void start() {
System.out.println("MyLifeCycle start ....");
} @Override
public void stop() {
System.out.println("MyLifeCycle stop .....");
} @Override
public boolean isRunning() {
return false;
} @Override
public boolean isAutoStartup() {
return true;
} @Override
public void stop(Runnable callback) { } @Override
public int getPhase() {
System.out.println("phase");
return 10;
}
}

  在这里我们继承SmartLifeCycle该接口继承了LifeCycle, isRunning方法用于检测当前的组件是否处在运行状态,注意只有当isRunning返回值为false才可以运行

  我们把MyLifeCycle配置到spring配置文件里,通过ClassPathXmlApplicationContext运行 会得到如下结果:

  另外在这里的getPhase方法,这个是定义阶段值(可以理解为优先级,值越小对应的LifeCycle越先执行)

  

二、服务自动注册原理

  在spring-cloud-commons的jar包里的META-INF/spring.factories里有如下配置:

  

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.CommonsClientAutoConfiguration,\
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.commons.httpclient.HttpClientConfiguration,\
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration

  在这里其中有一项是AutoServiceRegistrationAutoConfiguration的自动化配置,我们来看一下其源码:

package org.springframework.cloud.client.serviceregistry;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; /**
* @author Spencer Gibb
*/
@Configuration
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration { @Autowired(required = false)
private AutoServiceRegistration autoServiceRegistration; @Autowired
private AutoServiceRegistrationProperties properties; @PostConstruct
protected void init() {
if (autoServiceRegistration == null && this.properties.isFailFast()) {
throw new IllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
}
}
}

  通过源码我们可以发现在这里会自动注入一个AutoServiceRegistration的接口类型

  其中该接口有一个实现类:org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration,我们来看一下结构图:

我们可以得知这个类实现了Lifecycle接口,并实现了AutoServiceRegistration接口。那么我们看一看start方法,此方法在它的父类AbstractDiscoveryLifecycle里:

/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.client.discovery; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PreDestroy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment; /**
* Lifecycle methods that may be useful and common to various DiscoveryClient implementations.
*
* @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
*
* @author Spencer Gibb
*/
@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> { private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.class); private boolean autoStartup = true; private AtomicBoolean running = new AtomicBoolean(false); private int order = 0; private ApplicationContext context; private Environment environment; private AtomicInteger port = new AtomicInteger(0); protected ApplicationContext getContext() {
return context;
} @Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
this.environment = this.context.getEnvironment();
} @Deprecated
protected Environment getEnvironment() {
return environment;
} @Deprecated
protected AtomicInteger getPort() {
return port;
} @Override
public boolean isAutoStartup() {
return this.autoStartup;
} @Override
public void stop(Runnable callback) {
try {
stop();
} catch (Exception e) {
logger.error("A problem occurred attempting to stop discovery lifecycle", e);
}
callback.run();
} @Override
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
} // only set the port if the nonSecurePort is 0 and this.port != 0
if (this.port.get() != 0 && getConfiguredPort() == 0) {
setConfiguredPort(this.port.get());
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get() && getConfiguredPort() > 0) {
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
getConfiguration()));
this.running.compareAndSet(false, true);
}
} @Deprecated
protected abstract int getConfiguredPort();
@Deprecated
protected abstract void setConfiguredPort(int port); /**
* @return if the management service should be registered with the {@link ServiceRegistry}
*/
protected boolean shouldRegisterManagement() {
return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
} /**
* @return the object used to configure the registration
*/
@Deprecated
protected abstract Object getConfiguration(); /**
* Register the local service with the DiscoveryClient
*/
protected abstract void register(); /**
* Register the local management service with the DiscoveryClient
*/
protected void registerManagement() {
} /**
* De-register the local service with the DiscoveryClient
*/
protected abstract void deregister(); /**
* De-register the local management service with the DiscoveryClient
*/
protected void deregisterManagement() {
} /**
* @return true, if the {@link DiscoveryLifecycle} is enabled
*/
protected abstract boolean isEnabled(); /**
* @return the serviceId of the Management Service
*/
@Deprecated
protected String getManagementServiceId() {
// TODO: configurable management suffix
return this.context.getId() + ":management";
} /**
* @return the service name of the Management Service
*/
@Deprecated
protected String getManagementServiceName() {
// TODO: configurable management suffix
return getAppName() + ":management";
} /**
* @return the management server port
*/
@Deprecated
protected Integer getManagementPort() {
return ManagementServerPortUtils.getPort(this.context);
} /**
* @return the app name, currently the spring.application.name property
*/
@Deprecated
protected String getAppName() {
return this.environment.getProperty("spring.application.name", "application");
} @Override
public void stop() {
if (this.running.compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
}
} @PreDestroy
public void destroy() {
stop();
} @Override
public boolean isRunning() {
return this.running.get();
} protected AtomicBoolean getRunning() {
return running;
} @Override
public int getOrder() {
return this.order;
} @Override
public int getPhase() {
return 0;
} @Override
@Deprecated
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
// TODO: take SSL into account
// Don't register the management port as THE port
if (!"management".equals(event.getApplicationContext().getNamespace())) {
this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());
this.start();
}
}
}

注意在start方法里有一段这个代码:

if (!this.running.get() && getConfiguredPort() > 0) {
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
getConfiguration()));
this.running.compareAndSet(false, true);
}

请注意register() 这个方法是本类里的抽象方法。那么我们回过头看一下AbstractAutoServiceRegistration类里的代码,我这里只贴出关键部分:

//.....

    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
} //......
/**
* Register the local service with the {@link ServiceRegistry}
*/
@Override
protected void register() {
this.serviceRegistry.register(getRegistration());
}

我们可以发现在构造函数里传了一个ServiceRegistry类型,这个接口是SpringCloud给我们提供用于服务注册的接口。在这里EurekaServiceRegistry就是实现了此接口:

/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ package org.springframework.cloud.netflix.eureka.serviceregistry; import java.util.HashMap; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import com.netflix.appinfo.InstanceInfo; /**
* @author Spencer Gibb
*/
public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> { private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class); @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()); if (reg.getHealthCheckHandler() != null) {
reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler());
}
} private void maybeInitializeClient(EurekaRegistration reg) {
// force initialization of possibly scoped proxies
reg.getApplicationInfoManager().getInfo();
reg.getEurekaClient().getApplications();
} @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()
}
} @Override
public void setStatus(EurekaRegistration registration, String status) {
InstanceInfo info = registration.getApplicationInfoManager().getInfo(); //TODO: howto deal with delete properly?
if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {
registration.getEurekaClient().cancelOverrideStatus(info);
return;
} //TODO: howto deal with status types across discovery systems?
InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status);
registration.getEurekaClient().setStatus(newStatus, info);
} @Override
public Object getStatus(EurekaRegistration registration) {
HashMap<String, Object> status = new HashMap<>(); InstanceInfo info = registration.getApplicationInfoManager().getInfo();
status.put("status", info.getStatus().toString());
status.put("overriddenStatus", info.getOverriddenStatus().toString()); return status;
} public void close() {
} }

那么至此我们可以知道自动注册服务是利用了LifeCycle机制,在容器启动时会执行ServiceRegistry的register()方法。

三、服务自动注册实战之redis注册中心

下面我们实现一个基于redis为注册中心的需求,来理解一下Discoveryclient。顺便理解一下Springcloud重要的接口:ServiceRegistry,ServiceInstance,再此之前我们先添加对redis的支持:

   compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis'

1、实现Registration接口

package com.hzgj.lyrk.member;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.stereotype.Component; import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URI;
import java.util.Enumeration;
import java.util.Map; @Component
public class RedisRegistration implements Registration { @Value("${server.port}")
private Integer port; @Value("${spring.application.name}")
private String applicationName; private String host; public void setHost(String host) {
this.host = host;
} public void setPort(Integer port) {
this.port = port;
} public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
} @Override
public String getServiceId() { return applicationName + ":" + getHost() + ":" + getPort();
} @Override
public String getHost() {
try {
if (host == null)
return getLocalHostLANAddress().getHostAddress();
else
return host;
} catch (Exception e) {
e.printStackTrace();
}
return null;
} @Override
public int getPort() {
return port;
} @Override
public boolean isSecure() {
return false;
} @Override
public URI getUri() {
return null;
} @Override
public Map<String, String> getMetadata() {
return null;
} public String getServiceName() {
return this.applicationName;
} public InetAddress getLocalHostLANAddress() throws Exception {
try {
InetAddress candidateAddress = null;
// 遍历所有的网络接口
for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
// 在所有的接口下再遍历IP
for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址,就是它了
return inetAddr;
} else if (candidateAddress == null) {
// site-local类型的地址未被发现,先记录候选地址
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress != null) {
return candidateAddress;
}
// 如果没有发现 non-loopback地址.只能用最次选的方案
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
return jdkSuppliedAddress;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

  该接口继承了ServiceIntance,那么此接口最主要作用就是定义了一个服务实例的规范,比如说它的serviceId是什么,端口号是什么等

2、实现ServiceRegistry的接口

package com.hzgj.lyrk.member;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.data.redis.core.StringRedisTemplate; public class RedisServiceRegistry implements ServiceRegistry<RedisRegistration> { @Autowired
private StringRedisTemplate redisTemplate; @Override
public void register(RedisRegistration registration) {
String serviceId = registration.getServiceId();
redisTemplate.opsForList().leftPush(serviceId, registration.getHost() + ":" + registration.getPort()); } @Override
public void deregister(RedisRegistration registration) {
redisTemplate.opsForList().remove(registration.getServiceId(), 1, registration.getHost() + ":" + registration.getPort());
} @Override
public void close() {
//redisTemplate.d
System.out.println("closed ...");
} @Override
public void setStatus(RedisRegistration registration, String status) { } @Override
public <T> T getStatus(RedisRegistration registration) {
return null;
} }

  该接口主要作用是定义如何进行服务注册 ,服务注销,设置与获取服务状态等操作

3、继承 AbstractAutoServiceRegistration抽象类

package com.hzgj.lyrk.member;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry; public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration<RedisRegistration> { @Autowired
private RedisRegistration redisRegistration; protected RedisAutoServiceRegistration(ServiceRegistry<RedisRegistration> serviceRegistry, AutoServiceRegistrationProperties properties) {
super(serviceRegistry, properties);
// serviceRegistry.register(getRegistration());
} @Override
protected int getConfiguredPort() {
return redisRegistration.getPort();
} @Override
protected void setConfiguredPort(int port) { } @Override
protected Object getConfiguration() {
return null;
} @Override
protected boolean isEnabled() {
return true;
} @Override
protected RedisRegistration getRegistration() {
return redisRegistration;
} @Override
protected RedisRegistration getManagementRegistration() {
return null;
}
}

4、定义DiscoveryClient的实现类RedisDiscoveryClient

package com.hzgj.lyrk.member;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.data.redis.core.StringRedisTemplate; import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors; public class RedisDiscoveryClient implements DiscoveryClient { @Autowired
private StringRedisTemplate redisTemplate; @Override
public String description() {
return "redis注册中心的服务发现";
} @Override
public ServiceInstance getLocalServiceInstance() {
return null;
} @Override
public List<ServiceInstance> getInstances(String serviceId) { return redisTemplate.opsForList().range(serviceId, 0, -1).
parallelStream().map((Function<String, ServiceInstance>) s -> {
RedisRegistration redisRegistration = new RedisRegistration();
redisRegistration.setApplicationName(serviceId);
String hostName = StringUtils.split(s, ":")[0];
String port = StringUtils.split(s, ":")[1];
redisRegistration.setHost(hostName);
redisRegistration.setPort(Integer.parseInt(port));
//redisRegistration
return redisRegistration;
}).collect(Collectors.toList()); } @Override
public List<String> getServices() {
List<String> list = new ArrayList<>();
list.addAll(redisTemplate.keys("*"));
return list;
}
}

  该类主要是针对于redis注册中心的服务发现

5、定义自动装配的类用以创建对应的bean

package com.hzgj.lyrk.member;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; @Configuration
@EnableConfigurationProperties(RedisConfig.class)
@ConditionalOnProperty(value = "spring.redis.registry.enabled", matchIfMissing = true)
public class RedisRegistryAutoConfiguration { @Bean
RedisServiceRegistry redisServiceRegistry(RedisConfig redisConfig) {
System.out.println(redisConfig.getHost()); return new RedisServiceRegistry();
} @Bean
RedisAutoServiceRegistration redisAutoServiceRegistration(RedisServiceRegistry redisServiceRegistry) {
return new RedisAutoServiceRegistration(redisServiceRegistry, new AutoServiceRegistrationProperties());
} @Bean
@Primary
RedisDiscoveryClient redisDiscoveryClient() {
return new RedisDiscoveryClient();
}
}

6、定义启动类

package com.hzgj.lyrk.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext; @EnableDiscoveryClient
@SpringBootApplication(exclude = {SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class})
public class MemberApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(MemberApplication.class, args);
DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
discoveryClient.getServices().forEach(action -> {
System.out.println(action);
});
}
}

  这里在SpringbootApplication注解里排除DiscoveryClient的默认装配。

当我们启动成功后可以发现,控制台已经输出对应的服务名称与地址:

  我们再次通过gradle打包生成jar文件并运行:

java -jar member-server-0.0.-SNAPSHOT.jar --server.port=

  我们可以看到redis里已经缓存的有服务注册的值了:

SpringCloud学习之DiscoveryClient探究的更多相关文章

  1. SpringCloud学习之Ribbon

    一.负载均衡与Ribbon 负载均衡,在集群中是很常见的一个“名词”,顾名思义是根据一定的算法将请求分摊至对应的服务节点上,常见的算法有如下几种: 轮询法:所有请求被依次分发到每台应用服务器上,每台服 ...

  2. SpringCloud学习心得—1.3—Eureka与REST API

      SpringCloud学习心得—1.3—Eureka与REST API Eureka的REST API接口 API的基本访问 Eureka REST APIEureka 作为注册中心,其本质是存储 ...

  3. SpringCloud学习之feign

    一.关于feigin feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问.当然我们也可以在创建Feign对象时定制自定义解码器(xml或者jso ...

  4. SpringCloud学习系列之三----- 断路器(Hystrix)和断路器监控(Dashboard)

    前言 本篇主要介绍的是SpringCloud中的断路器(Hystrix)和断路器指标看板(Dashboard)的相关使用知识. SpringCloud Hystrix Hystrix 介绍 Netfl ...

  5. SpringCloud学习(二):微服务入门实战项目搭建

    一.开始使用Spring Cloud实战微服务 1.SpringCloud是什么? 云计算的解决方案?不是 SpringCloud是一个在SpringBoot的基础上构建的一个快速构建分布式系统的工具 ...

  6. Android:日常学习笔记(9)———探究持久化技术

    Android:日常学习笔记(9)———探究持久化技术 引入持久化技术 什么是持久化技术 持久化技术就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失 ...

  7. Android:日常学习笔记(9)———探究广播机制

    Android:日常学习笔记(9)———探究广播机制 引入广播机制 Andorid广播机制 广播是任何应用均可接收的消息.系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播.通过将 In ...

  8. Android:日常学习笔记(8)———探究UI开发(5)

    Android:日常学习笔记(8)———探究UI开发(5) ListView控件的使用 ListView概述 A view that shows items in a vertically scrol ...

  9. Android:日常学习笔记(7)———探究UI开发(4)

    Android:日常学习笔记(7)———探究UI开发(4) UI概述  View 和 ViewGrou Android 应用中的所有用户界面元素都是使用 View 和 ViewGroup 对象构建而成 ...

随机推荐

  1. RxSwift(一)

    文/iOS_Deve(简书作者) 原文链接:http://www.jianshu.com/p/429b5160611f 著作权归作者所有,转载请联系作者获得授权,并标注"简书作者" ...

  2. bzoj 4373 算术天才⑨与等差数列

    4373: 算术天才⑨与等差数列 Time Limit: 10 Sec  Memory Limit: 128 MBhttp://www.lydsy.com/JudgeOnline/problem.ph ...

  3. Linux命令及lamp搭建

    单纯属于Linux的命令:1.强制卸载有依赖关系的软件包: rpm -e httpd-2.2.15-26.el6.x86_64 --nodeps(--nodeps表示无依赖)4.删除当前目录所有的文件 ...

  4. OpenShift实战(三):OpenShift持久化存储Registry

    1.查看Registry组件的DC关于volume的定义 可以看到registry-storage这个挂载点被指向了一个/registry目录,使用的是empty directory,即数据保存在计算 ...

  5. GIT入门笔记(20)- git 开发提交代码过程梳理

    git开发提交流程新项目开发,可以直接往master上提交老项目维护,可以在分支上修改提交,多次add和commit之后,也可以用pull合并主干和本地master,解决冲突后再push 1.检出代码 ...

  6. java循环遍历类属性 get 和set值方法

    //遍历sqspb类 成员为String类型 属性为空的全部替换为"/"Field[] fields = sqspb.getClass().getDeclaredFields(); ...

  7. 前端学习之jquery/下

    前端学习之jquery 一 属性操作 html(): console.log($("div").html()); $(".test").html("& ...

  8. mysql 练习题

    导出现有数据库数据: C:\Users\Administrator>mysqldump -u root db1>D:\agon\db1.sql -p  #结构+数据 mysqldump - ...

  9. uvalive 3635 Pie

    https://vjudge.net/problem/UVALive-3635 题意: 有F+1个人要分n个蛋糕,他们得到的蛋糕的面积必须是一样的,但是每个蛋糕必须是整块的蛋糕,而不是有多块蛋糕拼成的 ...

  10. IDLE常用快捷键汇总(转)

    IDLE(An Integrated DeveLopment Environment for Python)是Python自带的编译器,在初学者,或写小程序,或用于验证的时候,经常用到!如果能熟练掌握 ...