SpringCloud-总结

目录

一、概述

1. 什么是SpringCloud?

2. 什么是微服务?

3. 微服务应用与传统应用的差异

3.1. 架构设计

3.2. 开发模式

3.3. 部署与运维

3.4. 数据管理

3.5. 容错性

二、Nacos 注册/配置中心

1. 基础

1.1. 概述

1.2. 安装

1.3. 启动

2. 功能:注册中心

2.1. 导入依赖

2.2. 服务注册

2.2.1. 配置nacos路径

2.2.2. 启动

2.3. 服务发现

2.3.1. 配置主类(开启服务发现功能)

2.3.2. 使用例

2.3.3. (扩展)@LoadBalanced实现负载均衡与服务发现

2.3.4. (面试)注册中心宕机后,远程调用能成功吗?

3. 功能:配置中心

3.1. 导入与配置

3.1.1. 导入依赖

3.1.2. 服务端文件配置

3.1.3. Nacos端配置集

3.2. 连接和监听

3.2.1. 基础连接

3.2.2. 动态刷新

3.2.3. 配置项监听器

3.3. 面试:Nacos配置集的优先级问题

3.3.1. Nacos配置集和项目配置文件谁的优先级高?

3.3.2. 如果我给一个项目imp导入多个配置集,那么此时的优先级呢?

3.4. 数据隔离

3.4.1. 名称空间

3.4.2. 基本使用

三、OpenFeign 远程调用

1. 基础配置

1.1. 导依赖

1.2. 注解式启动

2. 基础使用:调用业务api

3. 进阶使用:调用第三方api

4. 补充

4.1. 小技巧

4.2. 面试:负载均衡的两种实现

5. OpenFeign的进阶配置用法

5.1. 日志

5.2. 超时控制

5.3. 重试机制

5.3.1. 具体机制

5.3.2. 启用重试机制

5.4. 拦截器

5.5. Fallback兜底返回(需要熔断机制配合)

6. 总结

四、Sentinel 资源流程控制

1. 基础配置

1.1. 下载Sentinel

1.2. 启动Sentinel

1.3. 配置文件

1.4. 基础使用

2. 异常处理

2.1. 流程图

2.2. (回忆)SpringBoot的异常处理器

2.3. 对Web接口的兜底回调

2.4. blockHandler 兜底回调

2.5. OpenFeign 远程调用兜底回调

2.6. (扩展)SphU硬编码

2.7. (补充)blockHander与fallback兜底回调的区别

3. 流控规则

3.1. 流控模式

3.1.1. 关联

3.1.2. 直接

3.1.3. 链路

3.1.4. 关联

3.2. 流控效果

3.2.1. 快速失败

3.2.2. Warn Up 热启动

3.2.3. 匀速排队

4. 融断规则

4.1. 断路器

4.2. 熔断策略

5. 热点规则

5.1. 热点参数限流

5.2. 限流例外项

五、Gateway 网关

1. 路由配置

1.1. 模块创建与依赖导入

1.2. 基础使用

2. 断言规则 predicates

2.1. 断言类型 Query

2.2. 短写法

2.3. 长写法(断言工厂)

2.4. 自定义断言工厂

3. 过滤器 filter

3.1. 过滤器类型

3.2. 基础使用

3.3. 默认过滤器

3.4. 自定义过滤器

一、概述

1. 什么是SpringCloud?

pring Cloud 是一套基于 Spring Boot 的微服务架构开发工具集 ,它旨在简化分布式系统的开发,通过整合一系列成熟的分布式技术框架,提供了构建分布式系统所需的多种功能组件,实现了分布式系统开发中的诸多关键技术

2. 什么是微服务?

就是把应用分成一整套小服务模块,小服务可以抽离部署到多个服务器上,利用这些微型服务模块,构成大型的、模块的(可拆解的)、相互独立又有联系的应用。并且由于分离化,小服务构成的技术甚至语言也可以不同, 保持最 低限度的集中式管理。

简⽽⾔之:拒绝⼤型单体应⽤,基于业务边界进⾏服务微化拆分,各个服务独⽴部署运⾏。

3. 微服务应用与传统应用的差异

3.1. 架构设计

  • 传统应用:多为单体架构,所有功能模块集成在一个可执行程序中,各模块耦合度高 。比如早期的一些管理系统,用户管理、订单管理等功能模块都在同一程序,模块间通过代码直接调用 。
  • 微服务应用:采用分布式架构,将大型应用拆分成多个小服务,每个服务专注单一业务功能,服务间松耦合 。像电商系统,可拆分为用户服务、商品服务、订单服务等,通过接口通信 。

3.2. 开发模式

  • 传统应用:通常团队成员共同开发一个项目,技术栈相对统一 。开发时需考虑整个系统架构,牵一发而动全身,新功能或修改易引发连锁问题 。
  • 微服务应用:各服务可由不同小团队负责,能根据服务特点选合适技术栈,开发更灵活 。例如用户服务用 Java,商品服务用 Python 。开发人员聚焦单个服务,更易上手 。

3.3. 部署与运维

  • 传统应用:作为整体部署,更新或修复需重新部署整个应用,过程复杂、耗时长,期间服务可能中断 。
  • 微服务应用:各服务可独立部署、更新和扩展 。某个服务升级不影响其他服务,部署更灵活、高效,利于持续集成与交付 。比如流量高峰时,可单独扩展订单服务 。

3.4. 数据管理

  • 传统应用:一般使用单一数据库,不同模块数据可能关联,数据库结构变更困难,易影响多个模块 。
  • 微服务应用:每个服务可拥有独立数据库,能按需选合适数据库类型(如关系型、非关系型 ),数据管理更灵活,减少模块间数据耦合 。

3.5. 容错性

  • 传统应用:一处故障可能导致整个应用崩溃,因为模块耦合度高,故障易扩散 。
  • 微服务应用:单个服务故障通常不影响其他服务,通过服务降级、熔断等机制,可隔离故障,保障系统整体可用性 。

二、Nacos 注册/配置中心

1. 基础

1.1. 概述

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的⾸字⺟简称,⼀个更易于构 建云原⽣应⽤的动态服务发现、配置管理和服务管理平台

官⽹:Nacos官网| Nacos 配置中心 | Nacos 下载| Nacos 官方社区 | Nacos 官网

1.2. 安装

Windows:

  • 到官网下载.zip文件,然后解压

Docker:

  • docker run -d -p 8848:8848 -p 9848:9848 -e MODE=standalone --name nacos nac os/nacos-server:v2.4.3

1.3. 启动

2.*版本解压后直接进入bin目录然后使用终端指令即可,3.*版本比较麻烦

startup.cmd -m standalone 

如果无法使用也可以加个 .\

.\startup.cmd -m standalone 

2. 功能:注册中心

注册中心是为了协调联系各模块的相互联系,例如:服务注册与下线、服务发现、服务负载均衡

2.1. 导入依赖

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

2.2. 服务注册

服务注册到注册中心,可以通过注册中心调控客户对服务的访问

2.2.1. 配置nacos路径
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

这个是nacos的默认地址:本地回环ip + 端口8848

2.2.2. 启动

2.3. 服务发现

2.3.1. 配置主类(开启服务发现功能)
@EnableDiscoveryClient // 开启服务发现--核⼼注解

@SpringBootApplication
 public class OrderMainApplication {
 }
 public static void main(String[] args) {
 SpringApplication.run(OrderMainApplication.class, args);
 

也要配置好nacos路径才能使用其发现功能

2.3.2. 使用例
  1. 创建Order和Product两个服务并完成配置、连接等功能
  2. 在Order的ServiceImpl中导入 DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
  1. 创建getProductfromRemote方法
    /**
     * 通过注册中心获取Product的地址并发请求获取Product实例的方法
     */
    private Product getProductfromRemote(Long productId){
        // 1、通过注册中心调用商品服务的所有ip和port
        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");

        ServiceInstance instance = instances.get(0);// 这里可以做负载均衡,不过我直接使用了第1个服务

        // 2、创建服务url
        String url = "http://"+
                instance.getHost()+// 服务ip
                ":"
                +instance.getPort()// 服务端口
                +"/product"
                +productId;// 注册的第几个服务

        // 3、使用web发请求
        return restTemplate.getForObject(url, Product.class);
    }
  1. 调用getProductfromRemote方法
@Override
public Order createOrder(Long userId, Long productId) {
    Product product = getProductfromRemote(productId);
    Order order = new Order();
    order.setId(123456L);
    order.setTotalAmount(new BigDecimal("0"));
    order.setUserId(userId);
    order.setNickName("XX学院");
    order.setAddress("胡X");

    order.setProductList(Arrays.asList(product));//《---使用Product

    return order;
}
  1. 测试成功:

2.3.3. (扩展)@LoadBalanced实现负载均衡与服务发现

如果我们要在代码内实现负载均衡会有些麻烦,可以为web的RestTemplate标识@LoadBalanced注解,从而简单的实现负载均衡

  1. 在OrderConfig的RestTemplate标识@LoadBalanced注解
@Configuration
public class OrderConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  1. 新的服务发现方式
private Product getProductfromRemote(Long productId){

    // service-product是指微服务的名字
    String url = "http://service-product/product/"+productId;

    Product product = restTemplate.getForObject(url,Product.class);

    return product;
}
  1. 测试成功
2.3.4. (面试)注册中心宕机后,远程调用能成功吗?

3. 功能:配置中心

配置中心也是Nacos的一部分功能,开发者可以利用在Nacos中的配置集动态地修改微服务的配置,不用停机后更新配置

3.1. 导入与配置

3.1.1. 导入依赖
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

注册中心的artifactId项以discovery

3.1.2. 服务端文件配置

导了配置中心的依赖之后和注册中心一样,需要指定配置nacos地址,并且要指定该项目绑定的配置集(Nacos的云端配置文件)


# 指定配置中⼼地址
 spring.cloud.nacos.server-addr=localhost:8848
# 绑定配置文件,整理绑定了order服务的
 spring.config.import=nacos:service-order.properties

注意:如果导入了配置中心的依赖而不指定项云端绑定的配置集的话,nacos会检查出问题并报错终止启动,我们可以设置关闭配置集的启动检查 (防止子项目因为没有指定而报错)

  • spring.cloud.nacos.config.import-check.enabled=false
3.1.3. Nacos端配置集

  • DataID:配置集的名字,用来和服务绑定
  • Group:配置集的分组,用来精确匹配
  • 配置内容:和一般的配置文件一样可以用yaml、Properties等等配置,配置方式也一样

3.2. 连接和监听

3.2.1. 基础连接

完成以上配置后就能进行基本的使用了,访问Nacos的配置集

  1. OrderCollection:
@RefreshScope// 开启自动刷新功能的注解
@RestController
public class OrderCollection {

    @Value("${order.creater}")// 和项目中读取配置文件一样
    String creater;
    
    @GetMapping("/creater")
    public String getCreater(){
        return creater;
    }

}

注意:@RefreshScope 是开启自动刷新功能的注解

  1. 测试结果

3.2.2. 动态刷新

一般我们不使用基础的配置连接功能,而是把需要动态读取的配置封装到一个类中方便使用

@Component
@ConfigurationProperties(prefix = "order")// 指定类对应配置项,如order.xxx,可以直接用类中的xxx属性接收
@Data
public class OrderProperties {
    String creater;
}

注意:@ConfigurationProperties(prefix = "order")指定类对应配置项,如order.xxx,直接用类中的xxx属性接收

此时我们便可直接使用OrderProperties的get方法获取配置

3.2.3. 配置项监听器

顾名思义,可以在每次配置更新时获取/监听配置更新的信息

  1. 写在启动类中,格式比较死板
 @Bean
 ApplicationRunner applicationRunner(NacosConfigManager manager){
     return args -> {
         ConfigService configService = manager.getConfigService();
         configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {
             @Override
             public Executor getExecutor() {
                 return Executors.newFixedThreadPool(4);
             }
             @Override
             public void receiveConfigInfo(String configInfo) {
                 System.out.println("更改 = " + configInfo);
             }
         });
     };
 }
  1. 效果

3.3. 面试:Nacos配置集的优先级问题

3.3.1. Nacos配置集和项目配置文件谁的优先级高?

答:从配置中心的设计角度出发,如果项目的配置文件更优先,那么配置中心的管理与更新功能就相当于没有了。故,配置中心的配置集优先级更高

3.3.2. 如果我给一个项目imp导入多个配置集,那么此时的优先级呢?

答:还是从配置中心的设计角度出发,如果需要分块化写入配置而产生了多个不同的配置文件,那么非重复的配置共同生效;而因为我们可能多次更改,所以要以后更改的内容为正确。故,非重复配置叠加,重复就后导入优先

3.4. 数据隔离

在软件开发中会有多个环境,开发、测试、生产等等,而Nacos配置中心也为我们提供了环境数据隔离机制

3.4.1. 名称空间

所谓的名称空间其实就是和服务运行环境对接的分组,在名称空间中定义的配置集就对应了开发环境下的种种配置

配置与方法使用:

  1. 创建命名空间:

注意:id和名字最好是一样的,不要让系统自动生成id

  1. 和前面一样,需要注意选择自己创建的配置(这里设置了group分组)

  • data id:就是服务的配置文件中的绑定数据集名字
  • group:分组id
3.4.2. 基本使用

其实只是添加了个namespace配置项。(在开发中,我们可能还会给配置集分组,即使Data I重名了,也可以通过group分组来准确找到对应配置集)

  1. 指定名称空间***
spring.cloud.nacos.config.namespace=test // 这里指定的是test,可以通过改变name改
  1. 添加新的Nacos配置集
spring.config.import=\
  nacos:service-order.properties,\
  nacos:service-order-date.properties?group=IOS
  #下面这个

三、OpenFeign 远程调用

1. 基础配置

1.1. 导依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

1.2. 注解式启动

标识在主类/运行类上

@SpringBootApplication
@EnableFeignClients//<---这个
public class Application {
    public static void main(String[] args) {
     SpringApplication.run(Application.class, args);
    }
}

2. 基础使用:调用业务api

也就是远程调用项目的其他服务(在本地注册中心注册注册了的)(实现了负载均衡)

  1. 编写接口并使用@FeignClient的Value的属性来指定连接的服务(注册中心的服务名)
  2. 使用MVC的Mapping注解,标识远程调用的方式等等(Mapping原本是用来接收请求的,这里却用来发送请求)
  3. 在Mapping标识的抽象方法中指定参数列表和返回值
    1. 参数列表:和MVC反过来,@PathVariable("id")就是把该参数放到"id"的位置,同理如果用@RequestBody则把该数据转换成JSON并发出
    2. 返回值:用来封装远程调用的结果
@FeignClient(value = "service-product")
public interface ProductFeignClient {
    @GetMapping(value = "/productId/{id}")
    public Product getProductById(@PathVariable("id") Long productId);
}

MVC注解的两种用法:

  • 在@Controller上标识:接收如下参数
  • 在@FeignClient上标识:发送如下参数

3. 进阶使用:调用第三方api

Openfeign 除了能调用服务架构内业务服务的api外,在参数满足外部服务的要求时也能进行远程调用。(比如为软件需要一个获取日历的功能,就可以通过调用外部服务简单地完成)

  1. @FeignClient的value属性随便填,在url属性内填好服务的域名和端口
  2. 同上,编写远程调用方法(一定要仔细阅读连接服务的接口文档,保证参数完全正确)

使用方式:

@FeignClient(value = "weather-client", url = "http://aliv18.data.moji.com")
public interface WeatherFeignClient {
    @PostMapping("/whapi/json/alicityweather/condition")
    String getWeather(// 返回值作为JSON串
        @RequestHeader("Authorization") String auth,
        @RequestParam("token") String token,
        @RequestParam("cityId") String cityId);
}

4. 补充

4.1. 小技巧

由于OpenFeign对于MVC的适配性,我们可以直接把要调用的服务的Collection方法剔除方法体拿来用,可以完美适配

4.2. 面试:负载均衡的两种实现

问:客户端负载均衡与服务端负载均衡的区别是什么?

答:

  • 客户端负载均衡:是基于注册中心提供服务地址,由客户来选择使用哪个
  • 服务端负载均衡:就是网关,在网关中帮客户无感均衡地转发到服务群其中的一个

5. OpenFeign的进阶配置用法

5.1. 日志

使用流程:

  1. 对feign所在的包设置其下方法的日志级别以开启日志
logging.level.{feign所在的包的项目路径}:{日志级别}
logging.level.com.atguigu.order.feign: debug
  1. 配置feign的日志组件到一个配置类中
 @Bean
 Logger.Level feignLoggerLevel() {
     return Logger.Level.FULL;
 }

feign的日志级别和Spring的不一样:

  • NONE:不打印日志
  • BASIC: 仅打印请求方法、URL、状态码及执行时间
  • HEADERS:在BASIC基础上,打印请求和响应的Header
  • FULL:打印所有请求和响应的细节(URL、Header、Body、元数据等)

5.2. 超时控制

使用OpenFeign实现远程调用也是请求响应的过程,所以OpenFeign也有其自带的超时机制。开发中我们可以通过配置文件的方式配置超时机制

使用流程:

添加配置项,到服务配置目录

spring:
  cloud:
    openfeign:
      client:
        config:
          default: #默认配置
            logger-level: full #日志级别
            connect-timeout: 1500 #连接超时时间
            read-timeout: 1500
          service-product: #服务配置 (名字和服务名一般一致)
            connect-timeout: 3000
            read-timeout: 5000

注意:

  1. 如果没有指定具体配置时,以默认配置为主
  2. 在对于OpenFeign的具体服务时,配置名和服务名一般一致,如果feign的Client类中有contextId属性时,则以contextId属性为主

5.3. 重试机制

如果开启了重试机制:OpenFeign在远程调用时如果连接超时,会进入请求重试机制,如果请求不能在本服务配置的重试机制下完成连接,才会认为调用失败。我们可以通过注入组件或者配置文件来启动并配置重试机制

5.3.1. 具体机制

这是默认情况下的的配置:

  • period:100 -- 最大超时时间
  • duration:1 -- 初始超时时间
  • maxAttempts:5 -- 最多重试机会/次数
  • 总结 -- 当请求时间超过初始超时时间后会进入重试阶段,每次重试的时间都会扩大到上一次的1.5倍,当总时间超过最大超时时间或是次数用完,认为连接失败
5.3.2. 启用重试机制

在order 服务的OrderConfig中加入重试组件

@Configuration
public class OrderConfig {
    @Bean
    Retryer retryer() {
        return new Retryer.Default();// 这里用的是默认的重试机制,没有配
    }
}

5.4. 拦截器

同样也是因为OpenFeign的远程调用机制,要为其配置专门的拦截器。

流程:

添加拦截器到拦截器目录,使用@Component添加到容器(最好新开一个,这里的拦截器继承的是OpenFeign的拦截器)

@Component
public class OrderInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("U-I-D", UUID.randomUUID().toString());// 添加UID到请求头里面
    }
}

注:除了直接使用@Component添加拦截器,还可以在配置文件中指定。(必须使用两种方法中的一种)

5.5. Fallback兜底返回(需要熔断机制配合)

当调用失败时,我们可以通过Fallback机制为请求封装 假数据/缓存数据/默认数据。写在发送请求的客户端,实现之前远程调用功能的接口,这样同名方法就会在失败后执行会执行兜底方法

流程:

  1. 导入sentinel
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 配置sentinel为feign的熔断框架
feign:
  sentinel:
    enabled: true #开启
  1. 创建Fallback组件
    1. 要实现远程调用的接口
    2. 要把该实现类注册到容器中
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long productId) {
        System.out.println("兜底回调....");
        Product product = new Product();
        product.setId(productId);
        product.setPrice(new BigDecimal("0"));
        product.setProductName("未知商品");
        product.setNum(0);

        return product;
    }
}
  1. 给Client加上回调实现
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
    @GetMapping(value = "/productId/{id}")
    public Product getProductById(@PathVariable("id") Long productId);
}

注:
可以认为没有通过接口得到数据,于是到其实现类的同名方法中获取数据,很取巧也很有效

对应的远程调用如下:

  • product/{id} 就自然改换到本地服务中的Client上了

6. 总结

OpenFeign是一个封装完备的远程调用框架,配置完毕后只要简单的操作就能实现远程调用。如果还需要OpenFeign提供日志、超时、重连、拦截、兜底等机制,可以根据指示完成

四、Sentinel 资源流程控制

1. 基础配置

1.1. 下载Sentinel

sentinel-dashboard-1.8.8:登录 · 语雀

1.2. 启动Sentinel

启动指令:java -jar .\sentinel-dashboard-1.8.8.jar

控制台:

  • 账号密码一致为:sentinel
  • 路径为:http://localhost:8080/#/login

1.3. 配置文件

spring:
  cloud:
    sentinel:
    transport:
      dashboard: localhost:8080 #默认Sentinel访问路径
    eager: true #设置为项目启动就直接读取所有设置了的资源

注意:sentinel默认为懒加载,如果不设置true的话当资源被调用时才会被读取

1.4. 基础使用

  1. 为服务中某一Controller下的SerciceImpl标识@SentinelResource(value = "资源名")
    @SentinelResource(value = "createOrder")
    @Override
    public Order createOrder(Long userId, Long productId) {
        Product product = getProductfromRemote(productId);
        Order order = new Order();
        order.setId(123456L);
        order.setTotalAmount(new BigDecimal("0"));
        order.setUserId(userId);
        order.setNickName("XX学院"+productId);
        order.setAddress("胡X");

        order.setProductList(Arrays.asList(product));//《---使用Product

        return order;
    }
  1. 启动Sentinel与微服务
  2. 刷新以连接服务

  1. 添加流控规则
  2. 测试,发现快速刷新会导致访问失效

2. 异常处理

2.1. 流程图

  1. Sentinel规则优先触发顺序:Web接口-->@SentinelResource类
  2. 对应拦截器触发顺序:SentnelWebinterceptor类 --> blockHandler指定方法

2.2. (回忆)SpringBoot的异常处理器

@ResponseBody
@ControllerAdvice// 全局异常处理器集核心注解
public class GlobalExceptionHandler {
    @ExceptionHandler(异常类.class)// 类内部异常处理器
    public String XXXerror(对应异常类 error){
        /*
            异常处理逻辑
        */
        return 处理结果;
    }
}
  • 全局异常处理器集核心注解:@ControllerAdvice
  • 类内部异常处理器:@ExceptionHandler(异常类.class)

2.3. 对Web接口的兜底回调

当流程控制规则限制在Controller层级上的路径,会优先触发web接口对应的异常。如果不编写处理逻辑,会使用java默认的异常页来返回信息

做法:写一个BlockExceptionHandler的实现类,用handle方法处理请求

实现:

@Component
public class sentinelHandler implements BlockExceptionHandler {// <----重点
    
    ObjectMapper objectMapper = new ObjectMapper();
    
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       String resourceName, BlockException e) throws Exception {
        // 以下是一个处理流程:
        response.setStatus(429); //too many requests
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        R error = R.error(500, resourceName + " 被Sentinel限制了,原因:" + e.getClass());
        String json = objectMapper.writeValueAsString(error);
        writer.write(json);
        writer.flush();
        writer.close();
    }
}

2.4. blockHandler 兜底回调

当Sentinel的流控规则限制在由@SentinelResource注解标注的方法上时,可以使用两种方法为触发规则的请求进行兜底回调

做法:

  1. 为@SentinelResource添加参数blockHandler,参数指向为编写的Fallback处理方法
  2. Fallback处理方法可以在@SentinelResource方法的基础上添加BlockException exception参数,参数用来接收异常类

实现:

    @SentinelResource(value = "createOrder",blockHandler = "createOrderFallback")
    @Override
    public Order createOrder(Long userId, Long productId) {
        Product product = getProductfromRemote(productId);
        Order order = new Order();
        order.setId(123456L);
        order.setTotalAmount(new BigDecimal("0"));
        order.setUserId(userId);
        order.setNickName("XX学院"+productId);
        order.setAddress("胡X");

        order.setProductList(Arrays.asList(product));//《---使用Product

        return order;
    }

    // createOrder的兜底回调
    public Order createOrderBlockHandler(Long userId, Long productId, BlockException e){
        Order order =new Order();
        order.setId(0L);
        order.setTotalAmount(new BigDecimal("0"));
        order.setUserId(0L);
        order.setNickName("未知用户");
        order.setAddress("异常信息:"+e.getClass());

        return order;
    }

2.5. OpenFeign 远程调用兜底回调

feign:
  sentinel:
    enabled: true #开启
  1. 创建Fallback组件
    1. 要实现远程调用的接口
    2. 要把该实现类注册到容器中
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long productId) {
        System.out.println("兜底回调....");
        Product product = new Product();
        product.setId(productId);
        product.setPrice(new BigDecimal("0"));
        product.setProductName("未知商品");
        product.setNum(0);

        return product;
    }
}
  1. 给Client加上回调实现
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
    @GetMapping(value = "/productId/{id}")
    public Product getProductById(@PathVariable("id") Long productId);
}

2.6. (扩展)SphU硬编码

是以上3种方法的底层,不常用

2.7. (补充)blockHander与fallback兜底回调的区别

相同点:

都作为@SentinelResource注解指定的兜底回调方法

@SentinelResource(value = "createOrder",blockHandler = "createOrderBlockHander",fallback="createOrderFallback")
public Order createOrder(Long userId, Long productId) {
    .....
}

不同点:

  • blockHander指定的方法处理的是Sentinel内置的异常类
  • fallback指定的方法处理的是java的大异常类 Throwable
public Order createOrderBlockHandler(Long userId, Long productId, BlockException e){
    ...
}
public Order createOrderFallback(Long userId, Long productId, Throwable e){
    ...
}

3. 流控规则

3.1. 流控模式

在服务中,可能有各种各样的链式调用,为了控制调用链,我们引入流控模式

在编辑规则前,最好给项目配置web-context-unify: false

关闭共用上下文配置,因为后续的配置很多都是对于不同上下文时才能生效

# 关闭共用上下文配置
spring.cloud.sentinel.web-context-unify=false
3.1.1. 关联
3.1.2. 直接

只对编辑的资源进行限制控制:

3.1.3. 链路

对编辑的资源控制,以限制其链路上的入口资源:(比如秒杀功能需要调用创建功能,“秒杀”在调用链上在“创建”前,我们限制“创建”的入口若为“秒杀”,则限制其流量)

关闭共用上下文配置

3.1.4. 关联

对编辑的资源控制,限制可能关联的资源:(比如对于一个资源进行不同优先级操作时,如果优先级高的操作流量过大,为了稳定我们关联限制其他操作,图例是读>写操作)

关闭共用上下文配置

3.2. 流控效果

3.2.1. 快速失败

当超出阈值后直接失败报流控异常:

3.2.2. Warn Up 热启动

初始情况下只有单机阈值的30%能通过,在预热时长内逐渐增长直到达到阈值:(先低功耗,后预热启动)

3.2.3. 匀速排队

控制每秒请求通过数,排队进入,超时则报流控异常

4. 融断规则

融断降级:快速及时切断请求链,防止服务雪崩

4.1. 断路器

  • 闭合状态下:请求能正常通过
  • 打开状态下:请求无法正常通过
  • 半开状态下:允许一个请求通过,根据返回结果选择闭合或是打开

4.2. 熔断策略

  1. 慢调用比例:慢调用在统计时长内所有请求中所占比例,超过该比例则断路器开启
    • 慢调用:超出系统设置统计时长的请求
  1. 异常比例:结果为错误的请求在统计时长内所有请求中所占比例,超过该比例则断路器开启
  2. 异常数:在统计时长内所有请求中结果为错误的请求个数,超过该数量则断路器开启

5. 热点规则

流控规则的精细化版本,可以针对请求的具体参数进行流控,而不仅仅是资源位置

5.1. 热点参数限流

  • 参数索引:标识方法的参数列表中的序列索引,从0开始

注意:受限于对参数的要求,热点规则只能对于被精确设置为资源的方法有效,不能直接限制在Web层级上

5.2. 限流例外项

在添加了热点规则后可以编辑其高级项,添加例外参数列表

五、Gateway 网关

绝大部分请求相关的功能网关都能完成

1. 路由配置

网关就是管理路由

1.1. 模块创建与依赖导入

  • 由于网关是独立的功能,常用独立的服务项目作为网关,需要额外导入nacos注册中心和配置中心
  • 在新版本中,需要额外引入SpringCloud的负载均衡依赖,网关才能实现负载均衡
<!--        网关-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--        注册中心-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--        额外引入SpringCloud的负载均衡依赖-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

1.2. 基础使用

配置网关参数:在一般的SpringCloud服务项配置之外,网关需要专门编写配置项来管理其他服务:服务的连接路径、转发路径....

  • uri:请求接入网关转发后需要前往的服务,目的地
  • predicates:请求前往指定服务、目的地需要满足的规则,可以多个(取并集)
  • order:优先级(数值越小优先级越,默认优先级为自上而下)
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  #------------网关配置
    gateway:
      routes: #
        - id: order
          order: 0 
          uri: lb://service-order 
          predicates:
            - Path=/api/order/** #路径规则

        - id: product
          order: 1
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
            
server:
  port: 80
            

注意:

网关的路径规则匹配优先级很重要,如果兜底的响应被设置为高优先级,可能导致一些深层路径无法匹配:如localhost/**、localhost/product/**

2. 断言规则 predicates

断言是否匹配,才能路由到指定服务

2.1. 断言类型 Query

2.2. 短写法

spring:
  cloud:
    gateway:
      routes:
        - id: order
          order: 0
          uri: lb://service-order
          
          predicates: # 断言
            - Path=/api/order/**

2.3. 长写法(断言工厂)

短写法的本质,name(断言规则类型) + 参数列表 + ...

spring:
  cloud:
    gateway:
      routes:
        - id: product
          order: 1
          uri: lb://service-product
          
          predicates: # 断言
            - name:  Path
              args:
                  patterns: /api/product/**
                  matchTrailingSlash: true 

matchTrailingSlash:

  • true:结尾可以多个"/"
  • false:结尾多了个"/"就不归我管了

2.4. 自定义断言工厂

3. 过滤器 filter

顾名思义,给请求进行加工的工具

3.1. 过滤器类型

和断言规则一样,gateway的过滤器也用[name + 参数] 那一套

3.2. 基础使用

也和断言一样,有长短写法之分

使用例:不再在服务的Web接口指定前缀Mapping--"/order/**",使用拦截器自动去除前缀,便于服务内部连接

spring:
  cloud:
    gateway:
      routes:
        - id: order
          order: 0
          uri: lb://service-order 
          predicates: 
            - Path=/api/order/**
          filters: # 拦截器
            - RewriterPath=/api/order/(?<segment>.*), /${segment}

路径重写:RewriterPath = {原路径正则表达式 },{新路径正则表达式}

3.3. 默认过滤器

无论什么,只要过网关就操作

spring:
  cloud:
    gateway:
      routes:
        - id: order
          order: 0
          uri: lb://service-order 
          predicates: 
            - Path=/api/order/**
          filters: 
            - RewriterPath=/api/order/(?<segment>.*), /${segment}
      default-filters: # 默认拦截器
        - AddResponseHeader=X-Response-def, 666

3.4. 自定义过滤器

跟javaWeb的过滤器类似:重写方法、前后置处理、注入容器

例:为请求响应一个jwt令牌

@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    ServerHttpResponse response = exchange.getResponse();
                    HttpHeaders headers = response.getHeaders();
                    String value = config.getValue();
                    if ("uuid".equalsIgnoreCase(value)) {
                        value = UUID.randomUUID().toString();
                    }
                    if ("jwt".equalsIgnoreCase(value)) {
                        value = "My JWT";
                    }

                    headers.add(config.getName(), value);
                }));
            }
        };
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值