从源码底层聊聊Spring Cloud是如何一统服务注册、发现编程模型


  • 背景

  • 源码版本

  • 核心抽象接口

    • DiscoveryClient

    • EnableDiscoveryClient

    • ReactiveDiscoveryClient

    • ServiceInstance

    • Registration

    • ServiceRegistry

    • AbstractAutoServiceRegistration

  • 从Nacos源码来看看一个服务自动注册的过程

    • 整体流程

    • 源码分析

  • 总结


背景

最近在调研研究Spring Cloud 注册中心的一些组件,其中就调研了Nacos、ZooKeeper、Eureka、Kubernetes等主流的注册中心,然后发现我们在替换任何一款注册中心的时候,客户端的使用都比较简单不变,大致都是使用Spring Cloud 提供的注解@EnableDiscoveryClient即可开启服务发现功能。我们在替换服务注册组件的时候,不需要关注底层的实现,归根结底还是Spring Cloud 提供的上层抽象的服务注册发现编程模型,让我们更换注册中心只需要修改对应的Maven依赖和注册中心配置信息(比如注册中心、namespace、group)。那么Spring Cloud 是如何做到的呢?然后从源码角度一层一层揭开Spring Cloud服务注册编程模型,学习它的抽象能力

源码版本

Spring Cloud 统一服务注册和发现编程源码主要在spring-cloud-commons 模块Github地址

本次分析的源码版本:

3.0.0-SNAPSHOT

核心抽象接口

DiscoveryClient

DiscoveryClient 接口主要是定义了服务发现的一些方法

public interface DiscoveryClient extends Ordered {

 /**
  * Default order of the discovery client.
  * 默认优先级,多个 DiscoveryClient 将按这个优先级排序
  */

 int DEFAULT_ORDER = 0;

 /**
  * A human-readable description of the implementation, used in HealthIndicator.
  * 具体服务发现组件,在 HealthIndicator中被用到
  * @return The description.
  */

 String description();

 /**
  * Gets all ServiceInstances associated with a particular serviceId.
  * 根据 serviceId 获取所有服务实例
  * @param serviceId The serviceId to query.
  * @return A List of ServiceInstance.
  */

 List<ServiceInstance> getInstances(String serviceId);

 /**
  * @return All known service IDs.
  * 返回注册中心所有服务名
  */

 List<String> getServices();

 /**
  * 看注释是校验客户端是否存活
  * Can be used to verify the client is valid and able to make calls.
  * <p>
  * A successful invocation with no exception thrown implies the client is able to make
  * calls.
  * <p>
  * The default implementation simply calls {@link #getServices()} - client
  * implementations can override with a lighter weight operation if they choose to.
  */

 default void probe() {
  getServices();
 }

 /**
  * Default implementation for getting order of discovery clients.
  * @return order
  */

 @Override
 default int getOrder() {
  return DEFAULT_ORDER;
 }

}

EnableDiscoveryClient

用于开启服务发现的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient 
{

 /**
  * If true, the ServiceRegistry will automatically register the local server.
  * @return - {@code true} if you want to automatically register.
  */

 boolean autoRegister() default true;

}

ReactiveDiscoveryClient

ReactiveDiscoveryClient 接口中定义的方法和DiscoveryClient 中定义的方法完全一样,不同的是将返回值为List改为Flux,主要用于支持响应式服务发现,在Spring WebFlux就会使用该接口的实现

public interface ReactiveDiscoveryClient extends Ordered {

 int DEFAULT_ORDER = 0;

 String description();

 Flux<ServiceInstance> getInstances(String serviceId);


 Flux<String> getServices();

 default void probe() {
  getServices();
 }

 @Override
 default int getOrder() {
  return DEFAULT_ORDER;
 }

}

ServiceInstance

代表一个服务实例,主要用户获取服务的一些基本信息

public interface ServiceInstance {

 /**
  * @return The unique instance ID as registered.
  * 服务实例id 默认返回null
  */

 default String getInstanceId() {
  return null;
 }

 /**
  * @return The service ID as registered.
  * 注册服务ID
  */

 String getServiceId();

 /**
  * @return The hostname of the registered service instance.
  * 获取服务的 hostName
  */

 String getHost();

 /**
  * @return The port of the registered service instance.
  * 获取端口
  */

 int getPort();

 /**
  * @return Whether the port of the registered service instance uses HTTPS.
  * 是否使用https
  */

 boolean isSecure();

 /**
  * @return The service URI address.
  * 获取服务实例 URI 地址
  */

 URI getUri();

 /**
  * 获取服务元数据
  * @return The key / value pair metadata associated with the service instance.
  */

 Map<String, String> getMetadata();

 /**
  * @return The scheme of the service instance.
  */

 default String getScheme() {
  return null;
 }

}

Registration

空接口,只是继承了ServiceInstance,目前好像没什么用

public interface Registration extends ServiceInstance {

}

ServiceRegistry

主要用于服务信息注册(registier) 和 注销(deregister) 大多数方法参数都是需要实现上面定义的Registration接口方法,实际就是需要实现ServiceInstance定义的方法,因为Registration接口是一个空接口继承了ServiceInstance接口

public interface ServiceRegistry<R extends Registration{

 /**
  * 服务注册
  * Registers the registration. A registration typically has information about an
  * instance, such as its hostname and port.
  * @param registration registration meta data
  */

 void register(R registration);

 /**
  * 服务销毁
  * Deregisters the registration.
  * @param registration registration meta data
  */

 void deregister(R registration);

 /**
  * Closes the ServiceRegistry. This is a lifecycle method.
  * 关闭 ServiceRegistry
  */

 void close();

 /**
  * 设置服务状态
  * Sets the status of the registration. The status values are determined by the
  * individual implementations.
  * @param registration The registration to update.
  * @param status The status to set.
  * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
  */

 void setStatus(R registration, String status);

 /**
  * 获取服务状态
  * Gets the status of a particular registration.
  * @param registration The registration to query.
  * @param <T> The type of the status.
  * @return The status of the registration.
  * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
  */

 <T> getStatus(R registration);

}

AbstractAutoServiceRegistration

服务自动注册的抽象类,大多数服务自动注册的SDK都只需要继承该抽象类即可,该抽象类方法和属性较多,我们分析几个核心的方法

public abstract class AbstractAutoServiceRegistration<R extends Registration>
  implements AutoServiceRegistrationApplicationContextAwareApplicationListener<WebServerInitializedEvent
{

 private final ServiceRegistry<R> serviceRegistry;

 @Override
 @SuppressWarnings("deprecation")
 public void onApplicationEvent(WebServerInitializedEvent event) {
  bind(event);
 }

 @Deprecated
 public void bind(WebServerInitializedEvent event) {
  ApplicationContext context = event.getApplicationContext();
  if (context instanceof ConfigurableWebServerApplicationContext) {
   if ("management".equals(((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
    return;
   }
  }
  this.port.compareAndSet(0, event.getWebServer().getPort());
  this.start();
 }
 
 // 服务注册方法
 public void start() {
  if (!isEnabled()) {
   if (logger.isDebugEnabled()) {
    logger.debug("Discovery Lifecycle disabled. Not starting");
   }
   return;
  }

  // only initialize if nonSecurePort is greater than 0 and it isn't already running
  // because of containerPortInitializer below
  if (!this.running.get()) {
   this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
   register();
   if (shouldRegisterManagement()) {
    registerManagement();
   }
   this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
   this.running.compareAndSet(falsetrue);
  }

 }


}

// 服务销毁方法
 @PreDestroy
 public void destroy() {
  stop();
 }

主要是定义了服务注册和消费的两个方法,这里用到了一个模板设计模式,然后通过监听ApplicationListener<WebServerInitializedEvent> 容器初始化事件调用服务注册方法

从Nacos源码来看看一个服务自动注册的过程

整体流程

从源码底层聊聊Spring Cloud是如何一统服务注册、发现编程模型
在这里插入图片描述

源码分析

Nacos自动服务注册源码入口从NacosDiscoveryAutoConfiguration开始

NacosDiscoveryAutoConfiguration

@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnClass(name = "org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent")
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,
  NacosDiscoveryClientAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration 
{

 @Bean
 public NacosServiceRegistry nacosServiceRegistry() {
  return new NacosServiceRegistry();
 }

 @Bean
 @ConditionalOnBean(AutoServiceRegistrationProperties.class)
 public NacosRegistration nacosRegistration() 
{
  return new NacosRegistration();
 }

 @Bean
 @ConditionalOnBean(AutoServiceRegistrationProperties.class)
 public NacosAutoServiceRegistration nacosAutoServiceRegistration(
   NacosServiceRegistry registry,
   AutoServiceRegistrationProperties autoServiceRegistrationProperties,
   NacosRegistration registration
{
  return new NacosAutoServiceRegistration(registry,
    autoServiceRegistrationProperties, registration);
 }
}

可以看到有注解@ConditionalOnNacosDiscoveryEnabled 代表我们需要使用注解@EnableDiscoveryClient让这个配置类生效

然后配置了一个beanNacosAutoServiceRegistration

NacosAutoServiceRegistration

public class NacosAutoServiceRegistration
  extends AbstractAutoServiceRegistration<NacosRegistration
{
 private static final Logger LOGGER = LoggerFactory
   .getLogger(NacosAutoServiceRegistration.class);

 @Autowired
 private NacosRegistration registration;

 public NacosAutoServiceRegistration(
   ServiceRegistry<NacosRegistration> serviceRegistry,
   AutoServiceRegistrationProperties autoServiceRegistrationProperties,
   NacosRegistration registration)
 
{
  super(serviceRegistry, autoServiceRegistrationProperties);
  this.registration = registration;
 }

 @Deprecated
 public void setPort(int port) {
  getPort().set(port);
 }

 @Override
 protected NacosRegistration getRegistration() {
  if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
   this.registration.setPort(this.getPort().get());
  }
  Assert.isTrue(this.registration.getPort() > 0"service.port has not been set");
  return this.registration;
 }

 @Override
 protected NacosRegistration getManagementRegistration() {
  return null;
 }

 @Override
 protected void register() {
  if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
   LOGGER.debug("Registration disabled.");
   return;
  }
  if (this.registration.getPort() < 0) {
   this.registration.setPort(getPort().get());
  }
  super.register();
 }

 @Override
 protected void registerManagement() {
  if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
   return;
  }
  super.registerManagement();

 }

 @Override
 protected Object getConfiguration() {
  return this.registration.getNacosDiscoveryProperties();
 }

 @Override
 protected boolean isEnabled() {
  return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
 }

 @Override
 @SuppressWarnings("deprecation")
 protected String getAppName() {
  String appName = registration.getNacosDiscoveryProperties().getService();
  return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
 }

}

可以看到继承了我们上面分析的AbstractAutoServiceRegistration抽象类,然后通过监听WebServerInitializedEvent事件完成服务自动注册

总结

我们从Spring Cloud的服务注册编程模型到Nacos是如何依赖这套编程模型作扩展自动完成服务注册的。核心的几个类

  • DiscoveryClient:服务发现方法定义
  • ReactiveDiscoveryClient: 基于响应式编程服务发现方法定义
  • EnableDiscoveryClient: 开启服务发现注解
  • ServiceInstance: 单个服务的一些方法定义
  • ServiceRegistry: 服务注册与销毁方法定义
  • AbstractAutoServiceRegistration:供三方继承完成服务自动注册功能


原文始发于微信公众号(小奏技术):从源码底层聊聊Spring Cloud是如何一统服务注册、发现编程模型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/30184.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!