前言
當我們使用@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越先執行)
二、DiscoveryClient源碼探究
@EnableDiscoveyClient
/*
* 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}
請注意 @Import(EnableDiscoveryClientImportSelector.class) 我們可以參考一下這個類:
/*
* 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 org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.type.AnnotationMetadata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
/**
* @author Spencer Gibb
*/
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister");
if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
} else {
Environment env = getEnvironment();
if(ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
}
@Override
protected boolean isEnabled() {
return new RelaxedPropertyResolver(getEnvironment()).getProperty(
"spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
}
@Override
protected boolean hasDefaultFactory() {
return true;
}
}
這個類重寫的方法來自于接口 ImportSelector,我們可以根據 if(autoRegister)下的代碼追蹤到類:org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration ,我們來看一下結構圖:

我們可以得知這個類實現了Lifecycle接口,那么我們看一看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() {
}
}
那么至此我們可以總結如下幾點:
1、使用@DiscoveryClient注冊服務是利用了LifeCycle機制,在容器啟動時會執行ServiceRegistry的register()方法。
2、使用@DiscoveryClient要比@EnableEurekaClient與@EnableEurekaServer更靈活,因為它屏蔽了對服務注冊的實現,我們甚至可以自定義注冊中心。
3、這里面還會自動去尋找DiscoveryClient接口的實現用作服務發現
三、Discoveryclient實戰之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.1-SNAPSHOT.jar --server.port=8800
我們可以看到redis里已經緩存的有服務注冊的值了:

總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。