目录
????????在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以通过 RPC
相互调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证 100% 可用(第一个任务没执行完,第二又进来...),如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet
容器的线程资源有限会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩” 效应。????????
为了解决这个问题,业界提出了熔断器模型。
????????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 等 应用容器。
????????为微服务集成Sentinel非常简单, 只需要加入Sentinel的依赖即可 ,在生产者模块(basketball)的pom.xml中加入下面依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
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编写的程序。我们需要将我们的微服务程序注册到控制台上,即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口调用微服务中的监控程序获取微服务的各种信息。
Apache JMeter 是 Apache 组织基于?Java?开发的压力测试工具,用于对软件做压力测试。?
Jmeter主要元件
下载地址:Apache JMeter - Apache JMeter?
解压即可用,打开bin目录下的jmeter.bat进行启动:
?启动后自动进入该页面:
中文设置、大小、外观:
?
1、添加线程组
?2、添加取样器
?3、添加监听器->察看结果树
首先了解一下高并发性能指标:
- QPS,每秒查询
????????Queries Per Second是衡量信息检索系统(例如搜索引擎或数据库)在一秒钟内接收到的搜索流量的一种常见度量。该术语在任何请求-响应系统中都得到更广泛的使用,更正确地称为每秒请求数(RPS:Request Per Second)。
- TPS,每秒事务
????????Transactions Per Second的缩写,也就是事务数/秒。它是软件测试结果的测量单位。一个事务是指一个客户端向服务器发送请求然后服务器做出响应的过程。客户端在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
- RT,响应时间
????????执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间。该请求可以是任何东西,从内存获取,磁盘IO,复杂的数据库查询或加载完整的网页。
- Concurrency,并发数
????????并发数是指系统同时能处理的请求数量,这个也反应了系统的负载能力。
????????并发意味着可以同时进行多个处理。并发在现代编程中无处不在,网络中有多台计算机同时存在,一台计算机上同时运行着多个应用程序。
- 吞吐量
????????系统的吞吐量(承压能力)和处理对CPU的消耗、外部接口、IO等因素紧密关联。单个处理请求对CPU消耗越高,外部系统接口、IO速度越慢,系统吞吐能力越低,反之越高。
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
warm up也叫预热模式,是应对服务冷启动的一种方案。
请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3
????????大白话来讲,刚开始把 阈值调低,不要让过多的请求访问服务器,导致冲垮服务器,先让服务器一点一点处理,再慢慢加量。经典的例子:一个好久没运动的人,你刚开始让他跑10圈,他可能会累死,但是你给他一个预热时间,比如 第一天跑 2圈,第三天跑 3 圈,第四天跑4圈,以此类推...
例如:我设置QPS的threshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3。请求到达阈值时就会等5秒慢慢升高阈值
Jmeter 设置线程组
如图:我设置了一秒执行一次,总共十次。第一次阈值是3...慢慢变多
????????当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
?测试:
当关联的资源达到阈值的时候,就限流自己。
eg:当与?/chang?关联的资源 /tiao?达到阈值后,就限流 /chang?自己
当执行?/tiao 接口到达了阈值时,/chang 才能执行:
????????链路流控指针对指定接口进行限流,一般用在针对访问比较多的接口方法进行限流,这里做了一个简单的示例:
配置热点接口?必须使用@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 限流?
Sentinel 提供了三种主要的熔断策略,用于在系统遇到异常情况时保护服务和资源。这些熔断策略分别是:
1、慢调用比例 (Slow Request Ratio):
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):
第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):
????????这些熔断策略可以根据实际业务和服务特性进行选择和配置。通过合理的熔断策略,Sentinel 能有效地防止服务因过高的负载或错误而变得不稳定,保护系统免受进一步的损害。
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服务关闭, 将调用容错解决方案。