微服务Spring Cloud Alibaba简单笔记

Nacos

Nacos体系架构

领域模型

Nacos 领域模型描述了服务与实例之间的边界和层级关系。Nacos 的服务领域模型是以“服 务”为维度构建起来的,这个服务并不是指集群中的单个服务器,而是指微服务的服务名。

“服务”是 Nacos 中位于最上层的概念,在服务之下,还有集群和实例的概念。

  • 服务

    在服务这个层级上可以配置元数据和服务保护阈值等信息。服务阈值是一个 0~1 之间的 数字,当服务的健康实例数与总实例的比例小于这个阈值的时候,说明能提供服务的机器已经 没多少了。这时候 Nacos 会开启服务保护模式,不再主动剔除服务实例,同时还会将不健康 的实例也返回给消费者。

  • 集群

    一个服务由很多服务实例组成,在每个服务实例启动的时候,可以设置它所属的集群,在 集群这个层级上,也可以配置元数据。除此之外,还可以为持久化节点设置健康检查 模式。

    所谓持久化节点,是一种会保存到 Nacos 服务端的实例,即便该实例的客户端进程没有在运 行,实例也不会被服务端删除,只不过 Nacos 会将这个持久化节点状态标记为不健康, Nacos 可以采用一种“主动探活”的方式来对持久化节点做健康检查。

    除了持久化节点以外,大部分服务节点在 Nacos 中以“临时节点”的方式存在,它是默认的 服务注册方式,从名字中就可以看出,这种节点不会被持久化保存在 Nacos 服务器,临 时节点通过主动发送 heartbeat 请求向服务器报送自己的状态。

  • 实例

    这里所说的实例就是指服务节点,可以在 Nacos 控制台查看每个实例的 IP 地址和端口、 编辑实例的元数据信息、修改它的上线 / 下线状态或者配置路由权重等等。

在这三个层级上都有“元数据”这一数据结构,可以把它理解为一组包含了服务 描述信息(如服务版本等)和自定义标签的数据集合。Client 端通过服务发现技术可以获取到 每个服务实例的元数据,可以将自定义的属性加入到元数据并在 Client 端实现某些定制化 的业务场景。

数据模型

Nacos 的数据模型有三个层次结构,分别是 Namespace、Group 和 Service/DataId。
在这里插入图片描述

  • Namespace:即命名空间,它是最顶层的数据结构,可以用它来区分开发环境、生产 环境等不同环境。默认情况下,所有服务都部署到一个叫做“public”的公共命名空间;
  • Group:在命名空间之下有一个分组结构,默认情况下所有微服务都属于 “DEFAULT_GROUP”这个分组,不同分组间的微服务是相互隔离的;
  • Service/DataID:在 Group 分组之下,就是具体的微服务了,比如订单服务、商品服务等等。

通过 Namespace + Group + Service/DataID,就可以精准定位到一个具体的微服务

Nacos 基本架构

Nacos 的核心功能有两个,一个是 Naming Service,用来做服务发现的模块;另 一个是 Config Service,用来提供配置项管理、动态更新配置和元数据的功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OFQ0pyt6-1677596762818)(C:\Users\0\AppData\Roaming\Typora\typora-user-images\image-20230226225644390.png)]

Provider APP 和 Consumer APP 通过 Open API 和 Nacos 服务 器的核心模块进行通信。这里的 Open API 是一组对外暴露的 RESTful 风格的 HTTP 接口。

在 Nacos 和核心模块里,Naming Service 提供了将对象和实体的“名字”映射到元数据的 功能,这是服务发现的基础功能之一。

Nacos 还有一个相当重要的模块:Nacos Core 模块。它可以提供一系列的平台基础功能, 是支撑 Nacos 上层业务场景的基石

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49FknCy3-1677596762818)(E:\BaiduNetdiskDownload\180SpringCloud微服务项目实战\images\472384\d0c78d0c0f2bb72c45788a5c2d423512.jpg)]

Nacos集群环境搭建

Nacos Server 的安装包可以从 Alibaba 官方 GitHub 中的Release 页面下载。

下载完成后,可以在本地将 Nacos Server 压缩包解压,并将解压后的目录名改为“nacos-cluster1”,再复制一份同样的文件到 nacos-cluster2,以此来模拟一个由两台 Nacos Server 组成的集群。

修改启动项参数

Nacos Server 的启动项位于 conf 目录下的 application.properties 文件里,需要修改服务启动端口和数据库连接

Nacos Server 的启动端口由 server.port 属性指定,默认端口是 8848。在 nacos-cluster1 中仍然使用 8848 作为默认端口,需要把 nacos-cluster2 中的端口号改为 8948

在默认情况下,Nacos Server 会使用 Derby 作为数据源,用于保存配置管理数据。将 Nacos Server 的数据源迁移到更加稳定的 MySQL 数据库中,需要修改三处 Nacos Server 的数据库配置。

指定数据源:spring.datasource.platform=mysql 将这行注释放开;

指定 DB 实例数:放开 db.num=1 这一行的注释;

修改 JDBC 连接串:db.url.0 指定了数据库连接字符串,db.user.0 和 db.password.0 分别指定了连接数据库的用户名和密码

创建数据库表

Nacos 已经把建表语句放在解压后的 Nacos Server 安装目录中下的 conf 文件夹里

添加集群机器列表

Nacos Server 可以从一个本地配置文件中获取所有的 Server 地址信息,从而实现服务器之 间的数据同步。

在 Nacos Server 的 conf 目录下创建 cluster.conf 文件,并将 nacos-cluster1 和 nacos-cluster2 这两台服务器的 IP 地址 + 端口号添加到文件中。

## 注意,这里的IP不能是localhost或者127.0.0.1
192.168.1.100:8848
192.168.1.100:8948

启动 Nacos Server

通过 -m standalone 参数,可以单机模式启动。

Nacos 的启动脚本位于安装目录下的 bin 文件夹,其中 Windows 操作系统对应的启动脚本和关闭脚本分别是 startup.cmd 和 shutdown.cmd, Mac 和 Linux 系统对应的启动和关闭脚本是 startup.sh 和 shutdown.sh。

登录 Nacos 控制台

使用 Nacos 默认创建好的用户 nacos 登录系统,用户名和密码都是 nacos。

为了验证集群环境处于正常状态,可以在左侧导航栏中打开“集群管理”下的“节点列表” 页面,在这个页面上显示了集群环境中所有的 Nacos Server 节点以及对应的状态,它们的节点状态都是绿色的“UP”,这表示搭建的集群环境一切正常。

在实际的项目中,如果某个微服务 Client 要连接到 Nacos 集群做服务注册,并不会把 Nacos 集群中的所有服务器都配置在 Client 中,否则每次 Nacos 集群增加或删除了节点, 都要对所有 Client 做一次代码变更并重新发布。

常见的一个做法是提供一个 VIP URL 给到 Client,VIP URL 是一个虚拟 IP 地址,可以把 真实的 Nacos 服务器地址列表“隐藏”在虚拟 IP 后面,客户端只需要连接到虚 IP 即可,由 提供虚 IP 的组件负责将请求转发给背后的服务器列表。这样一来,即便 Nacos 集群机器数量 发生了变动,也不会对客户端造成任何感知。

提供虚 IP 的技术手段有很多,比如通过搭建 Nginx+LVS 或者 keepalived 技术实现高可用集群。

将服务提供者注册到 Nacos 服务器

添加 Nacos 依赖项

Spring Boot、Spring Cloud 和 Spring Cloud Alibaba 三者之间有严格的版本匹配关系

版本说明: link

将 Spring Cloud Alibaba 和 Spring Cloud 的依赖项版本添加到顶层项目下的 pom.xml 文件中。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2020.0.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2021.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    <!-- 省略部分代码 -->
</dependencyManagement>

定义了组件的大版本之后,就可以直接把 Nacos 的依赖项加入到两个子模块的 pom.xml 文件中

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在添加完依赖项之后,就可以通过配置项开启 Nacos 的服务治理功能了。Spring Cloud 各个组件都采用了自动装配器实现了轻量级的组件集成功能,只需要几行配置,剩下的初始化工作都可以交给背后的自动装配器来实现。

Nacos 自动装配原理

在 Spring Cloud 稍早一些的版本中,需要在启动类上添加 @EnableDiscoveryClient 注 解开启服务治理功能,而在新版本的 Spring Cloud 中,这个注解不再是一个必须的步骤, 们只需要通过配置项就可以开启 Nacos 的功能。

们将 Nacos 依赖项添加到项目中,同时也引入了 Nacos 自带的自动装配器,比如下面这几 个被引入的自动装配器就掌管了 Nacos 核心功能的初始化任务。

NacosDiscoveryAutoConfiguration:服务发现功能的自动装配器,它主要做两件事 儿:加载 Nacos 配置项,声明 NacosServiceDiscovery 类用作服务发现;

NacosServiceAutoConfiguration:声明核心服务治理类 NacosServiceManager,它可以通过 service id、group 等一系列参数获取已注册的服务列表;

NacosServiceRegistryAutoConfiguration:Nacos 服务注册的自动装配器。

添加 Nacos 配置项

spring:
    cloud:
        nacos:
            discovery:
            # Nacos的服务注册地址,可以配置多个,逗号分隔
            server-addr: localhost:8848
            # 服务注册到Nacos上的名称,一般不用配置
            service: coupon-customer-serv
            # nacos客户端向服务端发送心跳的时间间隔,时间单位其实是ms
            heart-beat-interval: 5000
            # 服务端没有接受到客户端心跳请求就将其设为不健康的时间间隔,默认为15s
            # 注:推荐值该值为15s即可,如果有的业务线希望服务下线或者出故障时希望尽快被发现,可以适
            heart-beat-timeout: 20000
            # 元数据部分 - 可以自己随便定制
            metadata:
                mydata: abc
            # 客户端在启动时是否读取本地配置项(一个文件)来获取服务列表
            # 注:推荐该值为false,若改成true。则客户端会在本地的一个
            # 文件中保存服务信息,当下次宕机启动时,会优先读取本地的配置对外提供服务。
            naming-load-cache-at-start: false
            # 命名空间ID,Nacos通过不同的命名空间来区分不同的环境,进行数据隔离,
            namespace: dev
            # 创建不同的集群
            cluster-name: Cluster-A
            # [注意]两个服务如果存在上下游调用关系,必须配置相同的group才能发起访问
            group: myGroup
            # 向注册中心注册服务,默认为true
            # 如果只消费服务,不作为服务提供方,倒是可以设置成false,减少开销
            register-enabled: true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ri6Gpvkn-1677596762819)(E:\BaiduNetdiskDownload\180SpringCloud微服务项目实战\images\473988\bd3383d12b43a35cfc3c240386c3e0f8.jpg)]

Namespace 可以用作环境隔离或者多租户隔离,其中:

环境隔离:比如设置三个命名空间 production、pre-production 和 dev,分别表示生产 环境、预发环境和开发环境,如果一个微服务注册到了 dev 环境,那么他无法调用其他环 境的服务,因为服务发现机制只会获取到同样注册到 dev 环境的服务列表。如果未指定 namespace 则服务会被注册到 public 这个默认 namespace 下。

多租户隔离:即 multi-tenant 架构,通过为每一个用户提供独立的 namespace 以实现租 户与租户之间的环境隔离。

Group 的使用场景非常灵活,列举几个:

环境隔离:在多租户架构之下,由于 namespace 已经被用于租户隔离,为了实现同一个租 户下的环境隔离,可以使用 group 作为环境隔离变量。

线上测试:对于涉及到上下游多服务联动的场景,将线上已部署的待上下游测服务的 group 设置为“group-A”,由于这是一个新的独立分组,所以线上的用户流量不会导向 到这个 group。这样一来,开发人员就可以在不影响线上业务的前提下,通过发送测试请 求到“group-A”的机器完成线上测试。

什么是单元封闭呢?为了保证业务的高可用性,通常会把同一个服务部署在 不同的物理单元(比如张北机房、杭州机房、上海机房),当某个中心机房出现故障的时 候,可以在很短的时间内把用户流量切入其他单元机房。由于同一个单元内的服务器资 源通常部署在同一个物理机房,因此本单元内的服务调用速度最快,而跨单元的服务调用将 要承担巨大的网络等待时间。这种情况下,可以为同一个单元的服务设置相同的 group,使微服务调用封闭在当前单元内,提高业务响应速度。

服务调用

服务消费者添加Nacos依赖项和配置信息

<!-- Nacos服务发现组件 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- 负载均衡组件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

<!-- webflux服务调用 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • spring-cloud-starter-loadbalancer:Spring Cloud御用负载均衡组件Loadbalancer,用来代替已经进入维护状态的Netflix Ribbon组件。会在下一课带深入了解Loadbalancer的功能,今天只需要简单了解下它的用法就可以了;
  • spring-boot-starter-webflux:Webflux是Spring Boot提供的响应式编程框架,响应式编程是基于异步和事件驱动的非阻塞程序。Webflux实现了Reactive Streams规范,内置了丰富的响应式编程特性。今天将用Webflux组件中一个叫做WebClient的小工具发起远程服务调用。

Nacos服务发现底层实现

Nacos Client通过一种 主动轮询 的机制从Nacos Server获取服务注册信息,包括地址列表、group分组、cluster名称等一系列数据。简单来说,Nacos Client会开启一个本地的定时任务,每间隔一段时间,就尝试从Nacos Server查询服务注册表,并将最新的注册信息更新到本地。这种方式也被称之为“Pull”模式,即客户端主动从服务端拉取的模式。

负责拉取服务的任务是UpdateTask类,它实现了Runnable接口。Nacos以开启线程的方式调用UpdateTask类中的run方法,触发本地的服务发现查询请求。

UpdateTask这个类是HostReactor的一个内部类,

在UpdateTask的源码中,它通过调用updateService方法实现了服务查询和本地注册表更新,在每次任务执行结束的时候,在结尾处它通过finally代码块设置了下一次executor查询的时间,周而复始循环往复。

OpenFeign

OpenFeign提供了一种声明式的远程调用接口,它可以大幅简化远程调用的编程体验。

OpenFeign使用了一种“动态代理”技术来封装远程服务调用的过程,远程服务调用的信息被写在了FeignClient接口中

OpenFeign的动态代理

在项目初始化阶段,OpenFeign会生成一个代理类,对所有通过该接口发起的远程调用进行动态代理。

![[,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pyah4TJ8-1677596762819)(C:\Users\

上图中的步骤1到步骤3是在项目启动阶段加载完成的,只有第4步“调用远程服务”是发生在项目的运行阶段。

首先,在项目启动阶段, OpenFeign框架会发起一个主动的扫包流程,从指定的目录下扫描并加载所有被@FeignClient注解修饰的接口。

然后, OpenFeign会针对每一个FeignClient接口生成一个动态代理对象,即图中的FeignProxyService,这个代理对象在继承关系上属于FeignClient注解所修饰的接口的实例。

接下来, 这个动态代理对象会被添加到Spring上下文中,并注入到对应的服务里,也就是图中的LocalService服务。

最后, LocalService会发起底层方法调用。实际上这个方法调用会被OpenFeign生成的代理对象接管,由代理对象发起一个远程服务调用,并将调用的结果返回给LocalService。

OpenFeign是如何通过动态代理技术创建代理对象的?

在这里插入图片描述

  1. 项目加载:在项目的启动阶段, EnableFeignClients注解 扮演了“启动开关”的角色,它使用Spring框架的 Import注解 导入了FeignClientsRegistrar类,开始了OpenFeign组件的加载过程。
  2. 扫包: FeignClientsRegistrar 负责FeignClient接口的加载,它会在指定的包路径下扫描所有的FeignClients类,并构造FeignClientFactoryBean对象来解析FeignClient接口。
  3. 解析FeignClient注解: FeignClientFactoryBean 有两个重要的功能,一个是解析FeignClient接口中的请求路径和降级函数的配置信息;另一个是触发动态代理的构造过程。其中,动态代理构造是由更下一层的ReflectiveFeign完成的。
  4. 构建动态代理对象:ReflectiveFeign 包含了OpenFeign动态代理的核心逻辑,它主要负责创建出FeignClient接口的动态代理对象。ReflectiveFeign在这个过程中有两个重要任务,一个是解析FeignClient接口上各个方法级别的注解,将其中的远程接口URL、接口类型(GET、POST等)、各个请求参数等封装成元数据,并为每一个方法生成一个对应的MethodHandler类作为方法级别的代理;另一个重要任务是将这些MethodHandler方法代理做进一步封装,通过Java标准的动态代理协议,构建一个实现了InvocationHandler接口的动态代理对象,并将这个动态代理对象绑定到FeignClient接口上。这样一来,所有发生在FeignClient接口上的调用,最终都会由它背后的动态代理对象来承接。

MethodHandler的构建过程涉及到了复杂的元数据解析,OpenFeign组件将FeignClient接口上的各种注解封装成元数据,并利用这些元数据把一个方法调用“翻译”成一个远程调用的Request请求。

那么上面说到的“元数据的解析”是如何完成的呢?它依赖于OpenFeign组件中的Contract协议解析功能。Contract是OpenFeign组件中定义的顶层抽象接口,它有一系列的具体实现。

专门用来解析Spring MVC标签的SpringMvcContract类的继承结构是SpringMvcContract->BaseContract->Contract。

OpenFeign的工作流程的重点是 动态代理机制。OpenFeing通过Java动态代理生成了一个“代理类”,这个代理类将接口调用转化成为了一个远程服务调用。

FeignClientsRegistrar是OpenFeign初始化的起点

实现服务间调用功能

把依赖项spring-cloud-starter-OpenFeign添加到子模块内的pom.xml文件中。

<!-- OpenFeign组件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在接口上声明了一个FeignClient注解,它专门用来标记被OpenFeign托管的接口。

@FeignClient(value = "coupon-template-serv", path = "/template")
public interface TemplateService {
    // 读取优惠券
    @GetMapping("/getTemplate")
    CouponTemplateInfo getTemplate(@RequestParam("id") Long id);

    // 批量获取
    @GetMapping("/getBatch")
    Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids);
}

在FeignClient注解中声明的value属性是目标服务的名称,需要确保这里的服务名称和Nacos服务器上显示的服务注册名称是一样的。

配置OpenFeign的加载路径

@EnableFeignClients(basePackages = {"com.xxx"})
public class Application {

}

在EnableFeignClients注解的basePackages属性中定义了一个com.xxx的包名,这个注解就会告诉OpenFeign在启动项目的时候做一件事儿:找到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值