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

https://blog.csdn.net/zhaodongchao1992/article/details/107691006

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

如下是Spring Cloud官方给的微服务架构图:
在这里插入图片描述

今天就要研究service registry模块。大致流程如下:
在这里插入图片描述

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

  • 服务注册
    先来看服务的注册。ServiceRegistry的结构如下:
    在这里插入图片描述
    源码如下:
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 <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);

}

可以看到这个接口类提供了注册的几个基本行为。而这些操作都是围绕Registration 来进行的。
在这里插入图片描述
可以看到Registration里面没有任何方法。只是扩展了一下ServiceInstance。Registration接口是一个典型的标记接口。实际接口方法都在ServiceInstance中。

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

Eureka中Registration的实现是EurekaRegistration。结构如下:
在这里插入图片描述
可以看到。这里使用到了建造者设计模式来构建EurekaRegistration实例。而构建它需要几个必须参数:
① CloudEurekaInstanceConfig
Eureka实例配置,要注册的服务的信息配置。唯一子类是EurekaInstanceConfigBean,专门封装spring boot配置文件中eureka.instance.*相关的配置信息。
在这里插入图片描述

② EurekaClient
EurekaClient客户端。用于与Eureka进行数据交互的工具类。
在这里插入图片描述
可以看到,真正实现注册逻辑在com.netflix.discovery.DiscoveryClient中。而将服务信息注册到Eureka服务器的工具类就是EurekaHttpClient , 它实现了与Eureka进行数据操作的所有接口。

③ HealthCheckHandler 健康检查处理器
一个Eureka运行状况检查器,将应用程序状态映射到InstanceInfo.InstanceStatus,将传播到Eureka注册表
在这里插入图片描述

而正在聚合这些组件,来在Eruka上进行注册的核心类是EurekaServiceRegistry ,这是Eureka对Spring Cloud服务注册与发现标准接口ServiceRegistry的实现。
在这里插入图片描述
下面来看源码:

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服务注册中心。

在这里插入图片描述
可以看到,使用了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实例。源码如下:
在这里插入图片描述

可以看到。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为例,它的实现如下:

在这里插入图片描述
在这里,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实例中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud是一个基于Spring Boot的开发工具集,提供了一系列用于构建分布式系统的解决方案。其中,Feign和Ribbon是Spring Cloud中常用的两个组件。 Feign是一个声明式的Web服务客户端,用于简化使用Spring Cloud服务之间进行通信的过程。它通过支持注解方式来定义和使用服务接口,底层使用的是基于反射的动态代理技术,将接口和实际调用的服务进行映射。Feign通过集成Ribbon来实现负载均衡的功能。 Ribbon是一个负载均衡的组件,它根据一系列的负载均衡策略,从多个服务实例中选择一个要调用的实例。Ribbon通过监听Eureka注册中心上已注册服务列表,并通过默认的轮询算法选择一个实例。在Feign中,Ribbon用于根据服务接口定义的URL和方法,选择一个具体的服务进行调用。 Feign和Ribbon的原理可以简单总结如下:首先,Feign通过使用@EnableFeignClients注解开启Feign功能,扫描包中带有@FeignClient注解的接口定义。接着,Feign将这些接口定义转化为动态代理对象,在调用接口方法时,实际上是通过动态代理对象进行了解析和转发,最终会调用到具体的服务实例上。此时,Ribbon会根据一定的策略从多个服务实例中选择一个实例进行调用,并返回调用结果。整个调用过程是通过HTTP协议进行通信的。 总之,Spring Cloud中的Feign和Ribbon组件能够实现微服务之间的通信和负载均衡功能。Feign简化了基于HTTP的服务接口定义和调用的过程,而Ribbon则负责根据一定的负载均衡策略选择合适的服务实例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值