前言
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以通过
RPC
相互调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证 100% 可用(第一个任务没执行完,第二又进来...),如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,
Servlet
容器的线程资源有限会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩” 效应。
为了解决这个问题,业界提出了熔断器模型。
一、Sentinel 入门
1.1 什么是 Sentinel ?
Sentinel (分布式系统的**流量防卫兵**) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从**流量控制、熔断降级、系统负载保护**等多个维度来保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即 突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒 级数据, 甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等 应用容器。
1.2 微服务集成 Sentinel
为微服务集成Sentinel非常简单, 只需要加入Sentinel的依赖即可 ,在生产者模块(basketball)的pom.xml中加入下面依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
1.3 安装Sentinel控制台
Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。
1、下载jar包,解压到文件夹 👉 https://github.com/alibaba/Sentinel/releases
2、启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=9999 -Dcsp.sentinel.dashboard.server=localhost:9999 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar
#参考1
java -jar sentinel-dashboard-1.8.4.jar --server.port=8080
#参考2
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar
3、添加配置,在服务里面加入有关控制台的配置
#sentinel流控
sentinel:
transport:
port: 8719
dashboard: localhost:9999
#提前加载开关
eager: true
4、通过浏览器访问localhost:9999 进入控制台 ( 默认用户名密码是 sentinel/sentinel )
控制台的使用原理:
Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上,即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口调用微服务中的监控程序获取微服务的各种信息。
二、Jmeter 压力测试工具
2.1 Jmeter 介绍
Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具,用于对软件做压力测试。
Jmeter主要元件
- 测试计划:是使用 JMeter 进行测试的起点,它是其它 JMeter测试元件的容器
- 线程组:代表一定数量的用户,它可以用来模拟用户并发发送请求。实际的请求内容在Sampler中定义,它被线程组包含。
- 配置元件:维护Sampler需要的配置信息,并根据实际的需要修改请求的内容。
- 前置处理器:负责在请求之前工作,常用来修改请求的设置
- 定时器:负责定义请求之间的延迟间隔。
- 取样器(Sampler):是性能测试中向服务器发送请求,记录响应信息、响应时间的最小单元,如:HTTP Request Sampler、FTP Request Sample、TCP Request Sample、JDBC Request Sampler等,每一种不同类型的sampler 可以根据设置的参数向服务器发出不同类型的请求。
- 后置处理器:负责在请求之后工作,常用获取返回的值。
- 断言:用来判断请求响应的结果是否如用户所期望的。
- 监听器:负责收集测试结果,同时确定结果显示的方式。
- 逻辑控制器:可以自定义JMeter发送请求的行为逻辑,它与Sampler结合使用可以模拟复杂的请求序列。
2.2 Jmeter 安装
下载地址:Apache JMeter - Apache JMeter™
解压即可用,打开bin目录下的jmeter.bat进行启动:
启动后自动进入该页面:
中文设置、大小、外观:
2.3 接口测试
1、添加线程组
2、添加取样器
3、添加监听器->察看结果树
三、Sentinel 使用
3.1 限流规则
首先了解一下高并发性能指标:
QPS,每秒查询
Queries Per Second是衡量信息检索系统(例如搜索引擎或数据库)在一秒钟内接收到的搜索流量的一种常见度量。该术语在任何请求-响应系统中都得到更广泛的使用,更正确地称为**每秒请求数**(RPS:Request Per Second)。
TPS,每秒事务
** Transactions Per Second的缩写,也就是事务数/秒**。它是软件测试结果的测量单位。一个事务是指一个客户端向服务器发送请求然后服务器做出响应的过程。客户端在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
RT,响应时间
执行一个**请求从开始到最后收到响应数据所花费的总体时间**,即从客户端发起请求到收到服务器响应结果的时间。该请求可以是任何东西,从内存获取,磁盘IO,复杂的数据库查询或加载完整的网页。
Concurrency,并发数
并发数是指系统同时能处理的请求数量,这个也反应了系统的负载能力。 并发意味着可以同时进行多个处理。并发在现代编程中无处不在,网络中有多台计算机同时存在,一台计算机上同时运行着多个应用程序。
吞吐量
系统的吞吐量(承压能力)和处理对CPU的消耗、外部接口、IO等因素紧密关联。单个处理请求对CPU消耗越高,外部系统接口、IO速度越慢,系统吞吐能力越低,反之越高。
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
- 快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
- warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
- 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
3.1.1 warm up(预热模式)
warm up也叫预热模式,是应对服务冷启动的一种方案。
请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3
** 大白话来讲,刚开始把 阈值调低,不要让过多的请求访问服务器,导致冲垮服务器,先让服务器一点一点处理,再慢慢加量。经典的例子:一个好久没运动的人,你刚开始让他跑10圈,他可能会累死,但是你给他一个预热时间,比如 第一天跑 2圈,第三天跑 3 圈,第四天跑4圈,以此类推...**
例如:我设置QPS的threshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3。请求到达阈值时就会等5秒慢慢升高阈值
Jmeter 设置线程组
如图:我设置了一秒执行一次,总共十次。第一次阈值是3...慢慢变多
3.1.2 排队等待
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。**而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。**后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
测试:
3.1.3 关联
当关联的资源达到阈值的时候,就限流自己。
eg:当与 **/chang **关联的资源 **/tiao **达到阈值后,就限流 **/chang **自己
当执行 **/tiao 接口到达了阈值时,/chang **才能执行:
3.1.4 链路
链路流控指针对指定接口进行限流,一般用在针对访问比较多的接口方法进行限流,这里做了一个简单的示例:
配置热点接口 必须使用@SentinelResource声明资源名
代码示例:
package com.example.basketball.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private OrderServiceImpl orderService;
@RequestMapping("/chang")
public String chang() {
orderService.message();
return "🎤";
}
@RequestMapping("/tiao")
public String tiao() {
orderService.message();
return "🪂";
}
@Service
public class OrderServiceImpl {
@SentinelResource("message")
public void message() {
System.out.println("kun");
}
}
}
添加yml配置:
添加链路给 **/chang 限流 **
3.2 熔断规则
Sentinel 提供了三种主要的熔断策略,用于在系统遇到异常情况时保护服务和资源。这些熔断策略分别是:
**1、慢调用比例 (Slow Request Ratio)**:
- 这种策略是基于调用响应时间来进行熔断的。当资源的每个请求的平均响应时间超过预设的阈值,并且在一个统计窗口期内,慢调用的比例超过设定的比例(例如20%),系统就会触发熔断操作。
- 熔断条件通常包括最小请求数(防止请求量过小导致误熔断)和慢调用比例阈值。
- 这种策略适用于响应时间敏感的服务,可以有效防止因为部分慢请求导致的系统雪崩。
1.调用:一个请求发送到服务器,服务器给与响应,一个响应就是一个调用。
2.RT:响应时间,指执行一个请求从开始到最后收到响应数据所花费的总体时间。
3.慢调用:当调用的时间(响应的实际时间)>设置的RT的时,这个调用叫做慢调用。
4.慢调用比例:在所以调用中,慢调用占有实际的比例,= 慢调用次数 / 调用次数
5.比例阈值:自己设定的 , 慢调用次数 / 调用次数 = 比例阈值统计时长与最小请求数:
这两个参数的关系在于,熔断的触发不仅依赖于异常比例或慢调用比例是否超过阈值,还依赖于在统计时长内是否有足够的请求量(即是否达到最小请求数)来保证统计结果的可靠性。如果统计时长内的请求量没有达到最小请求数,即使异常比例或慢调用比例超过了阈值,熔断器也不会触发,因为样本量太小可能无法代表系统的真实状态。
** 上面的熔断规则表示说: 访问/chang资源时,如果在1000毫秒内,请求数量超过5,并且这些数量中超过400毫秒的请求数量的比例超过0.2则熔断5秒,5秒后进入半开状态,如果半开状态中有一个请求小于400毫秒,则取消熔断,否则继续熔断。**
案例代码:
@RequestMapping("/message2")
public String message2() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "message2";
}
**2、异常比例 (Error Ratio)**:
- 异常比例策略是基于异常发生的比例来进行熔断的。当资源在一个统计窗口期内的异常比例超过预设的阈值(例如20%),系统就会触发熔断。
- 这种策略通常结合最小请求量来使用,以避免因为样本量太小而误触发熔断。
- 这种策略适用于那些对异常率敏感的服务,可以在异常率升高时及时进行熔断,保护系统。
第1步: 首先模拟一个异常
第2步: 设置异常比例大于0.2
int i = 0;
@RequestMapping("/message3")
public String message3() {
i++;
//异常比例为0.333
if (i % 3 == 0){
throw new RuntimeException();
}
return "message3";
}
**3、异常数 (Error Count)**:
- 异常数策略是根据一定时间窗口内的异常总数来触发熔断。当在统计窗口期内的异常总数超过预设的阈值(例如20个),无论请求总数多少,都会触发熔断。
- 这种策略不依赖于请求总数,而是直接关注异常总量。
- 适用于那些异常数绝对值更重要的场景,比如一些关键资源或操作,一旦异常数量超过阈值,立即熔断以防止系统进一步恶化。
这些熔断策略可以根据实际业务和服务特性进行选择和配置。通过合理的熔断策略,Sentinel 能有效地防止服务因过高的负载或错误而变得不稳定,保护系统免受进一步的损害。
3.3 服务降级
Feign整合Sentinel:
第1步: 引入sentinel的依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
第2步: 在配置文件中开启Feign对Sentinel的支持
server:
port: 8081
spring:
cloud:
nacos:
server-addr: localhost:8848
#sentinel流控
sentinel:
transport:
port: 8719
dashboard: localhost:9999
#提前加载开关
eager: true
application:
name: ikun
# 开启feign对sentinel的支持
feign:
sentinel:
enabled: true
第3步: 创建容错类
package com.example.ikun.service.impl;
import com.example.ikun.serice.FeginKunService;
import org.springframework.stereotype.Component;
/**
* 容错类要求必须实现被容错的接口,并为每个方法实现容错方案
* @author 云村小威
* @create 2024-01-12 0:44
*/
@Component
public class FeginKunServiceImpl implements FeginKunService {
@Override
public String getByPath(String account) {
/*
* 解决方案
* ...
* */
return "嘿嘿嘿";
}
}
第4步: 为被容器的接口指定容错类
package com.example.ikun.service;
import com.example.ikun.serice.impl.FeginKunServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 连接生产者 Controller
*
* @author 云村小威
* @create 2024-01-06 15:40
*/
@FeignClient(value = "basketball", fallback = FeginKunServiceImpl.class) //连接服务器名称
public interface FeginKunService {
@RequestMapping("/kun/{account}")
public String getByPath(@PathVariable(value = "account") String account);
}
第五步:编写controller接口
package com.example.ikun.controller;
import com.example.ikun.service.FeginKunService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private FeginKunService kunService;
@RequestMapping("/play01")
public String play() {
return kunService.getByPath("姬霓太美");
}
}
第六步:
package com.example.ikun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient //开启服务发现
@EnableFeignClients(basePackages = "com.example.ikun.serice") //开启Feigin远程
public class IkunApplication {
public static void main(String[] args) {
SpringApplication.run(IkunApplication.class, args);
}
}
测试
启动两个服务:
调用远程basketball服务执行service接口方法:
将basketball服务关闭, 将调用容错解决方案。
版权归原作者 云村小威 所有, 如有侵权,请联系我们删除。