温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Spring Cloud学习教程之DiscoveryClient的深入探究

发布时间:2020-08-21 14:27:40 来源:脚本之家 阅读:389 作者:洛阳融科聂晨 栏目:编程语言

前言

当我们使用@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类就实现了该接口

Spring Cloud学习教程之DiscoveryClient的深入探究

下面我们来简单演示一下案例,创建类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运行 会得到如下结果:

Spring Cloud学习教程之DiscoveryClient的深入探究

另外在这里的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 ,我们来看一下结构图:

Spring Cloud学习教程之DiscoveryClient的深入探究

我们可以得知这个类实现了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的默认装配。

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

Spring Cloud学习教程之DiscoveryClient的深入探究

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

java -jar member-server-0.0.1-SNAPSHOT.jar --server.port=8800

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

Spring Cloud学习教程之DiscoveryClient的深入探究

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI