欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

服务的注册与发现如何完成?
(last modified Nov 14, 2023, 1:52 AM )
by
侧边栏壁纸
  • 累计撰写 176 篇文章
  • 累计创建 61 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

服务的注册与发现如何完成?

橙序员
2023-11-05 / 0 评论 / 0 点赞 / 218 阅读 / 2,733 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

本文介绍了Spring Cloud服务的注册和发现流程,首先通过spring-cloud-commons包定义了注册和发现的规范流程,然后通过各个注册中心和管理中心进行具体的服务注册和实现。服务注册和发现包括两个步骤,一是通过EnableDiscoveryClient注解开启自动注册服务,二是在容器启动时向注册中心的注册表进行注册。其中,HostInfoEnvironmentPostProcessor获取了服务实例所属主机信息的属性,并将其封装到springCloudClientHostInfo属性源中。CompatibilityNotMetFailureAnalyzer用于解析当配置不兼容报错时,返回适当的提示信息。在Spring的自动配置中,引入了客户端状态配置和四种客户端的配置,包括通用客户端、响应式客户端、复合客户端和简单客户端。

spring-cloud服务进行服务注册发现是首先通过spring-cloud-commons包定义了一套注册和发现的规范流程,然后再有各个注册中心和管理中心进行具体的服务注册和实现。服务发现注册一共有两件事情,一个就是通过EnableDiscoveryClient注解开启服务自动注册,第二就是在容器启动时就向注册中心的注册表进行注册。

spring-cloud-commons配置说明

image-20231107000640052

其spring.factories文件如下:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.configuration.CompatibilityNotMetFailureAnalyzer

HostInfoEnvironmentPostProcessor获取了服务实例所属主机信息的属性,并将其封装到springCloudClientHostInfo属性源中。

CompatibilityNotMetFailureAnalyzer用于解析当配置不兼容报错时,返回适当的提示信息。

其spring文件夹下,自动配置引入的配置bean有:

##客户端状态配置:通用客户端、响应式客户端
org.springframework.cloud.client.CommonsClientAutoConfiguration
org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration
##四种客户端:复合客户端、响应式复合客户端、简单客户端、简单响应式客户端
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration
org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration
##客户端共享资源配置
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration
##客户端复杂均衡配置
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration
org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration
##服务注册表配置
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration
##网络IO工具类
org.springframework.cloud.commons.util.UtilAutoConfiguration
##版本验证
org.springframework.cloud.configuration.CompatibilityVerifierAutoConfiguration
##服务的发现与注册
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
##OAuth2访问支持
org.springframework.cloud.commons.security.ResourceServerTokenRelayAutoConfiguration
##配置属性解析绑定
org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration

是否开启服务发现

EnableDiscoveryClient注解

其在注解中导入了EnableDiscoveryClientImportSelector来完成服务注册与发现的过程

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

    /**
     * 如果为 true,ServiceRegistry 将自动注册本地服务器。
     * @return - {@code true} 如果想开启自动注册则返回true.
     */
    boolean autoRegister() default true;

}

导入服务发现配置-EnableDiscoveryClientImportSelector

实现了SpringFactoryImportSelector,它的作用是读取spring.factories文件,并根据EnableDiscoveryClient注解中autoRegister属性值,如果属性值为true,则向其中添加org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration属性;如果属性值为false,则向环境变量中添加spring.cloud.service-registry.auto-registration.enabled属性,值为false,并且添加springCloudDiscoveryClient属性源。

问:SpringFactoryImportSelector是什么作用?

答:SpringFactoryImportSelector是Spring框架中的一个类,它的作用是通过读取spring.factories文件中的配置信息,选择并导入需要加载的Spring配置类。

在Spring Boot中,SpringFactoryImportSelector通常用于自动装配(Auto-configuration)的机制。当使用某个Spring Boot Starter时,该Starter会在其META-INF/spring.factories文件中定义自己的自动配置类。SpringFactoryImportSelector会根据这些配置信息,选择并导入需要加载的自动配置类。

具体来说,SpringFactoryImportSelector的作用如下:

  1. 读取spring.factories文件:SpringFactoryImportSelector会在类路径下查找并读取spring.factories文件,该文件中记录了各个Spring组件的配置信息。
  2. 选择需要导入的配置类:根据spring.factories文件中的配置信息,SpringFactoryImportSelector会根据一定的规则选择需要导入的配置类,通常是自动配置类。
  3. 导入配置类:根据选择的配置类,SpringFactoryImportSelector会将它们以ImportSelector的形式(也可以是其他适配器形式)导入到Spring容器中。

通过这种机制,Spring Boot能够根据应用程序的依赖关系和配置文件的设定,自动加载相应的自动配置类,从而实现自动装配和快速配置应用程序的功能。

总而言之,SpringFactoryImportSelector是Spring框架中用于读取spring.factories文件,并根据配置信息选择和导入需要加载的Spring配置类的类。

源码注释:

@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));
	   //获取EnableDiscoveryClient注解中的autoRegister属性
       boolean autoRegister = attributes.getBoolean("autoRegister");
	   //如果autoRegister属性开启,则导入AutoServiceRegistrationConfiguration
       if (autoRegister) {
          List<String> importsList = new ArrayList<>(Arrays.asList(imports));
          importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
          imports = importsList.toArray(new String[0]);
       }
       //如果autoRegister属性关闭,则将spring.cloud.service-registry.auto-registration设置为false
       //并创建springCloudDiscoveryClient属性集合,添加到spring容器配置集合中
       else {
          Environment env = getEnvironment();
          if (env instanceof ConfigurableEnvironment configEnv) {
             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;
    }
}

服务发现自动配置-AutoServiceRegistrationAutoConfiguration

实现了InitializingBean,即在容器启动时就注册该配置类。该配置类声明了两个类,一个适用于服务注册操作的AutoServiceRegistration,一个是包含服务注册属性的AutoServiceRegistrationProperties

源码注释:

@Configuration(proxyBeanMethods = false)
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration implements InitializingBean {

    //服务注册
    @Autowired(required = false)
    private AutoServiceRegistration autoServiceRegistration;

    //服务注册配置
    @Autowired
    private AutoServiceRegistrationProperties properties;

    //如果服务注册的属性为空且为快速失败则抛异常中断服务启动
    @Override
    public void afterPropertiesSet() {
       if (this.autoServiceRegistration == null && this.properties.isFailFast()) {
          throw new IllegalStateException(
                "Auto Service Registration has " + "been requested, but there is no AutoServiceRegistration bean");
       }
    }

}

服务注册属性配置AutoServiceRegistrationProperties

本身是一个配置属性类,其包含了服务发现注册的几个开关:

  1. 是否自动注册,默认是true
  2. 是否向管理服务注册,默认是true
  3. 没有配置服务注册时,是否可以启动失败,默认是false

分别对应以下三个配置属性:

###是否自动注册
spring.cloud.service-registry.auto-registration.enabled=true
###是否向管理服务注册
spring.cloud.service-registry.auto-registration.register-management=true
###没有配置服务注册时,是否可以启动失败
spring.cloud.service-registry.auto-registration.fail-fast=true

服务发现具体实现AutoServiceRegistration

image-20231105224151314

在服务发现包中,由AutoServiceRegistration的定义的子类AbstractAutoServiceRegistration进行了默认实现,从它的定义中:

public abstract class AbstractAutoServiceRegistration<R extends Registration>
       implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> 

我们可以发现,其实现了ApplicationContextAwareApplicationListener,那么他又实现了容器获取和监听容器事件的操作。

spring容器获取:

//获取spring容器和spring容器的环境
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
    this.environment = this.context.getEnvironment();
}

监听容器事件,完成服务注册:

//监听消息
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(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
    // 线程安全判断:仅当 nonSecurePort 大于 0 且由于下面的 containerPortInitializer 而尚未运行时才初始化
    if (!this.running.get()) {
       //发布实例预注册事件-InstancePreRegisteredEvent
       this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
       //执行注册表启动前的处理方法
       registrationLifecycles.forEach(
             registrationLifecycle -> registrationLifecycle.postProcessBeforeStartRegister(getRegistration()));
       //向注册表注册。注册通常包含有关实例的信息,例如其主机名和端口。
       register();
       //执行注册表启动后的处理方法
       this.registrationLifecycles.forEach(
             registrationLifecycle -> registrationLifecycle.postProcessAfterStartRegister(getRegistration()));
       //是否向管理服务注册
       if (shouldRegisterManagement()) {
          //执行管理注册表启动前的处理方法
          this.registrationManagementLifecycles
                .forEach(registrationManagementLifecycle -> registrationManagementLifecycle
                      .postProcessBeforeStartRegisterManagement(getManagementRegistration()));
          //向管理服务注册
          this.registerManagement();
          //执行管理注册表启动后的处理方法
          registrationManagementLifecycles
                .forEach(registrationManagementLifecycle -> registrationManagementLifecycle
                      .postProcessAfterStartRegisterManagement(getManagementRegistration()));

       }
       //发布实例注册完成事件-InstanceRegisteredEvent
       this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
       this.running.compareAndSet(false, true);
    }

}

在监听到webserver初始化完成的事件时,我们会判断他是否是management应用。如果不是management应用(actuator或者admin应用不会注册到注册中心中,所以无法在注册中心看到这类应用),那么将更新服务的端口以及启动服务注册流程。

Q:ConfigurableWebServerApplicationContext的serverNameSpace为management时,该spring应用是哪一种web服务器部署的应用?

A:当ConfigurableWebServerApplicationContextserverNamespace属性值为management时,这表示Spring应用是以管理服务器模式部署的应用(Management Server Deployment)。

在Spring Boot中,可以通过application.propertiesapplication.yaml配置文件的server.namespace属性来设置serverNamespace。如果将该属性值设为management,则Spring Boot应用将以管理服务器模式运行。

管理服务器模式是指Spring Boot应用作为一个独立的管理服务器来运行,用于管理和监控其他独立的应用。通常,在微服务架构中,使用一个管理服务器来运行一些共享的管理和监控组件,例如Spring Boot AdminActuator。这些组件可以收集、监控和管理多个独立的应用的运行状况和性能指标。

在管理服务器模式下,Spring应用仅用于管理和监控其他应用,而不承担实际的业务逻辑。它可以提供一种集中管理和监控的解决方案,简化了对微服务体系中多个应用的管理和监控工作。

所以,当ConfigurableWebServerApplicationContextserverNamespace属性值为management时,这表示Spring应用以管理服务器模式部署,用于管理和监控其他独立应用。

服务注册表-ServiceRegistry

通过注册表的方式,负责服务实例的注册和注销:

public interface ServiceRegistry<R extends Registration> {

    /**
     * 向注册表注册服务实例。注册通常包含有关实例的信息,例如其主机名和端口。
     * @param registration 要注册的元数据
     */
    void register(R registration);

    /**
     * 将注册表的服务实例注销。
     * @param registration 要注册的元数据
     */
    void deregister(R registration);

    /**
     * 关闭注册表
     */
    void close();

    /**
     * 获取已注册服务的实例状态
     */
    void setStatus(R registration, String status);

    /**
     * 获取已注册服务的实例状态
     */
    <T> T getStatus(R registration);

}

服务实例信息-Registration

其继承了ServiceInstance接口,包括了已注册的实例ID、服务ID、主机、端口、服务uri、实例关联的元数据和scheme信息。

image-20231105223437895

小结

本文只是介绍了spring-cloud-commons如何定义了一套标准的服务注册和发现规范,下篇文章将介绍eureka的服务注册和发现(本文图片中出现的EurekaAutoServiceRegistration)是怎么样的一个实现流程.

0

评论区