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

2,249 阅读5分钟

背景

最近在调研研究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> T getStatus(R registration);

}

AbstractAutoServiceRegistration

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

public abstract class AbstractAutoServiceRegistration<R extends Registration>
		implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<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(false, true);
		}

	}


}

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

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

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

整体流程

在这里插入图片描述

源码分析

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:供三方继承完成服务自动注册功能

关于我

觉得文章不错请扫码关注我吧

weichat