SpringCloud高级篇 - 微服务保护-2

简介: SpringCloud高级篇 - 微服务保护-2

.2.线程隔离(舱壁模式)

3.2.1.线程隔离的实现方式

线程隔离有两种方式实现:

  • 线程池隔离
  • 信号量隔离(Sentinel默认采用)



如图:

线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果



信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。

两者的优缺点:

扇出:接受一个,发出多个

扇出是指一个节点向其它多个节点发送消息或任务的过程。在计算机领域中,扇出通常用于描述分布式系统中任务被分发到多个工作节点上执行的情况。


在分布式系统中,通常有一个节点(例如负载均衡器)负责将任务分配给多个工作节点。这个过程就是扇出。当任务被分配到工作节点后,工作节点就会独立地执行任务并将结果返回给调度节点。这个过程我们称之为扇入。


扇出能够提高系统的处理能力和吞吐量,因为任务可以同时被多个工作节点处理,从而快速完成任务。但是,扇出也会增加系统的复杂性和维护难度,因为需要对任务进行分发和调度,并确保所有工作节点都能够正确地接收和处理任务。


扇出在很多场景中都很常见,例如分布式计算、流式处理、消息队列等。这些技术都需要扇出来实现任务的分发和并行处理。

3.2.2.sentinel的线程隔离

用法说明

在添加限流规则时,可以选择两种阈值类型:

  • QPS:就是每秒的请求数,在快速入门中已经演示过


线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现线程隔离(舱壁模式)。


案例需求:给 order-service服务中的UserClient的查询用户接口设置流控规则,线程数不能超过 2。然后利用jemeter测试。


1)配置隔离规则选择feign接口后面的流控按钮:

填写表单:

2)Jmeter测试


选择《阈值类型-线程数<2》:

一次发生10个请求,有较大概率并发线程数超过2,而超出的请求会走之前定义的失败降级逻辑。

查看运行结果:

发现虽然结果都是通过了,不过部分请求得到的响应是降级返回的null信息。


3.2.3.总结


线程隔离的两种手段是?

  • 信号量隔离
  • 线程池隔离

信号量隔离的特点是?

  • 基于计数器模式,简单,开销小

线程池隔离的特点是?

  • 基于线程池模式,有额外开销,但隔离控制更强


3.3.熔断降级


熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。


断路器控制熔断和放行是通过状态机来完成的:

状态机包括三个状态:



closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态

open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态

half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。

请求成功:则切换到closed状态

请求失败:则切换到open状态

断路器熔断策略有三种:慢调用、异常比例、异常数

3.3.1.慢调用

慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。



例如:

解读:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。

案例


需求:给 UserClient的查询用户接口设置降级规则,慢调用的RT阈值为50ms,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5


1)设置慢调用

修改user-service中的/user/{id}这个接口的业务。通过休眠模拟一个延迟时间:

34a59524cfebce050a278aa7fb4f2495.png此时,orderId=101的订单,关联的是id为1的用户,调用时长为60ms:

orderId=102的订单,关联的是id为2的用户,调用时长为非常短;

2)设置熔断规则


下面,给feign接口设置降级规则:

规则:

超过50ms的请求都会被认为是慢请求

3)测试


在浏览器访问:http://localhost:8088/order/101,快速刷新5次,可以发现:



c88892af9cd28a73ee38da2507b1d37a.png

触发了熔断,请求时长缩短至5ms,快速失败了,并且走降级逻辑,返回的null


在浏览器访问:http://localhost:8088/order/102,竟然也被熔断了:


fa8c2e855a60a39809c75bbdfdf301c6.png


3.3.2.异常比例、异常数异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。


例如,一个异常比例设置:



b9be39b07d968bfa5c1b5261e0cf2eec.png

解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.4,则触发熔断。


一个异常数设置:



308a475ade95f482929e165fdb56e5b4.png

解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于2次,则触发熔断。

案例

需求:给 UserClient的查询用户接口设置降级规则,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5s

1)设置异常请求


首先,修改user-service中的/user/{id}这个接口的业务。手动抛出异常,以触发异常比例的熔断:

也就是说,id 为 2时,就会触发异常


2)设置熔断规则

下面,给feign接口设置降级规则:

规则:


fa6c46c1344db70364a57d03630eabc2.png


在5次请求中,只要异常比例超过0.4,也就是有2次以上的异常,就会触发熔断。

3)测试

在浏览器快速访问:http://localhost:8088/order/102,快速刷新5次,触发熔断:

此时,我们去访问本来应该正常的103:

3.4 总结




Sentinel熔断降级的策略有哪些?

  • 慢调用比例:超过指定时长的调用为慢调用,统计单位时长内慢调用的比例,超过阈值则熔断
  • 异常比例:统计单位时长内异常调用的比例,超过阈值则熔断
  • 异常数:统计单位时长内异常调用的次数,超过阈值则熔断


4.授权规则

授权规则可以对请求方来源做判断和控制。

4.1.授权规则

4.1.1.基本规则



授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。


白名单:来源(origin)在白名单内的调用者允许访问


黑名单:来源(origin)在黑名单内的调用者不允许访问


点击左侧菜单的授权,可以看到授权规则:

1084e04de714fc2460f28f497f7bbdd4.png



资源名:就是受保护的资源,例如/order/{orderId}


  • 资源名:就是受保护的资源,例如/order/{orderId}
  • 流控应用:是来源者的名单,
  • 如果是勾选白名单,则名单中的来源被许可访问。
  • 如果是勾选黑名单,则名单中的来源被禁止访问。


比如:

我们允许请求从gateway到order-service,不允许浏览器访问order-service,那么白名单中就要填写网关的来源名称(origin)



4.1.2.如何获取origin

Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。

public interface RequestOriginParser {
    /**
     * 从请求request对象中获取origin,获取方式自定义
     */
    String parseOrigin(HttpServletRequest request);
}


这个方法的作用就是从request对象中,获取请求者的origin值并返回。


默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。


因此,我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。


例如order-service服务中,我们定义一个RequestOriginParser的实现类:

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 1.获取请求头
        String origin = request.getHeader("origin");
        // 2.非空判断
        if (StringUtils.isEmpty(origin)) {
            origin = "blank";
        }
        return origin;
    }
}


我们会尝试从request-header中获取origin值。

4.1.3.给网关添加请求头

既然获取请求origin的方式是从reques-header中获取origin值,我们必须让所有从gateway路由到微服务的请求都带上origin头


这个需要利用之前学习的一个GatewayFilter来实现,AddRequestHeaderGatewayFilter。

修改gateway服务中的application.yml,添加一个defaultFilter:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway
      routes:


这样,从gateway路由的所有请求都会带上origin头,值为gateway。而从其它地方到达微服务的请求则没有这个头。

4.1.4.配置授权规则

接下来,我们添加一个授权规则,放行origin值为gateway的请求。

配置如下:

现在,我们直接跳过网关,访问order-service服务:


a4eb77b7e9f23dfea3149dd5ad39e6f7.png

4.2.自定义异常结果

默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。异常结果都是flow limmiting(限流)。这样不够友好,无法得知是限流还是降级还是授权拦截。



4.2.1.异常类型

而如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:

public interface BlockExceptionHandler {
    /**
     * 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
     */
    void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}


这个方法有三个参数:


HttpServletRequest request:request对象

HttpServletResponse response:response对象

BlockException e:被sentinel拦截时抛出的异常

这里的BlockException包含多个不同的子类:

异常 说明
FlowException 限流异常
ParamFlowException 热点参数限流的异常
DegradeException 降级异常
AuthorityException 授权规则异常
SystemBlockException 系统规则异常


4.2.2.自定义异常处理

下面,我们就在order-service定义一个自定义异常处理类:

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;
        if (e instanceof FlowException) {
            msg = "请求被限流了";
        } else if (e instanceof ParamFlowException) {
            msg = "请求被热点参数限流";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级了";
        } else if (e instanceof AuthorityException) {
            msg = "没有权限访问";
            status = 401;
        }
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}


重启测试,在不同场景下,会返回不同的异常消息.

限流:

授权拦截时:

5.规则持久化

现在,sentinel的所有规则都是内存存储,重启后所有规则都会丢失。在生产环境下,我们必须确保这些规则的持久化,避免丢失。

5.1.规则管理模式


规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式:

  • 原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
  • pull模式
  • push模式

5.1.1.pull模式


pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。

缺点:时效性问题,Sentinel更新之后不会实时更新自己的Sentinel



5.1.2.push模式

push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

5.2.实现push模式 - Sentinel 规则持久化


5.2.1 修改order-service服务

修改OrderService,让其监听Nacos中的sentinel规则配置。

具体步骤如下:


1.引入依赖

在order-service中引入sentinel监听nacos的依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>


2.配置nacos地址

在order-service中的application.yml文件配置nacos地址及监听的配置信息:

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow # 还可以是:degrade、authority、param-flow


5.2.1 修改sentinel-dashboard源码

SentinelDashboard默认不支持nacos的持久化,需要修改源码。

1. 解压

解压课前资料中的sentinel源码包:


ef9c543c31909cc9e9058c7fa712e9e4.png



然后并用IDEA打开这个项目,结构如下:

2. 修改nacos依赖


在sentinel-dashboard源码的pom文件中,nacos的依赖默认的scope是test,只能在测试时使用,这里要去除:

将sentinel-datasource-nacos依赖的scope去掉:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>


3. 添加nacos支持

在sentinel-dashboard的test包下,已经编写了对nacos的支持,我们需要将其拷贝到main下。

4. 修改nacos地址

然后,还需要修改测试代码中的NacosConfig类:

修改其中的nacos地址,让其读取application.properties中的配置:



在sentinel-dashboard的application.properties中添加nacos地址配置:

nacos.addr=localhost:8848
5. 配置nacos数据源

另外,还需要修改com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2类:

让我们添加的Nacos数据源生效:



08d9d4e71c0e83b589f7d35eb85f1923.png



6. 修改前端页面

接下来,还要修改前端页面,添加一个支持nacos的菜单。

修改src/main/webapp/resources/app/scripts/directives/sidebar/目录下的sidebar.html文件:


40603076482899993ed1169f0327dba2.png


将其中的这部分注释打开:

修改其中的文本:

7. 重新编译、打包项目

运行IDEA中的maven插件,编译和打包修改好的Sentinel-Dashboard:

8.启动

启动方式跟官方一样:

java -jar sentinel-dashboard.jar

如果要修改nacos地址,需要添加参数:

java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar

资料中的sentinel源码包:

[外链图片转存中…(img-R3wrKaMR-1685249577484)]

然后并用IDEA打开这个项目,结构如下:

[外链图片转存中…(img-qCqYeHfA-1685249577486)]


2. 修改nacos依赖

在sentinel-dashboard源码的pom文件中,nacos的依赖默认的scope是test,只能在测试时使用,这里要去除:

[外链图片转存中…(img-FCTF9zK3-1685249577487)]


将sentinel-datasource-nacos依赖的scope去掉:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>


3. 添加nacos支持

在sentinel-dashboard的test包下,已经编写了对nacos的支持,我们需要将其拷贝到main下。

[外链图片转存中…(img-IKoBFvxv-1685249577488)]

4. 修改nacos地址


然后,还需要修改测试代码中的NacosConfig类:


[外链图片转存中…(img-8O80bf6g-1685249577489)]


修改其中的nacos地址,让其读取application.properties中的配置:


[外链图片转存中…(img-eqFsVt6x-1685249577490)]在sentinel-dashboard的application.properties中添加nacos地址配置:

nacos.addr=localhost:8848
5. 配置nacos数据源

另外,还需要修改com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2类:


[外链图片转存中…(img-ZbwtQV0z-1685249577491)]


让我们添加的Nacos数据源生效:


[外链图片转存中…(img-IapgzBn3-1685249577492)]

6. 修改前端页面


接下来,还要修改前端页面,添加一个支持nacos的菜单。


修改src/main/webapp/resources/app/scripts/directives/sidebar/目录下的sidebar.html文件:


[外链图片转存中…(img-7czoz8i1-1685249577493)]


将其中的这部分注释打开:


[外链图片转存中…(img-lmMYB2Xw-1685249577494)]


修改其中的文本:


[外链图片转存中…(img-kMfrMxZN-1685249577496)]

7. 重新编译、打包项目

运行IDEA中的maven插件,编译和打包修改好的Sentinel-Dashboard:

8.启动

启动方式跟官方一样:

java -jar sentinel-dashboard.jar


如果要修改nacos地址,需要添加参数:

java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar


目录
相关文章
|
1月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
57 2
|
1月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
1月前
|
JSON Java 数据格式
【微服务】SpringCloud之Feign远程调用
本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
83 1
|
1月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
1月前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
44 1
|
2月前
|
前端开发 API 微服务
SpringCloud微服务之间使用Feign调用不通情况举例
SpringCloud微服务之间使用Feign调用不通情况举例
522 2
|
1月前
|
负载均衡 算法 Nacos
SpringCloud 微服务nacos和eureka
SpringCloud 微服务nacos和eureka
63 0
|
2月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
126 1
|
1月前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
129 1
Springcloud Alibaba + jdk17+nacos 项目实践