# 第二代Spring Cloud 核心组件


<!--more-->


## 一. SCA Nacos 服务注册和配置中心

### 1.1 nacos介绍

Nacos（DynamicNamingandConﬁgurationService）是阿⾥巴巴开源的⼀个针对微服务架构中服务发现、配置管理和服务管理平台。

Nacos就是注册中⼼+配置中⼼的组合（Nacos=Eureka+Conﬁg+Bus）https://nacos.io

功能特性:

- 服务发现与健康检查
- 动态配置管理
- 动态DNS服务
- 服务和元数据管理（管理平台的⻆度，nacos也有⼀个ui⻚⾯，可以看到注册的服务及其实例信息（元数据信息）等），动态的服务权重调整、动态服务优雅下线，都可以去做

### 1.2 nacos单例服务部署

[官方安装文档](https://nacos.io/zh-cn/docs/2.0.0-upgrading.html)

- 下载最新稳定版压缩包 nacos-server-1.4.3.tar.gz 并解压
- 执行单机启动命令 `sh startup.sh  -m  standalone`
- 访问 http://127.0.0.1:8848/nacos/#/login 默认用户名密码 nacos/nacos

### 1.3 nacos服务注册中心

#### 1.3.1 服务提供者注册到nacos

改造简历微服务

- 父Pom引入 spring-cloud-alibaba-dependencies

- 创建子工程 demo-service-resume-8083-nacos

- 服务提供者工程引入nacos客户端依赖 spring-cloud-starter-alibaba-nacos-discovery

- application.yml 添加 nacos服务发现配置

- 启动服务,观察nacos控制台

  ![img_46](https://img.zhaojq.top/20260727092005070.png "img_46")

  ![img_47](https://img.zhaojq.top/20260727092005301.png "img_47")

  > 保护阈值：可以设置为0-1之间的浮点数，它其实是⼀个⽐例值（当前服务健康实例数/当前服务总实例数）
  >
  > 保护阈值的意义在于:
  >
  > 当服务A健康实例数/总实例数<保护阈值的时候，说明健康实例真的不多了，这个时候保护阈值会被触发（状态true）
  >
  > nacos将会把该服务所有的实例信息（健康的+不健康的）全部提供给消费者，消费者可能访问到不健康的实例，请求失败，但这样也⽐造成雪崩要好，牺牲了⼀些请求，保证了整个系统的⼀个可⽤。

#### 1.3.2 服务消费者从nacos获取服务提供者

改造自动投递微服务

- 新创建示例工程 demo-service-autodeliver-8092-nacos
- 其他配置和提供者一样
- 测试 http://localhost:8092/autodeliver/checkState/{userId}

#### 1.3.3 负载均衡

Nacos客户端引⼊的时候，会关联引⼊Ribbon的依赖包，我们使⽤OpenFiegn的时候也会引⼊Ribbon的依赖，Ribbon包括Hystrix都按原来⽅式进⾏配置即可

此处，我们将简历微服务，再创建一个示例工程  demo-service-resume-8083-nacos 并启动，注册到Nacos上，便于测试负载均衡，我们通过后台也可以看出。

#### 1.3.4 nacos数据模型(领域模型)

Namespace命名空间、Group分组、集群这些都是为了进⾏归类管理，把服务和配置⽂件进⾏归类，归类之后就可以实现⼀定的效果，⽐如隔离. ⽐如，对于服务来说，不同命名空间中的服务不能够互相访问调⽤

- Namespace 

  命名空间，对不同的环境进⾏隔离，⽐如隔离开发环境、测试环境和⽣产环境

- group 

  分组，将若⼲个服务或者若⼲个配置集归为⼀组，通常习惯⼀个系统归为⼀个组

- service

  某⼀个服务，⽐如简历微服务

- dataId

  配置集或者可以认为是⼀个配置⽂件

**Namespace+Group+Service** 如同Maven中的GAV坐标，GAV坐标是为了锁定Jar，这⾥是为了锁定服务

**Namespace+Group+DataId** 如同Maven中的GAV坐标，GAV坐标是为了锁定Jar，这⾥是为了锁定配置⽂件

> Nacos抽象出了Namespace、Group、Service、DataId等概念，具体代表什么取决于怎么⽤（⾮常灵活）

nacos服务的分级模型:

![img_48](https://img.zhaojq.top/20260727092005515.png "img_48")


#### 1.3.5 nacos server 数据持久化

Nacos默认使⽤嵌⼊式数据库进⾏数据存储，它⽀持改为外部Mysql存储

- 新建数据库nacos_conﬁg，数据库初始化脚本⽂件${nacoshome}/conf/nacos-mysql.sql 
- 修改${nacoshome}/conf/application.properties，增加Mysql数据源配置

#### 1.3.6 nacos server集群

- 安装3个或3个以上的Nacos 

  复制解压后的nacos⽂件夹，分别命名为nacos-01、nacos-02、nacos-03 修改配置⽂件

- 修改配置文件

  > 同⼀台机器模拟，将上述三个⽂件夹中application.properties中的server.port分别改为8848、8849、8850 
  >
  > 同时给当前实例节点绑定ip，因为服务器可能绑定多个ip
  >
  > nacos.inetutils.ip-address=127.0.0.1

  > 复制⼀份conf/cluster.conf.example⽂件，命名为cluster.conf 在配置⽂件中设置集群中每⼀个节点的信息
  >
  > 127.0.0.1:8848 
  >
  > 127.0.0.1:8849 
  >
  > 127.0.0.1:8850

- 分别启动每一个实例

  `sh startup.sh -m cluster`



### 1.4 nacos 配置中心

之前：SpringCloudConﬁg+Bus, 有Nacos之后，分布式配置就简单很多

![img_49](https://img.zhaojq.top/20260727092005716.png "img_49")

如上配置文件配置完成后,即可在微服务中开启nacos配置管理

- 添加依赖 spring-cloud-starter-alibaba-nacos-config

- 微服务中如何锁定NacosServer中的配置⽂件（dataId）

  通过Namespace+Group+dataId来锁定配置⽂件，Namespace不指定就默认public，Group不指定就默认DEFAULT_GROUP 

  **dataId 完整格式如下**:

  `${prefix}-${spring.profile.active}.${file-extension}`

  - prefix

    默认为 `spring.application.name` 的值, 也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置

  - spring.profile.active

    即为当前环境的对应的profile. 当此值为空时,对应的连接符"-"也将不存在, dataId的拼接格式变成 `${prefix}-${file-extension}`

  - file-extension

    为配置内容的数据格式 , 通过`spring.cloud.nacos.config.file-extension`来配置

  > 为便于测试,我们在nacos中创建了3个配置文件
  >
  > ![img_50](https://img.zhaojq.top/20260727092005917.png "img_50")

- 使用Spring cloud 注解 @RefreshScope实现配置自动更新

  我们写一个测试controller用于测试配置更新

  ```java
  @RestController
  @RequestMapping("/config")
  @RefreshScope
  public class ConfigController {
  
      // 和取本地配置信息一样
      @Value("${test}")
      private String test;
  
      @GetMapping("/viewconfig")
      public String viewconfig() {
          return test;
      }
  }
  ```

  可以通过以下方式引入扩展的dataId

  ```yml
  ext-config[0]:
    data-id: test1.yml
    group: DEFAULT_GROUP
    refresh: true  #开启扩展dataId的动态刷新
  ext-config[1]:
    data-id: test2.yml
    group: DEFAULT_GROUP
    refresh: true  #开启扩展dataId的动态刷新
  ```

  > 优先级：根据规则⽣成的dataId>扩展的dataId（对于扩展的dataId，[n]n越⼤优先级越⾼）
  >
  > 注意: 用配置中心时,启动前把application.yml 改成系统级别 bootstrap.yml



## 二. SCA Sentinel分布式系统的流量防卫兵

### 2.1 sentinel介绍

**Sentinel**是⼀个⾯向云原⽣微服务的流量控制、熔断降级组件。

替代**Hystrix**，针对问题：服务雪崩、服务降级、服务熔断、服务限流

> Hystrix：
>
> 服务消费者（⾃动投递微服务）—>调⽤服务提供者（简历微服务）, 在调⽤⽅引⼊Hystrix—>单独搞了⼀个Dashboard项⽬—>Turbine 
>
> 1）⾃⼰搭建监控平台dashboard 
>
> 2）没有提供UI界⾯进⾏服务熔断、服务降级等配置（⽽是写代码，⼊侵了我们源程序环境）
>
> Sentinel：
>
> 1）独⽴可部署Dashboard/控制台组件
>
> 2）减少代码开发，通过UI界⾯配置即可完成细粒度控制（⾃动投递微服务）

![img_51](https://img.zhaojq.top/20260727092006238.png "img_51")

**Sentinel分两个部分:**

- 核心库

  (Java客户端）不依赖任何框架/库，能够运⾏于所有Java运⾏时环境，同时对Dubbo/SpringCloud等框架也有较好的⽀持。

- 控制台

  Dashboard）基于SpringBoot开发，打包后可以直接运⾏，不需要额外的Tomcat等应⽤容器。

**特征:**

- 丰富的应⽤场景：Sentinel承接了阿⾥巴巴近10年的双⼗⼀⼤促流量的核⼼场景，例如秒杀（即突发流量控制在系统容量可以承受的范围）、消息削峰填⾕、集群流量控制、实时熔断下游不可⽤应⽤等。
- 完备的实时监控：Sentinel同时提供实时的监控功能。您可以在控制台中看到接⼊应⽤的单台机器秒级数据，甚⾄500台以下规模的集群的汇总运⾏情况。
- ⼴泛的开源⽣态：Sentinel提供开箱即⽤的与其它开源框架/库的整合模块，例如与SpringCloud、Dubbo的整合。您只需要引⼊相应的依赖并进⾏简单的配置即可快速地接⼊Sentinel。
- 完善的SPI扩展点：Sentinel提供简单易⽤、完善的SPI扩展接⼝。您可以通过实现扩展接⼝来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

![img_52](https://img.zhaojq.top/20260727092006462.png "img_52")

### 2.2 sentinel部署

- 下载 [sentinel](https://github.com/alibaba/Sentinel/releases) sentinel-dashboard-v**.jar
- 启动 java -jar sentinel-dashboard-v**.jar &
- 用户名/密码: sentinel/sentinel
- Localhost:8080进入管理界面

### 2.3 服务改造

- 创建示例工程 demo-service-autodeliver-8093-sentinel

- 引入依赖 spring-cloud-starter-alibaba-sentinel

- application.yml 修改

- 启动,使用sentinel监控

  此时我们发现控制台没有任何变化，因为懒加载，我们只需要发起⼀次请求触发即可

  ![img_53](https://img.zhaojq.top/20260727092006669.png "img_53")

### 2.4 sentinel关键概念

- 资源

  它可以是Java应⽤程序中的任何内容，例如，由应⽤程序提供的服务，或由应⽤程序调⽤的其它应⽤提供的服务，甚⾄可以是⼀段代码。我们请求的API接⼝就是资源

- 规则

  围绕资源的实时状态设定的规则，可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

### 2.5 sentinel 流量规则模块

系统并发能⼒有限，⽐如系统A的QPS⽀持1个，如果太多请求过来，那么A就应该进⾏流量控制了，⽐如其他请求直接拒绝

![img_54](https://img.zhaojq.top/20260727092006862.png "img_54")

> 资源名：默认请求路径
>
> 针对来源：Sentinel可以针对调⽤者进⾏限流，填写微服务名称，默认default（不区分来源）
>
> 阈值类型/单机阈值
>
> - QPS：（每秒钟请求数量）当调⽤该资源的QPS达到阈值时进⾏限流
> - 线程数：当调⽤该资源的线程数达到阈值的时候进⾏限流（线程处理请求的时候，如果说业务逻辑执⾏时间很⻓，流量洪峰来临时，会耗费很多线程资源，这些线程资源会堆积，最终可能造成服务不可⽤，进⼀步上游服务不可⽤，最终可能服务雪崩）
>
> 是否集群：是否集群限流
>
> 流控模式：
>
> - 直接：资源调⽤达到限流条件时，直接限流
> - 关联：关联的资源调⽤达到阈值时候限流⾃⼰
> - 链路：只记录指定链路上的流量
>
> 流控效果：
>
> - 快速失败：直接失败，抛出异常
> - WarmUp：根据冷加载因⼦（默认3）的值，从阈值/冷加载因⼦，经过预热时⻓，才达到设置的QPS阈值
> - 排队等待：匀速排队，让请求匀速通过，阈值类型必须设置为QPS，否则⽆效
>
> 流控模式之关联限流: 关联的资源调⽤达到阈值时候限流⾃⼰，⽐如⽤户注册接⼝，需要调⽤身份证校验接⼝（往往身份证校验接⼝），如果身份证校验接⼝请求达到阈值，使⽤关联，可以对⽤户注册接⼝进⾏限流。
>
> > Tips: 模拟密集式请求/user/validateID验证接⼝，我们会发现/user/register接⼝也被限流了
> >
> > Blocked by Sentinel (flow limiting)
>
> 
>
> 流控模式之链路限流: 链路指的是请求链路（调⽤链）
>
> 链路模式下会控制该资源所在的调⽤链路⼊⼝的流量。需要在规则中配置⼊⼝资源，即该调⽤链路⼊⼝的上下⽂名称。
>
> ![img_55](https://img.zhaojq.top/20260727092007048.png "img_55")
>
>  
>
> 流控效果之Warmup:
>
> 当系统⻓期处于空闲的情况下，当流量突然增加时，直接把系统拉升到⾼⽔位可能瞬间把系统压垮，⽐如电商⽹站的秒杀模块。通过WarmUp模式（预热模式），让通过的流量缓慢增加，经过设置的预热时间以后，到达系统处理请求速率的设定值。WarmUp模式默认会从设置的QPS阈值的1/3开始慢慢往上增加⾄QPS设置值。
>
>  
>
> 流控效果之排队等待:
>
> 排队等待模式下会严格控制请求通过的间隔时间，即请求会匀速通过，允许部分请求排队等待，通常⽤于消息队列削峰填⾕等场景。需设置具体的超时时间，当计算的等待时间超过超时时间时请求就会被拒绝。很多流量过来了，并不是直接拒绝请求，⽽是请求进⾏排队，⼀个⼀个匀速通过（处理），请求能等就等着被处理，不能等（等待时间>超时时间）就会被拒绝
>
> 例如，QPS配置为5，则代表请求每200ms才能通过⼀个，多出的请求将排队等待通过。超时时间代表最⼤排队时间，超出最⼤排队时间的请求将会直接被拒绝。排队等待模式下，QPS设置值不要超过1000（请求间隔1ms）。



### 2.6 sentinel 降级规则模块

流控是对外部来的⼤流量进⾏控制，熔断降级的视⻆是对内部问题进⾏处理.

sentinel降级会在调⽤链路中某个资源出现不稳定状态时（例如调⽤超时或异常⽐例升⾼），对这个资源的调⽤进⾏限制，让请求快速失败，避免影响到其它的资源⽽导致级联错误。当资源被降级后，在接下来的降级时间窗⼝之内，对该资源的调⽤都⾃动熔断.

> 这⾥的降级其实是Hystrix中的熔断

**策略**

Sentinel不会像Hystrix那样放过⼀个请求尝试⾃我修复，就是明明确确按照时间窗⼝来，熔断触发后，时间窗⼝内拒绝请求，时间窗⼝后就恢复

- RT（平均响应时间）

  当1s内持续进⼊>=5个请求，平均响应时间超过阈值（以ms为单位），那么在接下的时间窗⼝（以s为单位）之内，对这个⽅法的调⽤都会⾃动地熔断（抛出DegradeException）。注意Sentinel默认统计的RT上限是4900ms，超出此阈值的都会算作4900ms，若需要变更此上限可以通过启动配置项`-Dcsp.sentinel.statistic.max.rt=xxx`来配置。

  ![img_56](https://img.zhaojq.top/20260727092007206.png "img_56")

- 异常⽐例

  当资源的每秒请求量>=5，并且每秒异常总数占通过量的⽐值超过阈值之后，资源进⼊降级状态，即在接下的时间窗⼝（以s为单位）之内，对这个⽅法的调⽤都会⾃动地返回。异常⽐率的阈值范围是[0.0,1.0]，代表0%-100%。

- 异常数
  当资源近1分钟的异常数⽬超过阈值之后会进⾏熔断。注意由于统计时间窗⼝是分钟级别的, 若timeWindow小于60s, 则结束熔断状态后仍可能再进⼊熔断状态

### 2.7 其他模块

这里不再细说

### 2.8 自定义兜底逻辑

@SentinelResource注解类似于Hystrix中的@HystrixCommand注解

@SentinelResource注解中有两个属性需要我们进⾏区分，blockHandler属性⽤来指定不满⾜Sentinel规则的降级兜底⽅法，fallback属性⽤于指定Java运⾏时异常兜底⽅法

- 在api接口资源处配置

  ```java
  @SentinelResource(value = "findResumeOpenState",
          blockHandlerClass = SentinelHandlersClass.class, blockHandler = "handleException",
          fallbackClass = SentinelHandlersClass.class, fallback = "handleError")
  ```

- 自定义兜底逻辑类 SentinelHandlersClass



### 2.9 基于nacos实现sentinel规则持久化

⽬前，SentinelDashboard中添加的规则数据存储在内存，微服务停掉规则数据就消失，在⽣产环境下不合适。我们可以将Sentinel规则数据持久化到Nacos配置中⼼，让微服务从Nacos获取规则数据。

- 添加依赖 sentinel-datasource-nacos

- 配置文件中添加nacos数据源

- Nacos server中添加对应规则的配置集

  流控规则配置集: demo-service-autodeliver-nacos-flow-rules

  ```json
  [
      {
          "resource":"findResumeOpenState",
          "limitApp":"default",
          "grade":1,
          "count":1,
          "strategy":0,
          "controlBehavior":0,
          "clusterMode":false
      }
  ]
  # 所有属性来⾃源码FlowRule类
  resource：资源名称
  limitApp：来源应⽤
  grade：阈值类型 0 线程数 1 QPS
  count：单机阈值
  strategy：流控模式，0 直接 1 关联 2 链路
  controlBehavior：流控效果，0 快速失败 1 Warm Up 2 排队等待
  clusterMode：true/false 是否集群
  ```

  降级规则配置集 demo-service-autodeliver-nacos-degrade-rules

  ```json
  [
      {
          "resource":"findResumeOpenState",
          "grade":2,
          "count":1,
          "timeWindow":5
      }
  ]
  
  # 所有属性来⾃源码DegradeRule类
  resource：资源名称
  grade：降级策略 0 RT 1 异常⽐例 2 异常数
  count：阈值
  timeWindow：时间窗
  ```

  > 注意
  >
  > 1）⼀个资源可以同时有多个限流规则和降级规则，所以配置集中是⼀个json数组
  >
  > 2）Sentinel控制台中修改规则，仅是内存中⽣效，不会修改Nacos中的配置值，重启后恢复原来的值； Nacos控制台中修改规则，不仅内存中⽣效，Nacos中持久化规则也⽣效，重启后规则依然保持



## 三. nacos+sentinel+dubbo

改造“⾃动投递微服务”和“简历微服务”，删除OpenFeign 和 Ribbon，使⽤Dubbo RPC 和 Dubbo LB

⾸先，需要删除或者注释掉⽗⼯程中的热部署依赖

> <!--<dependency>
> <groupId>org.springframework.boot</groupId>
> <artifactId>spring-boot-devtools</artifactId><optional>true</optional>
> </dependency>
> -->

### 3.1 服务提供者改造

见示例工程,为演示多提供者,我们复制两个: demo-service-resume-8085-nacos-sentinel-dubbo, demo-service-resume-8086-nacos-sentinel-dubbo

> Tips:
>
> @Service为dubbo的注解
>
> 添加 spring.main: allow-bean-definition-overriding: true 配置

### 3.2 服务消费者改造

见示例工程: demo-service-autodeliver-8094-nacos-sentinel-dubbo

> tips:
>
> @Reference 使用dubbo注解



## 四. SCA小结

SCA实际上发展了三条线

- 第⼀条线：开源出来⼀些组件
- 第⼆条线：阿⾥内部维护了⼀个分⽀，⾃⼰业务线使⽤
- 第三条线：阿⾥云平台部署⼀套，付费使⽤

从战略上来说，SCA更是为了贴合阿⾥云。

⽬前来看，开源出来的这些组件，推⼴及普及率不⾼，社区活跃度不⾼，稳定性和体验度上仍需进⼀步提升，根据实际使⽤来看Sentinel的稳定性和体验度要好于Nacos。

