Spring Cloud之服务注册和发现的原理解析

在Java微服务越来越火的今天。几乎什么公司都在搞微服务。而使用的比较多的就是Spring Cloud技术栈。今天就来研究一下Spring Cloud中服务注册与发现的基本原理。

如下是Spring Cloud官方给的微服务架构图:
Spring Cloud之服务注册和发现的原理解析_第1张图片

今天就要研究service registry模块。大致流程如下:
Spring Cloud之服务注册和发现的原理解析_第2张图片

对于服务注册与发现Spring Cloud官方也给出了标准的接口DiscoveryClient(服务发现) ,ServiceRegistry(服务注册),要是实现服务注册与发现,第三方必须实现这两个接口。

  • 服务注册
    先来看服务的注册。ServiceRegistry的结构如下:
    Spring Cloud之服务注册和发现的原理解析_第3张图片
    源码如下:
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.
	 */
	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  The type of the status.
	 * @return The status of the registration.
	 * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
	 */
	<T> T getStatus(R registration);

}

可以看到这个接口类提供了注册的几个基本行为。而这些操作都是围绕Registration 来进行的。
Spring Cloud之服务注册和发现的原理解析_第4张图片
可以看到Registration里面没有任何方法。只是扩展了一下ServiceInstance。Registration接口是一个典型的标记接口。实际接口方法都在ServiceInstance中。

因此对于Registration接口,不同的注册中心也应该有不同的实现。下面就以传统的Eureka注册中心为例。来看实现服务注册和发现的详细过程。

Eureka中Registration的实现是EurekaRegistration。结构如下:
Spring Cloud之服务注册和发现的原理解析_第5张图片
可以看到。这里使用到了建造者设计模式来构建EurekaRegistration实例。而构建它需要几个必须参数:
CloudEurekaInstanceConfig
Eureka实例配置,要注册的服务的信息配置。唯一子类是EurekaInstanceConfigBean,专门封装spring boot配置文件中eureka.instance.*相关的配置信息。
Spring Cloud之服务注册和发现的原理解析_第6张图片

EurekaClient
EurekaClient客户端。用于与Eureka进行数据交互的工具类。
Spring Cloud之服务注册和发现的原理解析_第7张图片
可以看到,真正实现注册逻辑在com.netflix.discovery.DiscoveryClient中。而将服务信息注册到Eureka服务器的工具类就是EurekaHttpClient , 它实现了与Eureka进行数据操作的所有接口。

HealthCheckHandler 健康检查处理器
一个Eureka运行状况检查器,将应用程序状态映射到InstanceInfo.InstanceStatus,将传播到Eureka注册表
Spring Cloud之服务注册和发现的原理解析_第8张图片

而正在聚合这些组件,来在Eruka上进行注册的核心类是EurekaServiceRegistry ,这是Eureka对Spring Cloud服务注册与发现标准接口ServiceRegistry的实现。
Spring Cloud之服务注册和发现的原理解析_第9张图片
下面来看源码:

public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> {
     
	private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);
    //服务注册逻辑
	@Override
	public void register(EurekaRegistration reg) {
     
	    //初始化CloudEurekaClient实例
		maybeInitializeClient(reg);

		if (log.isInfoEnabled()) {
     
			log.info("Registering application "
					+ reg.getApplicationInfoManager().getInfo().getAppName()
					+ " with eureka with status "
					+ reg.getInstanceConfig().getInitialStatus());
		}
        //将当前注册的服务的状态设置为初始化状态
		reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
        //注册健康检查器
		reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg
				.getEurekaClient().registerHealthCheck(healthCheckHandler));
	}

	private void maybeInitializeClient(EurekaRegistration reg) {
     
		// force initialization of possibly scoped proxies
		reg.getApplicationInfoManager().getInfo();
		reg.getEurekaClient().getApplications();
	}
    //取消注册
	@Override
	public void deregister(EurekaRegistration reg) {
     
	    //如果注册信息本地管理器中存在。就将状态设置为DOWN(下线)
		if (reg.getApplicationInfoManager().getInfo() != null) {
     
			if (log.isInfoEnabled()) {
     
				log.info("Unregistering application "
						+ reg.getApplicationInfoManager().getInfo().getAppName()
						+ " with eureka with status DOWN");
			}
            //将服务状态设置为DOWN后,会触发相应的监听器。将状态跟新到Eureka服务器中
			reg.getApplicationInfoManager()
					.setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);

			// shutdown of eureka client should happen with EurekaRegistration.close()
			// auto registration will create a bean which will be properly disposed
			// manual registrations will need to call close()
		}
	}

	@Override
	public void setStatus(EurekaRegistration registration, String status) {
     
		InstanceInfo info = registration.getApplicationInfoManager().getInfo();

		// TODO: howto deal with delete properly?
		if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {
     
			registration.getEurekaClient().cancelOverrideStatus(info);
			return;
		}

		// TODO: howto deal with status types across discovery systems?
		InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status);
		registration.getEurekaClient().setStatus(newStatus, info);
	}
    
    //从Eureka服务注册中心查询当前服务的详细信息
	@Override
	public Object getStatus(EurekaRegistration registration) {
     
	    //获取app名称
		String appname = registration.getApplicationInfoManager().getInfo().getAppName();
		//获取应用ID
		String instanceId = registration.getApplicationInfoManager().getInfo().getId();
		//从Eureka服务器查询服务实例的详细信息
		InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname,instanceId);

		HashMap<String, Object> status = new HashMap<>();
		if (info != null) {
     
			status.put("status", info.getStatus().toString());
			status.put("overriddenStatus", info.getOverriddenStatus().toString());
		}
		else {
     
			status.put("status", UNKNOWN.toString());
		}

		return status;
	}

	public void close() {
     
	}

}

以上把服务注册的实现逻辑关系理清楚了。现在的问题是服务启动的时候怎么去进行注册的?

懂Spring Boot应用事件的小伙伴肯定会想到。这个整合与Spring Boot事件有关。毕竟一般框架与spring整合都会使用到事件的原理。Eureka也是一样。在触发RefreshScopeRefreshedEvent事件后。会将应用的信息注册到Eureka服务注册中心。

Spring Cloud之服务注册和发现的原理解析_第10张图片
可以看到,使用了ApplicationListener的方式来进行服务的注册。

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RefreshScopeRefreshedEvent.class)
	protected static class EurekaClientConfigurationRefresher
			implements ApplicationListener<RefreshScopeRefreshedEvent> {
     

		@Autowired(required = false)
		private EurekaClient eurekaClient;

		@Autowired(required = false)
		private EurekaAutoServiceRegistration autoRegistration;

		public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
     
			// This will force the creation of the EurkaClient bean if not already created
			// to make sure the client will be reregistered after a refresh event
			if (eurekaClient != null) {
     
				eurekaClient.getApplications();
			}
			if (autoRegistration != null) {
     
				// register in case meta data changed
				this.autoRegistration.stop();
				this.autoRegistration.start();
			}
		}

	}

注册的调用客户端是EurekaAutoServiceRegistration实例。源码如下:
Spring Cloud之服务注册和发现的原理解析_第11张图片

可以看到。EurekaAutoServiceRegistration本身也是一个监听器。实现的是SmartApplicationListener。SmartApplicationListener实现了ApplicationListener。能提供更精确的事件触发控制。

因此可以通过两种事件触发服务注册。因此这里执行注册之前会先stop再start .防止重复注册。

EurekaAutoServiceRegistration#start()逻辑如下:

	private AtomicBoolean running = new AtomicBoolean(false);

	private int order = 0;

	private AtomicInteger port = new AtomicInteger(0);

	private ApplicationContext context;

	private EurekaServiceRegistry serviceRegistry;

	private EurekaRegistration registration;
	
    @Override
	public void start() {
     
		// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
		if (this.port.get() != 0) {
     
			if (this.registration.getNonSecurePort() == 0) {
     
				this.registration.setNonSecurePort(this.port.get());
			}

			if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
     
				this.registration.setSecurePort(this.port.get());
			}
		}

		// only initialize if nonSecurePort is greater than 0 and it isn't already running
		// because of containerPortInitializer below
		if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
     
            //注册
			this.serviceRegistry.register(this.registration);
            //发布InstanceRegisteredEvent事件
			this.context.publishEvent(new InstanceRegisteredEvent<>(this,this.registration.getInstanceConfig()));
			this.running.set(true);
		}
	}
  • 服务发现

开头说到了,实现Spring Cloud的服务发现。需要自定义实现org.springframework.cloud.client.discovery.DiscoveryClient接口。

还是以Eureka为例,它的实现如下:

Spring Cloud之服务注册和发现的原理解析_第12张图片
在这里,CompositeDiscoveryClient是Spring Cloud自己的实现。使用了委托设计模式。用来管理其他的DiscoveryClient实现。使用服务发现时。也是调用的这个类。

官方解释: A {@link DiscoveryClient} that is composed of other discovery clients and delegates calls to each of them in order.

当Eureka实现了DiscoveryClient接口后。就会按照order的顺序。去调用对应的实现。Eureka获取服务的具体实现也是在EurekaClient实例中。

你可能感兴趣的:(Spring,Boot,Spring5源码解读,设计模式,java,spring)