news 2026/5/1 7:34:44

SpringCloud —— Sentinel详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringCloud —— Sentinel详解

一、前言

在前面的课程中,我们可以发现一个问题,就是即使是低耦合的微服务架构,在微服务和微服务之间依然是有耦合的,比如相互之间的远程调用,这就导致一个微服务出现异常(不一定是崩溃,也有可能是请求处理速度变慢,响应时间过长),另一个微服务也会受到影响,同时,在调用链上的其他微服务也有可能受到影响,所以这是一个安全问题,我们在这里会给出几种方式用于解决这个问题。

二、Sentinel

Sentinel是一个中间件,可以通过流量控制、线程隔离、服务熔断的方式来降低异常发生后的风险。

1.快速入门

首先要在官网下载Sentinel,然后解压,在无中文的目录中使用命令行打开,命令如下:

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

从这里就可以看出,我们是直接在windows上启动的Sentinel,但是Sentinel的客户端是使用SpringBoot开发的,最终启动后会占用tomcat默认的8080端口,在同一台电脑中,我们的项目网关端口也是8080,所以端口会冲突。

解决办法有两个,第一是用上述配置,在启动时更改端口号为8090,第二是在虚拟机中的docker容器中启动,这样就只会占用宿主机的8080端口。

这里我们选用前一种,比较快捷直观,而且由于之前我把项目也部署到docker容器中了,所以即使将Sentinel放到docker中,也有可能会出现端口冲突的问题。

启动后通过访问8090端口可以看到Sentinel的页面:

向需要进行微服务保护的微服务导入依赖:

<!--sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>

配置Sentinel:

spring: cloud: sentinel: transport: dashboard: localhost:8090 # sentinel的控制台地址 http-method-specify: true # 是否设置请求方式作为资源名称

在访问一次购物车页面后就可以看得到购物车微服务的服务名称了:

2.流量限制

首先要了解请求被处理的流程:先建立连接,然后再排队,最后被处理

为什么要限制流量?

因为流量会影响请求的响应速度,导致用户使用时会很“卡”,如果流量不被限制,并且变得太大时,SpringBoot默认的Tomcat线程池中的工作线程会被占满,新的请求就只能去排队了,而等待队列如果也被占满了,将会导致新请求被拒绝,这样用户就会完全无法访问网页了。

那为什么会影响响应速度呢?

因为请求超过处理能力后,会自动排队,这就导致许多请求无法被及时处理,也就是:

正常:响应时长=请求被处理的时长

过多:响应时长=请求被处理的时长+排队等待的时长

我们这里为了模拟服务响应超时,我们修改代码,让请求处理时长增加,从而尽可能让更多线程去排队:

@ApiOperation("根据id批量查询商品") @GetMapping public List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids){ //TODO 模拟业务延迟 ThreadUtil.sleep(500); return itemService.queryItemByIds(ids); }

同时我们将Tomcat的最大线程处理数降低,等待队列的线程数降低,同时将最大连接数降低。

tomcat: threads: max: 25 accept-count: 25 max-connections: 100

这里限制这三个参数就是为了限制请求的处理,也就是降低Tomcat处理请求的能力,让Tomcat线程池尽快饱和,从而模拟大用户量的场景。

接下来,正式开始流量限制,首先先设置流量限制,我们这里配置访问购物车的流量限制:

然后我们使用测试软件jmeter模拟大用户量的情况:

开始测试,我们可以看到,异常率接近百分之四十,这是因为我们限制的流量是每秒6个,而测试案例是每秒十个(1000/100),所以平均每秒会有4个请求被抛出异常429。

在测试过程中,我们自己也访问购物车来测试,发现延迟在522ms,这说明我们访问购物车的时间是没变的,所以流量限制的好处:通过限制请求处理量,来保障被处理请求的质量。

3.线程隔离

线程隔离是指:在线程池中分配指定量线程给某个微服务,如果这个微服务异常,只会影响被分配的几个线程,其他线程不被影响,从而让其他和本微服务无耦合的微服务(不远程调用异常微服务的其他微服务)正常运行。

购物车中有一个功能是增加商品数量,这个是通过远程调用实现的,和购物车查询无关,如果我们将购物车查询功能线程隔离,那按道理说,即使查询异常,也不会影响增加商品的功能。

接下来尝试模拟这个场景:

一样的设置步骤,对于查询购物车的GET请求,在流控中选择并发线程数,我们这里设置为5:

这里我们设置测试的配置:

开始测试后,我们尝试增加商品到购物车,我们会发现,购物车是查不出来了,但是我们增加商品数量是不被影响的。

4.Fallback

我们先前的购物车微服务会远程调用ItemClient,如果远程调用失败就会抛异常,从而影响购物车的功能,Fallback指的是回退,其实就是一个备用方案,如果ItemClient远程调用异常,那么这个时候就会自动进入回退流程,重新创建一个远程调用对象,这个新对象中会重写方法,但是这个方法里面就没有逻辑了,直接返回空,并且报错误日志,这样就可以正常返回了,只是返回的是空,对于用户来讲,这个就不会过多影响其他功能,而报错的日志又对开发人员的维护起到提示作用。

先模拟商品微服务大用户量,方便后续让远程调用异常,从而逼它走fallback策略:

@ApiOperation("根据id批量查询商品") @GetMapping public List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids){ //TODO 模拟业务延迟 ThreadUtil.sleep(500); return itemService.queryItemByIds(ids); }

首先需要创建一个工厂类,用于生成降级代理对象(替代方案)。

@Slf4j public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> { @Override public ItemClient create(Throwable cause) { return new ItemClient() { @Override public List<ItemDTO> queryItemByIds(Collection<Long> ids) { log.error("查询商品失败:",cause); return CollUtils.emptyList(); } @Override public void deductStock(List<OrderDetailDTO> items) { log.error("扣减商品库存失败:",cause); throw new RuntimeException(cause); } }; } }

然后我们需要将工厂写到配置类中,在远程调用异常的时候生效。

public class DefaultFeignConfig { /** * 配置日志级别 * * @return */ @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } @Bean public RequestInterceptor userInfoRequestInterceptor() { return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { Long userId = UserContext.getUser(); if (userId != null) { requestTemplate.header("user-info", userId.toString()); } } }; } @Bean public ItemClientFallbackFactory itemClientFallbackFactory(){ return new ItemClientFallbackFactory(); } }

最后将配置类中的工厂方法配置到远程调用接口中去:

@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class) public interface ItemClient { @GetMapping("/items") List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); @PutMapping("/items/stock/deduct") void deductStock(@RequestBody List<OrderDetailDTO> items); }

这样,我们就可以看到这个远程调用的资源名了,这就意味着我们可以单独对远程调用进行线程隔离了(远程调用和微服务共享一个线程池,所以是有隔离的必要的),隔离后,这个远程调用服务异常就不会对购物车微服务本身产生影响了,同时由于远程调用服务异常,会执行fallback,于是会返回空响应,从而让购物车其他功能不受影响。

我们对购物车进行测试,发现不会出现异常了

同时将响应空值回去。

5.服务熔断

服务熔断通常会和fallback策略一起使用,服务熔断指当远程调用发生异常时,就直接拒绝远程调用的请求了,这个时候远程调用会走fallback策略(此时熔断器处于关闭状态),同时,熔断器会在指定时间后尝试放行一次请求到已经异常的远程调用服务(半开状态)

如果这次请求无异常了,熔断器就会重新启用正常远程调用服务(熔断器恢复打开)。

如果这次请求依旧有异常,就继续等待指定时间,然后再次尝试。

我们对远程调用配置熔断规则,直接在Sentinel中配置:

开始测试,可以看到,这个时候商品远程调用已经异常了,所以执行的是fallback策略:

停止测试后,会发现又恢复了,因为熔断器进入半开状态后又向商品远程调用发出了少量请求,这次由于没有大流量测试了,所以直接通过了,所以熔断器恢复打开状态,远程调用也就不再执行fallback策略了,服务恢复。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 7:19:27

解决鸿蒙PC命令行编译 macOS 上 cp 命令参数冲突问题

解决鸿蒙PC命令行编译 macOS 上 cp 命令参数冲突问题 问题背景 在 macOS 系统上编译 OpenHarmony PC 命令行 项目时&#xff0c;运行构建脚本 build.sh 遇到了以下错误&#xff1a; cp: the -R and -r options may not be specified together这个错误导致后续的构建流程无法…

作者头像 李华
网站建设 2026/4/24 18:38:09

COMSOL模拟相场锂枝晶:相场+浓度+电势的联合仿真

comsol模拟相场锂枝晶—相场浓度电势。 此案例为文献复现&#xff0c;含视频讲解。「玩COMSOL的老司机都懂&#xff0c;搞锂枝晶模拟最酸爽的就是相场、浓度、电势三场耦合。今天这个案例直接扒了文献里的核心算法&#xff0c;带大家手把手搭个能跑出枝晶分叉的模型&#xff0c…

作者头像 李华
网站建设 2026/4/27 2:34:46

AI大模型训练师:破解AI与人类沟通障碍,高薪岗位全解析

文章介绍了AI大模型训练师这一新兴职业&#xff0c;他们负责解决AI与人类之间的沟通障碍&#xff0c;将模糊需求转化为AI能理解的清晰指令。由于市场供需失衡&#xff0c;该职业薪资优厚&#xff0c;部分年薪可达45万。训练师工作包括数据管理、模型训练、评估迭代和业务落地四…

作者头像 李华
网站建设 2026/4/18 9:18:11

测试思维创新:从技术执行到价值创造的范式转移

在人工智能与云原生技术重塑软件研发格局的2025年&#xff0c;软件测试正站在历史性的转折点。传统以缺陷发现为核心的测试思维已难以应对敏捷开发、持续交付与复杂系统稳定性的多重挑战。本文旨在探讨测试思维创新的核心维度&#xff0c;提出从“质量控制”到“质量赋能”的思…

作者头像 李华
网站建设 2026/4/16 17:30:16

从业务痛点出发 - 为什么需要多智能体协作?

想象这样一个场景&#xff1a;周一早上9点&#xff0c;某制造企业的项目经理李明收到一个紧急任务——公司决定开发一套新的ERP系统&#xff0c;预算300万元&#xff0c;需要12个月完成。李明深吸一口气&#xff0c;开始了他漫长的一天&#xff1a;9:30-10:30&#xff1a;找技术…

作者头像 李华
网站建设 2026/4/23 14:47:56

深入理解 Java 线程池 ThreadPoolExecutor:原理、实战与调优

在 Java 并发编程领域&#xff0c;线程池是提升系统性能、优化资源利用率的核心组件。无论是高并发的 Web 服务&#xff0c;还是后台批处理任务&#xff0c;线程池都扮演着至关重要的角色。本文将以 JDK 原生线程池ThreadPoolExecutor为核心&#xff0c;从原理剖析、参数详解、…

作者头像 李华