当前位置: 首页 > news >正文

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策略了,服务恢复。

http://www.cnnetsun.cn/news/137951.html

相关文章:

  • 公考日记7
  • 火电一次调频、自抗扰调频及群智能算法智能调频在MATLAB/Simulink中的应用
  • 科研实验室温湿度监控新范式:以太网 POE 技术全场景解决方案
  • RV1126 NO.57:ROCKX+RV1126人脸识别推流项目之读取人脸图片并把特征值保存到sqlite3数据库
  • 探索SAR ADC:45nm工艺下的高速高精度设计
  • 【小增长技术团队东哥分享】Electron vs Electron-Vite vs Electron-Egg:桌面端开发到底该选谁?
  • 测试价值的量化评估:从成本中心到价值证明的路径探索
  • 测试领导力:在敏捷洪流中筑造质量堤坝
  • C++常用设计模式
  • Spring Boot 自动配置深度解析:原理、实战与源码追踪
  • 无代码解决方案:破解企业数字化转型效率困局
  • SAM (Segment Anything Model):万物皆可分割-k学长深度学习专栏
  • Mysql 报错 “Public Key Retrieval is not allowed”
  • 熊市中最适用的公式==底部建仓
  • 100G双光口网卡技术解析:Intel E810-CAM2方案的性能与应用突破
  • BioSIM抗人组蛋白H1抗体SIM0385:广泛应用于表观遗传学、染色质结构分析等领域
  • 智慧灯杆数字孪生系统:“多杆合一“技术实现
  • SCI一稿多投会不会被发现?
  • RUI Builder-图形化UI设计-工程范例
  • win10 - 删除非法命名的文件夹的方法
  • 必看!2025年单北斗GNSS形变监测高口碑产品排行榜
  • 【计网】网络分层模型和http协议
  • Kotaemon在华为云上的部署实践:全流程记录
  • 校园便利平台|基于springboot + vue校园便利平台系统(源码+数据库+文档)
  • 38、Linux 脚本编程:bc 计算器、数组与特殊技巧
  • 揭秘高亮车灯升级2025年值得推荐的TOP8车灯产品
  • WSL2 / Ubuntu 下用 SDKMAN 管理多版本 Java(项目级切换,真香)
  • 从“幻觉”到“诚实”:OpenAI 如何重新定义大模型的不靠谱问题
  • 高精度宽频段VG7050CDN压控晶体振荡器(VCXO),适用于通信与GPS设备等
  • 重塑艺术“原罪”?Nano Banana Pro 引入数字水印与归属协议:谷歌要给 AI 生图打上“DNA”标签?