找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2105

积分

0

好友

277

主题
发表于 3 天前 | 查看: 14| 回复: 0

前几天有朋友问到,如果想让自己实现的注册中心与SpringCloud集成,需要遵循哪些接口和规范。这不正好是一个梳理主流微服务组件如何协同工作的好机会吗?本文将以Nacos为例,带你剖析SpringCloud环境下,服务注册发现、负载均衡及远程调用的完整链路。

关于服务注册与SpringCloud负载均衡的提问对话

了解这些组件的协作原理,自然就能明白一个注册中心需要实现哪些关键接口了。当然,不仅仅是Nacos,其他如Eureka、Zookeeper等注册中心的集成思路也大同小异。

一个开心的简笔画表情

服务注册中心:Nacos

首先,我们从服务注册中心开始。Nacos是什么?官方文档给出了明确的定义。

Nacos官方介绍文字

简单来说,Nacos是一个集服务注册发现和配置管理于一体的平台。

在Nacos的架构中,分为客户端(Client)和服务端(Server)。服务端独立部署,用于存储所有服务实例的元数据;而客户端则是集成在业务应用中的SDK,用于与服务端通信。

Nacos客户端与服务端交互图

  • 服务端:独立部署,核心作用是保存服务实例数据。
  • 客户端:与业务应用集成,作为SDK与服务端进行通信(支持多语言)。

当需要向Nacos注册服务或查询服务实例时,只需要使用其客户端SDK即可,示例如下:

引入依赖

<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>1.4.4</version>
</dependency>

示例代码

Properties properties = new Properties();
properties.setProperty("serverAddr", "localhost");
properties.setProperty("namespace", "8848");

NamingService naming = NamingFactory.createNamingService(properties);

//服务注册,注册一个order服务,order服务的ip是192.168.2.100,端口8080
naming.registerInstance("order", "192.168.2.100", 8080);

//服务发现,获取所有的order服务实例
List<Instance> instanceList = naming.selectInstances("order", true);

服务实例注册到Nacos服务端后,会在服务端内部的一个集合中被维护,这个集合在微服务领域有一个广为人知的名字——服务注册表

Nacos服务注册表数据结构示例代码

服务如何实现自动注册?

使用过SpringCloud的开发者都知道,服务在应用启动时会自动注册到注册中心,无需手动编写上述注册代码。这背后的机制是如何实现的呢?

服务自动注册的“三板斧”

SpringCloud为服务自动注册定义了一套机制或规范,核心是三个关键接口。任何注册中心只要实现了这些接口,就能无缝接入SpringCloud的自动注册流程。

第一板斧:服务实例数据封装——Registration

Registration 是SpringCloud提供的一个标记接口,它继承了 ServiceInstance 接口。

Registration接口定义

ServiceInstance接口定义

ServiceInstance 的定义可以看出,它是对一个服务实例(IP、端口等)信息的抽象封装。因此,Registration 就是对当前应用自身服务实例数据的封装。

Nacos要整合SpringCloud,自然需要实现这个接口。

NacosRegistration类定义

这样,当前待注册服务的元数据就被封装好了。

第二板斧:服务注册执行器——ServiceRegistry

ServiceRegistry 也是一个接口,其泛型就是上面提到的 Registration

ServiceRegistry接口定义

这个接口的作用非常明确:通过 register 方法,将封装好的 Registration(当前服务实例数据)注册到具体的注册中心。

Nacos也实现了这个接口。

NacosServiceRegistry类定义

其核心的 register 方法实现,逻辑与我们之前演示的手动注册代码几乎一致。

NacosServiceRegistry.register方法实现

第三板斧:自动注册触发器——AutoServiceRegistration

AutoServiceRegistration 是一个标记接口,本身没有方法,仅代表“自动注册”的语义。

AutoServiceRegistration接口定义

它有一个重要的抽象实现类 AbstractAutoServiceRegistration

AbstractAutoServiceRegistration类定义

这个类实现了 ApplicationListener,监听 WebServerInitializedEvent 事件。这个事件在SpringBoot应用启动时,当内嵌的Web服务器(如Tomcat)成功启动后发布。

SpringBoot中发布WebServerInitializedEvent的代码

因此,一旦SpringBoot项目启动完成,Web服务器就绪,就会触发 AbstractAutoServiceRegistration 监听器执行。最终,它会调用 ServiceRegistry.register(getRegistration()) 来完成服务的自动注册。

AbstractAutoServiceRegistration中的注册方法

Nacos通过继承 AbstractAutoServiceRegistration,实现了自己的自动注册类。

NacosAutoServiceRegistration类定义

至此,当前服务的IP、端口等信息就被自动注册到了Nacos服务端。

整个自动注册流程可以用下图清晰概括:

微服务自动注册到Nacos的完整流程图

值得注意的是,这套“三板斧”并非Nacos独有。Eureka、Zookeeper等常见注册中心在整合SpringCloud时,都遵循了相同的模式。

多种ServiceRegistry实现列表

负载均衡组件:Ribbon

理解了服务如何自动注册,我们再来看看负载均衡组件Ribbon。它的核心作用是从多个服务实例中,根据既定算法选择一个进行调用。

但问题来了:服务实例数据都存储在注册中心,Ribbon是如何获取这些数据的?

答案是需要注册中心主动适配Ribbon。Ribbon定义了一个获取服务实例列表的接口:ServerList

ServerList接口定义

接口中的两个方法在大多数实现中功能是一致的。Ribbon通过 ServerList 获取到服务实例列表后,才能基于这些数据进行负载均衡。

Nacos为此实现了 NacosServerList,为Ribbon提供来自Nacos注册中心的服务数据。

NacosServerList类定义及核心方法

这样,Ribbon就能“知道”Nacos中注册了哪些服务实例。同理,Eureka、Zookeeper等也都提供了各自的 ServerList 实现。

多种ServerList实现列表,包括Eureka、Nacos等

到这里,我们明白了Ribbon获取数据的原理:需要注册中心进行适配。

不过,这里引申出一个值得讨论的设计问题。SpringCloud本身倡导的是一套规范和抽象,以实现组件的可插拔替换。而Ribbon作为一个具体的负载均衡实现,却要求每个注册中心都必须单独适配它,这在一定程度上与SpringCloud的初衷相悖。

一个更理想的设计是:Ribbon在适配SpringCloud时,应使用SpringCloud自身提供的、统一的API来获取服务实例。这样,各个注册中心只需要适配这套统一API即可,无需额外关心上层的负载均衡组件是什么。

事实上,SpringCloud确实提供了这样一个统一的API:DiscoveryClient

DiscoveryClient接口定义

通过 DiscoveryClient 可以获取服务实例,当然,这也需要各个注册中心提供对应的实现。

多种DiscoveryClient实现列表

随着Netflix Ribbon进入维护模式,SpringCloud官方推出了自己的负载均衡组件 spring-cloud-starter-loadbalancer 作为替代方案。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

loadbalancer 在获取服务实例时,采用的就是 DiscoveryClient 这套统一的接口。

LoadBalancer使用DiscoveryClient获取服务实例的代码

因此,对于 loadbalancer 而言,注册中心只需实现 DiscoveryClient,就自动完成了适配,无需像适配Ribbon那样额外实现 ServerList。这无疑是更符合SpringCloud设计哲学的做法。

远程调用框架:OpenFeign

最后,我们来看负责发起远程调用的OpenFeign。它是一个声明式的HTTP客户端,通过简单的接口注解就能实现远程服务调用,例如:

使用OpenFeign声明式调用的示例接口

其原理是为接口创建动态代理对象,并解析方法上的注解。当调用方法时,会根据注解信息拼接出HTTP请求地址,格式通常为:http://服务名/接口路径

以上面代码为例,调用 saveOrder 方法时,拼接出的地址是 http://order/order。然而,这里只知道服务名 order,并不知道其具体的IP和端口。

此时,就需要负载均衡组件(Ribbon或loadbalancer)出场了。

OpenFeign会向负载均衡组件请求:“请给我一个order服务的可用实例”。

OpenFeign向Ribbon请求服务实例的示意图

负载均衡组件则从服务实例列表中(通过ServerListDiscoveryClient获得),根据负载均衡策略选出一个实例(包含IP和端口)返回给OpenFeign。

拿到具体的IP和端口后,OpenFeign会重构请求URL,将原来的服务名替换为具体的 IP:端口。以下是Ribbon中的相关实现:

Ribbon中重构URL的reconstructURIWithServer方法

假设选中的实例IP为 192.168.2.100,端口为 8080,那么重构后的最终请求URL就是 http://192.168.2.100:8080/order。之后,OpenFeign便能发送HTTP请求了。

对于 loadbalancer,其重构URL的逻辑也是类似的:

LoadBalancer中重构URL的doReconstructURI方法

总结

通过以上分析,我们清晰地看到了Nacos、OpenFeign、Ribbon/loadbalancer等组件在SpringCloud微服务体系中的协同工作原理。其核心在于:框架定义好扩展接口(如ServiceRegistryDiscoveryClient),各个组件通过实现这些接口来完成适配与整合。

这其实也是很多优秀开源框架的通用做法:定义好抽象和规范,具体的实现交给生态。

最后,让我们用一张完整的架构图来回顾整个流程:

Nacos、OpenFeign、Ribbon协同工作全链路架构图

  1. 服务注册:Order服务启动,通过NacosAutoServiceRegistration -> NacosServiceRegistry将自身实例注册到Nacos Server
  2. 服务发现:User服务中的Ribbon通过NacosServerList(或loadbalancer通过NacosDiscoveryClient)从Nacos Server拉取Order服务的实例列表。
  3. 负载均衡调用:User服务中的OpenFeign声明式接口被调用时,向Ribbon请求一个实例,Ribbon根据策略选取一个,OpenFeign重构URL后发起HTTP调用。

希望这篇从实践出发的原理剖析,能帮助你更深刻地理解SpringCloud微服务组件间的协作。如果你想深入某个组件的源码细节,欢迎在云栈社区Java后端 & 架构板块探索更多相关文章和讨论。




上一篇:Nacos 源码解析:服务注册与发现机制实现原理详解
下一篇:Nacos整合Spring Cloud注册中心原理:ServiceRegistry与Ribbon适配详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-10 14:47 , Processed in 0.480022 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表