-
背景
-
源码版本
-
核心抽象接口
-
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> 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
:供三方继承完成服务自动注册功能
原文始发于微信公众号(小奏技术):从源码底层聊聊Spring Cloud是如何一统服务注册、发现编程模型
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/30184.html