查看: 265|回复: 0

[Java代码] Spring Cloud学习教程之DiscoveryClient的深入探究

发表于 2018-5-4 08:00:02

前言

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

一、Springframework的LifeCycle接口

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

  1. /*
  2. * Copyright 2002-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.context;
  17. /**
  18. * A common interface defining methods for start/stop lifecycle control.
  19. * The typical use case for this is to control asynchronous processing.
  20. * <b>NOTE: This interface does not imply specific auto-startup semantics.
  21. * Consider implementing {[url=home.php?mod=space&uid=17823]@LINK[/url] SmartLifecycle} for that purpose.</b>
  22. *
  23. * <p>Can be implemented by both components (typically a Spring bean defined in a
  24. * Spring context) and containers (typically a Spring {@link ApplicationContext}
  25. * itself). Containers will propagate start/stop signals to all components that
  26. * apply within each container, e.g. for a stop/restart scenario at runtime.
  27. *
  28. * <p>Can be used for direct invocations or for management operations via JMX.
  29. * In the latter case, the {@link org.springframework.jmx.export.MBeanExporter}
  30. * will typically be defined with an
  31. * {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},
  32. * restricting the visibility of activity-controlled components to the Lifecycle
  33. * interface.
  34. *
  35. * <p>Note that the Lifecycle interface is only supported on <b>top-level singleton
  36. * beans</b>. On any other component, the Lifecycle interface will remain undetected
  37. * and hence ignored. Also, note that the extended {@link SmartLifecycle} interface
  38. * provides integration with the application context's startup and shutdown phases.
  39. *
  40. * @author Juergen Hoeller
  41. * @since 2.0
  42. * @see SmartLifecycle
  43. * @see ConfigurableApplicationContext
  44. * @see org.springframework.jms.listener.AbstractMessageListenerContainer
  45. * @see org.springframework.scheduling.quartz.SchedulerFactoryBean
  46. */
  47. public interface Lifecycle {
  48. /**
  49. * Start this component.
  50. * <p>Should not throw an exception if the component is already running.
  51. * <p>In the case of a container, this will propagate the start signal to all
  52. * components that apply.
  53. * @see SmartLifecycle#isAutoStartup()
  54. */
  55. void start();
  56. /**
  57. * Stop this component, typically in a synchronous fashion, such that the component is
  58. * fully stopped upon return of this method. Consider implementing {@link SmartLifecycle}
  59. * and its {[url=home.php?mod=space&uid=24691]@code[/url] stop(Runnable)} variant when asynchronous stop behavior is necessary.
  60. * <p>Note that this stop notification is not guaranteed to come before destruction: On
  61. * regular shutdown, {@code Lifecycle} beans will first receive a stop notification before
  62. * the general destruction callbacks are being propagated; however, on hot refresh during a
  63. * context's lifetime or on aborted refresh attempts, only destroy methods will be called.
  64. * <p>Should not throw an exception if the component isn't started yet.
  65. * <p>In the case of a container, this will propagate the stop signal to all components
  66. * that apply.
  67. * @see SmartLifecycle#stop(Runnable)
  68. * @see org.springframework.beans.factory.DisposableBean#destroy()
  69. */
  70. void stop();
  71. /**
  72. * Check whether this component is currently running.
  73. * <p>In the case of a container, this will return {@code true} only if <i>all</i>
  74. * components that apply are currently running.
  75. * @return whether the component is currently running
  76. */
  77. boolean isRunning();
  78. }
复制代码

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

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

  1. package org.hzgj.spring.study.context;
  2. import org.springframework.context.SmartLifecycle;
  3. public class MyLifeCycle implements SmartLifecycle {
  4. @Override
  5. public void start() {
  6. System.out.println("MyLifeCycle start ....");
  7. }
  8. @Override
  9. public void stop() {
  10. System.out.println("MyLifeCycle stop .....");
  11. }
  12. @Override
  13. public boolean isRunning() {
  14. return false;
  15. }
  16. @Override
  17. public boolean isAutoStartup() {
  18. return true;
  19. }
  20. @Override
  21. public void stop(Runnable callback) {
  22. }
  23. @Override
  24. public int getPhase() {
  25. System.out.println("phase");
  26. return 10;
  27. }
  28. }
复制代码

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

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

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

二、DiscoveryClient源码探究

@EnableDiscoveyClient

  1. /*
  2. * Copyright 2013-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.cloud.client.discovery;
  17. import java.lang.annotation.Documented;
  18. import java.lang.annotation.ElementType;
  19. import java.lang.annotation.Inherited;
  20. import java.lang.annotation.Retention;
  21. import java.lang.annotation.RetentionPolicy;
  22. import java.lang.annotation.Target;
  23. import org.springframework.context.annotation.Import;
  24. /**
  25. * Annotation to enable a DiscoveryClient implementation.
  26. * @author Spencer Gibb
  27. */
  28. @Target(ElementType.TYPE)
  29. @Retention(RetentionPolicy.RUNTIME)
  30. @Documented
  31. @Inherited
  32. @Import(EnableDiscoveryClientImportSelector.class)
  33. public @interface EnableDiscoveryClient {
  34. /**
  35. * If true, the ServiceRegistry will automatically register the local server.
  36. */
  37. boolean autoRegister() default true;
  38. }
复制代码

请注意 @Import(EnableDiscoveryClientImportSelector.class) 我们可以参考一下这个类:

  1. /*
  2. * Copyright 2013-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.cloud.client.discovery;
  17. import org.springframework.boot.bind.RelaxedPropertyResolver;
  18. import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
  19. import org.springframework.core.Ordered;
  20. import org.springframework.core.annotation.AnnotationAttributes;
  21. import org.springframework.core.annotation.Order;
  22. import org.springframework.core.env.ConfigurableEnvironment;
  23. import org.springframework.core.env.Environment;
  24. import org.springframework.core.env.MapPropertySource;
  25. import org.springframework.core.type.AnnotationMetadata;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.LinkedHashMap;
  29. import java.util.List;
  30. /**
  31. * @author Spencer Gibb
  32. */
  33. @Order(Ordered.LOWEST_PRECEDENCE - 100)
  34. public class EnableDiscoveryClientImportSelector
  35. extends SpringFactoryImportSelector<EnableDiscoveryClient> {
  36. @Override
  37. public String[] selectImports(AnnotationMetadata metadata) {
  38. String[] imports = super.selectImports(metadata);
  39. AnnotationAttributes attributes = AnnotationAttributes.fromMap(
  40. metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
  41. boolean autoRegister = attributes.getBoolean("autoRegister");
  42. if (autoRegister) {
  43. List<String> importsList = new ArrayList<>(Arrays.asList(imports));
  44. importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
  45. imports = importsList.toArray(new String[0]);
  46. } else {
  47. Environment env = getEnvironment();
  48. if(ConfigurableEnvironment.class.isInstance(env)) {
  49. ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
  50. LinkedHashMap<String, Object> map = new LinkedHashMap<>();
  51. map.put("spring.cloud.service-registry.auto-registration.enabled", false);
  52. MapPropertySource propertySource = new MapPropertySource(
  53. "springCloudDiscoveryClient", map);
  54. configEnv.getPropertySources().addLast(propertySource);
  55. }
  56. }
  57. return imports;
  58. }
  59. @Override
  60. protected boolean isEnabled() {
  61. return new RelaxedPropertyResolver(getEnvironment()).getProperty(
  62. "spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
  63. }
  64. @Override
  65. protected boolean hasDefaultFactory() {
  66. return true;
  67. }
  68. }
复制代码

这个类重写的方法来自于接口 ImportSelector,我们可以根据 if(autoRegister)下的代码追踪到类:org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration ,我们来看一下结构图:

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

  1. /*
  2. * Copyright 2013-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.cloud.client.discovery;
  17. import java.util.concurrent.atomic.AtomicBoolean;
  18. import java.util.concurrent.atomic.AtomicInteger;
  19. import javax.annotation.PreDestroy;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.springframework.beans.BeansException;
  23. import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
  24. import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
  25. import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
  26. import org.springframework.context.ApplicationContext;
  27. import org.springframework.context.ApplicationContextAware;
  28. import org.springframework.context.ApplicationListener;
  29. import org.springframework.core.env.Environment;
  30. /**
  31. * Lifecycle methods that may be useful and common to various DiscoveryClient implementations.
  32. *
  33. * @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
  34. *
  35. * @author Spencer Gibb
  36. */
  37. @Deprecated
  38. public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
  39. ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {
  40. private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.class);
  41. private boolean autoStartup = true;
  42. private AtomicBoolean running = new AtomicBoolean(false);
  43. private int order = 0;
  44. private ApplicationContext context;
  45. private Environment environment;
  46. private AtomicInteger port = new AtomicInteger(0);
  47. protected ApplicationContext getContext() {
  48. return context;
  49. }
  50. @Override
  51. public void setApplicationContext(ApplicationContext applicationContext)
  52. throws BeansException {
  53. this.context = applicationContext;
  54. this.environment = this.context.getEnvironment();
  55. }
  56. @Deprecated
  57. protected Environment getEnvironment() {
  58. return environment;
  59. }
  60. @Deprecated
  61. protected AtomicInteger getPort() {
  62. return port;
  63. }
  64. @Override
  65. public boolean isAutoStartup() {
  66. return this.autoStartup;
  67. }
  68. @Override
  69. public void stop(Runnable callback) {
  70. try {
  71. stop();
  72. } catch (Exception e) {
  73. logger.error("A problem occurred attempting to stop discovery lifecycle", e);
  74. }
  75. callback.run();
  76. }
  77. @Override
  78. public void start() {
  79. if (!isEnabled()) {
  80. if (logger.isDebugEnabled()) {
  81. logger.debug("Discovery Lifecycle disabled. Not starting");
  82. }
  83. return;
  84. }
  85. // only set the port if the nonSecurePort is 0 and this.port != 0
  86. if (this.port.get() != 0 && getConfiguredPort() == 0) {
  87. setConfiguredPort(this.port.get());
  88. }
  89. // only initialize if nonSecurePort is greater than 0 and it isn't already running
  90. // because of containerPortInitializer below
  91. if (!this.running.get() && getConfiguredPort() > 0) {
  92. register();
  93. if (shouldRegisterManagement()) {
  94. registerManagement();
  95. }
  96. this.context.publishEvent(new InstanceRegisteredEvent<>(this,
  97. getConfiguration()));
  98. this.running.compareAndSet(false, true);
  99. }
  100. }
  101. @Deprecated
  102. protected abstract int getConfiguredPort();
  103. @Deprecated
  104. protected abstract void setConfiguredPort(int port);
  105. /**
  106. * @return if the management service should be registered with the {@link ServiceRegistry}
  107. */
  108. protected boolean shouldRegisterManagement() {
  109. return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
  110. }
  111. /**
  112. * @return the object used to configure the registration
  113. */
  114. @Deprecated
  115. protected abstract Object getConfiguration();
  116. /**
  117. * Register the local service with the DiscoveryClient
  118. */
  119. protected abstract void register();
  120. /**
  121. * Register the local management service with the DiscoveryClient
  122. */
  123. protected void registerManagement() {
  124. }
  125. /**
  126. * De-register the local service with the DiscoveryClient
  127. */
  128. protected abstract void deregister();
  129. /**
  130. * De-register the local management service with the DiscoveryClient
  131. */
  132. protected void deregisterManagement() {
  133. }
  134. /**
  135. * @return true, if the {@link DiscoveryLifecycle} is enabled
  136. */
  137. protected abstract boolean isEnabled();
  138. /**
  139. * @return the serviceId of the Management Service
  140. */
  141. @Deprecated
  142. protected String getManagementServiceId() {
  143. // TODO: configurable management suffix
  144. return this.context.getId() + ":management";
  145. }
  146. /**
  147. * @return the service name of the Management Service
  148. */
  149. @Deprecated
  150. protected String getManagementServiceName() {
  151. // TODO: configurable management suffix
  152. return getAppName() + ":management";
  153. }
  154. /**
  155. * @return the management server port
  156. */
  157. @Deprecated
  158. protected Integer getManagementPort() {
  159. return ManagementServerPortUtils.getPort(this.context);
  160. }
  161. /**
  162. * @return the app name, currently the spring.application.name property
  163. */
  164. @Deprecated
  165. protected String getAppName() {
  166. return this.environment.getProperty("spring.application.name", "application");
  167. }
  168. @Override
  169. public void stop() {
  170. if (this.running.compareAndSet(true, false) && isEnabled()) {
  171. deregister();
  172. if (shouldRegisterManagement()) {
  173. deregisterManagement();
  174. }
  175. }
  176. }
  177. @PreDestroy
  178. public void destroy() {
  179. stop();
  180. }
  181. @Override
  182. public boolean isRunning() {
  183. return this.running.get();
  184. }
  185. protected AtomicBoolean getRunning() {
  186. return running;
  187. }
  188. @Override
  189. public int getOrder() {
  190. return this.order;
  191. }
  192. @Override
  193. public int getPhase() {
  194. return 0;
  195. }
  196. @Override
  197. @Deprecated
  198. public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
  199. // TODO: take SSL into account
  200. // Don't register the management port as THE port
  201. if (!"management".equals(event.getApplicationContext().getNamespace())) {
  202. this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());
  203. this.start();
  204. }
  205. }
  206. }
复制代码

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

  1. if (!this.running.get() && getConfiguredPort() > 0) {
  2. register();
  3. if (shouldRegisterManagement()) {
  4. registerManagement();
  5. }
  6. this.context.publishEvent(new InstanceRegisteredEvent<>(this,
  7. getConfiguration()));
  8. this.running.compareAndSet(false, true);
  9. }
复制代码

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

  1. //.....
  2. protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
  3. this.serviceRegistry = serviceRegistry;
  4. this.properties = properties;
  5. }
  6. //......
  7. /**
  8. * Register the local service with the {@link ServiceRegistry}
  9. */
  10. @Override
  11. protected void register() {
  12. this.serviceRegistry.register(getRegistration());
  13. }
复制代码

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

  1. /*
  2. * Copyright 2013-2016 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.springframework.cloud.netflix.eureka.serviceregistry;
  18. import java.util.HashMap;
  19. import org.apache.commons.logging.Log;
  20. import org.apache.commons.logging.LogFactory;
  21. import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
  22. import com.netflix.appinfo.InstanceInfo;
  23. /**
  24. * @author Spencer Gibb
  25. */
  26. public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> {
  27. private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);
  28. @Override
  29. public void register(EurekaRegistration reg) {
  30. maybeInitializeClient(reg);
  31. if (log.isInfoEnabled()) {
  32. log.info("Registering application " + reg.getInstanceConfig().getAppname()
  33. + " with eureka with status "
  34. + reg.getInstanceConfig().getInitialStatus());
  35. }
  36. reg.getApplicationInfoManager()
  37. .setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
  38. if (reg.getHealthCheckHandler() != null) {
  39. reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler());
  40. }
  41. }
  42. private void maybeInitializeClient(EurekaRegistration reg) {
  43. // force initialization of possibly scoped proxies
  44. reg.getApplicationInfoManager().getInfo();
  45. reg.getEurekaClient().getApplications();
  46. }
  47. @Override
  48. public void deregister(EurekaRegistration reg) {
  49. if (reg.getApplicationInfoManager().getInfo() != null) {
  50. if (log.isInfoEnabled()) {
  51. log.info("Unregistering application " + reg.getInstanceConfig().getAppname()
  52. + " with eureka with status DOWN");
  53. }
  54. reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);
  55. //shutdown of eureka client should happen with EurekaRegistration.close()
  56. //auto registration will create a bean which will be properly disposed
  57. //manual registrations will need to call close()
  58. }
  59. }
  60. @Override
  61. public void setStatus(EurekaRegistration registration, String status) {
  62. InstanceInfo info = registration.getApplicationInfoManager().getInfo();
  63. //TODO: howto deal with delete properly?
  64. if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {
  65. registration.getEurekaClient().cancelOverrideStatus(info);
  66. return;
  67. }
  68. //TODO: howto deal with status types across discovery systems?
  69. InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status);
  70. registration.getEurekaClient().setStatus(newStatus, info);
  71. }
  72. @Override
  73. public Object getStatus(EurekaRegistration registration) {
  74. HashMap<String, Object> status = new HashMap<>();
  75. InstanceInfo info = registration.getApplicationInfoManager().getInfo();
  76. status.put("status", info.getStatus().toString());
  77. status.put("overriddenStatus", info.getOverriddenStatus().toString());
  78. return status;
  79. }
  80. public void close() {
  81. }
  82. }
复制代码

那么至此我们可以总结如下几点:

  1、使用@DiscoveryClient注册服务是利用了LifeCycle机制,在容器启动时会执行ServiceRegistry的register()方法。

  2、使用@DiscoveryClient要比@EnableEurekaClient与@EnableEurekaServer更灵活,因为它屏蔽了对服务注册的实现,我们甚至可以自定义注册中心。

  3、这里面还会自动去寻找DiscoveryClient接口的实现用作服务发现

三、Discoveryclient实战之redis注册中心

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

  1. compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis'
复制代码

1、实现Registration接口

  1. package com.hzgj.lyrk.member;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.cloud.client.serviceregistry.Registration;
  4. import org.springframework.stereotype.Component;
  5. import java.net.InetAddress;
  6. import java.net.NetworkInterface;
  7. import java.net.URI;
  8. import java.util.Enumeration;
  9. import java.util.Map;
  10. @Component
  11. public class RedisRegistration implements Registration {
  12. @Value("${server.port}")
  13. private Integer port;
  14. @Value("${spring.application.name}")
  15. private String applicationName;
  16. private String host;
  17. public void setHost(String host) {
  18. this.host = host;
  19. }
  20. public void setPort(Integer port) {
  21. this.port = port;
  22. }
  23. public void setApplicationName(String applicationName) {
  24. this.applicationName = applicationName;
  25. }
  26. @Override
  27. public String getServiceId() {
  28. return applicationName + ":" + getHost() + ":" + getPort();
  29. }
  30. @Override
  31. public String getHost() {
  32. try {
  33. if (host == null)
  34. return getLocalHostLANAddress().getHostAddress();
  35. else
  36. return host;
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. return null;
  41. }
  42. @Override
  43. public int getPort() {
  44. return port;
  45. }
  46. @Override
  47. public boolean isSecure() {
  48. return false;
  49. }
  50. @Override
  51. public URI getUri() {
  52. return null;
  53. }
  54. @Override
  55. public Map<String, String> getMetadata() {
  56. return null;
  57. }
  58. public String getServiceName() {
  59. return this.applicationName;
  60. }
  61. public InetAddress getLocalHostLANAddress() throws Exception {
  62. try {
  63. InetAddress candidateAddress = null;
  64. // 遍历所有的网络接口
  65. for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
  66. NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
  67. // 在所有的接口下再遍历IP
  68. for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
  69. InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
  70. if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
  71. if (inetAddr.isSiteLocalAddress()) {
  72. // 如果是site-local地址,就是它了
  73. return inetAddr;
  74. } else if (candidateAddress == null) {
  75. // site-local类型的地址未被发现,先记录候选地址
  76. candidateAddress = inetAddr;
  77. }
  78. }
  79. }
  80. }
  81. if (candidateAddress != null) {
  82. return candidateAddress;
  83. }
  84. // 如果没有发现 non-loopback地址.只能用最次选的方案
  85. InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
  86. return jdkSuppliedAddress;
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. }
  90. return null;
  91. }
  92. }
复制代码

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

2、实现ServiceRegistry的接口

  1. package com.hzgj.lyrk.member;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
  4. import org.springframework.data.redis.core.StringRedisTemplate;
  5. public class RedisServiceRegistry implements ServiceRegistry<RedisRegistration> {
  6. @Autowired
  7. private StringRedisTemplate redisTemplate;
  8. @Override
  9. public void register(RedisRegistration registration) {
  10. String serviceId = registration.getServiceId();
  11. redisTemplate.opsForList().leftPush(serviceId, registration.getHost() + ":" + registration.getPort());
  12. }
  13. @Override
  14. public void deregister(RedisRegistration registration) {
  15. redisTemplate.opsForList().remove(registration.getServiceId(), 1, registration.getHost() + ":" + registration.getPort());
  16. }
  17. @Override
  18. public void close() {
  19. //redisTemplate.d
  20. System.out.println("closed ...");
  21. }
  22. @Override
  23. public void setStatus(RedisRegistration registration, String status) {
  24. }
  25. @Override
  26. public <T> T getStatus(RedisRegistration registration) {
  27. return null;
  28. }
  29. }
复制代码

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

3、继承 AbstractAutoServiceRegistration抽象类

  1. package com.hzgj.lyrk.member;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
  4. import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
  5. import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
  6. public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration<RedisRegistration> {
  7. @Autowired
  8. private RedisRegistration redisRegistration;
  9. protected RedisAutoServiceRegistration(ServiceRegistry<RedisRegistration> serviceRegistry, AutoServiceRegistrationProperties properties) {
  10. super(serviceRegistry, properties);
  11. // serviceRegistry.register(getRegistration());
  12. }
  13. @Override
  14. protected int getConfiguredPort() {
  15. return redisRegistration.getPort();
  16. }
  17. @Override
  18. protected void setConfiguredPort(int port) {
  19. }
  20. @Override
  21. protected Object getConfiguration() {
  22. return null;
  23. }
  24. @Override
  25. protected boolean isEnabled() {
  26. return true;
  27. }
  28. @Override
  29. protected RedisRegistration getRegistration() {
  30. return redisRegistration;
  31. }
  32. @Override
  33. protected RedisRegistration getManagementRegistration() {
  34. return null;
  35. }
  36. }
复制代码

4、定义DiscoveryClient的实现类RedisDiscoveryClient

  1. package com.hzgj.lyrk.member;
  2. import org.apache.commons.lang.StringUtils;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.cloud.client.ServiceInstance;
  5. import org.springframework.cloud.client.discovery.DiscoveryClient;
  6. import org.springframework.data.redis.core.StringRedisTemplate;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. import java.util.function.Function;
  10. import java.util.stream.Collectors;
  11. public class RedisDiscoveryClient implements DiscoveryClient {
  12. @Autowired
  13. private StringRedisTemplate redisTemplate;
  14. @Override
  15. public String description() {
  16. return "redis注册中心的服务发现";
  17. }
  18. @Override
  19. public ServiceInstance getLocalServiceInstance() {
  20. return null;
  21. }
  22. @Override
  23. public List<ServiceInstance> getInstances(String serviceId) {
  24. return redisTemplate.opsForList().range(serviceId, 0, -1).
  25. parallelStream().map((Function<String, ServiceInstance>) s -> {
  26. RedisRegistration redisRegistration = new RedisRegistration();
  27. redisRegistration.setApplicationName(serviceId);
  28. String hostName = StringUtils.split(s, ":")[0];
  29. String port = StringUtils.split(s, ":")[1];
  30. redisRegistration.setHost(hostName);
  31. redisRegistration.setPort(Integer.parseInt(port));
  32. //redisRegistration
  33. return redisRegistration;
  34. }).collect(Collectors.toList());
  35. }
  36. @Override
  37. public List<String> getServices() {
  38. List<String> list = new ArrayList<>();
  39. list.addAll(redisTemplate.keys("*"));
  40. return list;
  41. }
  42. }
复制代码

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

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

  1. package com.hzgj.lyrk.member;
  2. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  3. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  4. import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.context.annotation.Primary;
  8. @Configuration
  9. @EnableConfigurationProperties(RedisConfig.class)
  10. @ConditionalOnProperty(value = "spring.redis.registry.enabled", matchIfMissing = true)
  11. public class RedisRegistryAutoConfiguration {
  12. @Bean
  13. RedisServiceRegistry redisServiceRegistry(RedisConfig redisConfig) {
  14. System.out.println(redisConfig.getHost());
  15. return new RedisServiceRegistry();
  16. }
  17. @Bean
  18. RedisAutoServiceRegistration redisAutoServiceRegistration(RedisServiceRegistry redisServiceRegistry) {
  19. return new RedisAutoServiceRegistration(redisServiceRegistry, new AutoServiceRegistrationProperties());
  20. }
  21. @Bean
  22. @Primary
  23. RedisDiscoveryClient redisDiscoveryClient() {
  24. return new RedisDiscoveryClient();
  25. }
  26. }
复制代码

6、定义启动类

  1. package com.hzgj.lyrk.member;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.DiscoveryClient;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration;
  7. import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
  8. import org.springframework.context.ConfigurableApplicationContext;
  9. @EnableDiscoveryClient
  10. @SpringBootApplication(exclude = {SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class})
  11. public class MemberApplication {
  12. public static void main(String[] args) {
  13. ConfigurableApplicationContext applicationContext = SpringApplication.run(MemberApplication.class, args);
  14. DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
  15. discoveryClient.getServices().forEach(action -> {
  16. System.out.println(action);
  17. });
  18. }
  19. }
复制代码

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

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

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

  1. java -jar member-server-0.0.1-SNAPSHOT.jar --server.port=8800
复制代码

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

总结

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



回复

使用道具 举报