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


目录
相关文章
|
4天前
|
消息中间件 Java RocketMQ
Spring Cloud RocketMQ:构建可靠消息驱动的微服务架构
【4月更文挑战第28天】消息队列在微服务架构中扮演着至关重要的角色,能够实现服务之间的解耦、异步通信以及数据分发。Spring Cloud RocketMQ作为Apache RocketMQ的Spring Cloud集成,为微服务架构提供了可靠的消息传输机制。
16 1
|
7天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
8天前
|
运维 监控 Java
SpringCloud&认识微服务
SpringCloud&认识微服务
13 0
|
15天前
|
人工智能 监控 安全
Java+Spring Cloud +Vue+UniApp微服务智慧工地云平台源码
视频监控系统、人员实名制与分账制管理系统、车辆管理系统、环境监测系统、大型设备监测(龙门吊、塔吊、升降机、卸料平台等)、用电监测系统、基坑监测系统、AI算法分析(安全帽佩戴、火焰识别、周界报警、人员聚众报警、升降机超载报警)、安全培训、设备监测。
20 4
|
16天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
25天前
|
SpringCloudAlibaba Java 数据库
SpringCloud Alibaba微服务 -- Seata的原理和使用
SpringCloud Alibaba微服务 -- Seata的原理和使用
|
25天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
2天前
|
存储 运维 负载均衡
探索微服务架构下的服务治理
【4月更文挑战第30天】 在当今软件开发领域,微服务架构已经成为了解决复杂系统问题的重要技术手段。随着微服务的广泛应用,如何有效管理与治理这些分散的服务成为了开发和维护的关键。本文将探讨在微服务架构下,实现高效服务治理的策略与实践,重点分析服务发现、配置管理、负载均衡和故障处理等核心要素,旨在为读者提供一套系统的服务治理思路。
|
1天前
|
Kubernetes API 开发者
构建高效微服务架构:后端开发的新范式
【5月更文挑战第2天】 随着现代软件开发的演进,传统的单体应用已难以满足快速变化的业务需求和敏捷开发的挑战。本文探讨了如何通过构建高效的微服务架构来提升后端开发的灵活性、可维护性和扩展性。我们将深入分析微服务的核心组件,包括服务拆分、容器化、API网关和持续集成/持续部署(CI/CD)等关键技术,并讨论它们如何共同作用以支持复杂的业务场景和云原生应用的需求。
7 1
|
1天前
|
负载均衡 Java API
构建高效微服务架构:API网关与服务熔断策略
【5月更文挑战第2天】 在微服务架构中,确保系统的高可用性与灵活性是至关重要的。本文将深入探讨如何通过实施有效的API网关和设计合理的服务熔断机制来提升分布式系统的鲁棒性。我们将分析API网关的核心职责,包括请求路由、负载均衡、认证授权以及限流控制,并讨论如何利用熔断器模式防止故障传播,维护系统的整体稳定性。文章还将介绍一些实用的技术和工具,如Netflix Zuul、Spring Cloud Gateway以及Hystrix,以帮助开发者构建一个可靠且高效的微服务环境。