十一、配置持久化第十七章 SpringCloud Alibaba入门简介
一.为什么使用Alibaba
1.因为:spring netflix进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now#spring-cloud-netflix-projects-entering-maintenance-mode
什么是维护模式:spring cloud团队将不会再向模块添加新功能,我们将修复block级别的bug以及安全问题,我们也会考虑并审查社区的小型pull request。我们打算继续支持这些模块,知道Greenwich版本被普遍采用至少一年
SpringCloud Netflix将不再开发新的组件
以下spring cloud netflix模块和响应的starter将进入维护模式:
- spring-cloud-netflix-archaius
- spring-cloud-netflix-hystrix-contract
- spring-cloud-netflix-hystrix-dashboard
- spring-cloud-netflix-hystrix-stream
- spring-cloud-netflix-hystrix
- spring-cloud-netflix-ribbon
- spring-cloud-netflix-turbine-stream
- spring-cloud-netflix-turbine
- spring-cloud-netflix-zuul
我们都知道SpringCloud版本迭代是比较快的,因而出现了很多重大ISSUE都还来不及Flix就又推另一个RELEASE了。进入维护模式意思就是目前以及以后一段时间SpingCloud Netflix提供的报务和功能就这么多了,不在开发新的组件和功能了。以后将以雏护和Merge分支Full Request为主。
2.所以:spring cloud alibaba来了
https://spring.io/projects/spring-cloud-alibaba
2018.10.31,spring cloud Alibaba正式入驻了Spring Cloud官方孵化器,并在Maven中央库发布了第一个版本
主要功能:
- 服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
- 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
组件:
- Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
- Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
- RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
- Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
- Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
- Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
- Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
二.如何使用?
- Spring官网:https://spring.io/projects/spring-cloud-alibaba
- GitHub:https://github.com/alibaba/spring-cloud-alibaba
- GitHub中文文档:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
- Spring Cloud Alibaba参考文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
按照官网一步一步来
https://spring.io/projects/spring-cloud-alibaba#learn
新建父工程 spring-cloud-alibaba
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
三.版本对照
我们按照最新的学习
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
---|---|---|
2021.0.4.0* | Spring Cloud 2021.0.4 | 2.6.11 |
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
2021.1 | Spring Cloud 2020.0.1 | 2.4.2 |
第十九章 服务注册和配置中心
一、是什么
官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html
1.nacos(NAming COnfiguration Service):服务注册和配置中心
Nacos = Eureka + Config + Bus
替代Eureka做服务注册中心
替代Config做服务配置中心
github地址: https://github.com/alibaba/Nacos
Nacos地址: https://nacos.io/zh-cn/
三.与其他注册中心对比
服务注册与服务框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP高可用 | 支持 | 低(2.x版本闭源) |
Zookeeper | CP一致 | 支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP+CP | 支持 | 高 |
四.切换
nacos可以切换 AP 和 CP ,可使用如下命令切换成CP模式
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
五.下载
1.下载地址: https://github.com/alibaba/nacos/releases
直接下载网址:https://github.com/alibaba/nacos/releases/download/2.0.4/nacos-server-2.0.4.zip
2.下载压缩包以后解压,进入bin目录,打开dos窗口,执行startup命令启动它。
startup.cmd -m standalone
3.端口号8848
4.可访问 : http://localhost:8848/nacos/index.html 地址,默认账号密码都是nacos
六.注册中心
急速构建 server你只需允许可执行文件即可,不用自己创建了
1.服务提供者1
新建模块 nacos-provider8000
pom.xml
<!--SpringCloud Alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml
server:
port: 8000
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
主启动类
@EnableDiscoveryClient
controller 显示server.port
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Value("${server.port}")
Integer port;
@GetMapping("/findById/{id}")
public String findById(@PathVariable("id")Integer id){
//业务逻辑
return "nacos provider.port:"+port+"|id:"+id;
}
}
http://localhost:8848/nacos/index.html
2.服务提供者2
新建模块 nacos-provider:8001
和上面类似
创建不同的端口号,但是服务名都要死nacos-provider,方便我们进行负载均衡
3.服务调用方
新建模块 nacos-conusmer9000 其他准备一样就是controller不一样
导入了nacos,2020年后alibaba的nacos不能再使用netfix的ribbon
(1).导入pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId> <!--别加版本,去父工程内进行版本管理-->
</dependency>
父工程的pom
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
(2) 配置文件下禁用ribbon
server:
port: 9000
spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
loadbalancer:
ribbon:
enabled: false
(3)主启动类上加
(4) Resttemplate的配置类
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
(5)controller
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/add/{id}")
public String add(@PathVariable("id")int id){
String url="http://nacos-provider/goods/findById/"+id;
String result = restTemplate.getForObject(url, String.class);
return result;
}
}
测试:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZaI9Que-1668410728234)(https://gitee.com/wu-xiapeng/images/raw/master/images/202210301231194.png)]
.
.
**4.整合feign **
找实例端口 + 负载均衡
(1) pom,xml导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2) 主启动类上添加@EnableFeignClients,激活feign
(3) 新建feign接口
@FeignClient("nacos-provider")
public interface GoodsFeign {
@GetMapping("/goods/findById/{id}")
public String findById(@PathVariable("id")Integer id);
}
(4) 重写controller
@RestController
@RequestMapping("order")
public class OrderController {
// @Autowired
// private RestTemplate restTemplate;
@Autowired
private GoodsFeign goodsFeign;
@GetMapping("/add/{id}")
public String add(@PathVariable("id")int id){
String url="http://nacos-provider/goods/findById/"+id;
String result = goodsFeign.findById(id);
return result;
}
}
测试:http://localhost:9000/order/add/5
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PVak8HzD-1668410728237)(https://gitee.com/wu-xiapeng/images/raw/master/images/202210301231197.png)].
.
七.服务注册中心对比
1.Nacos生态图
2.Nacos切换到AP模式和CP模式,默认AP A:可用性 C:一致性 P:分区容错性
切换到CP :
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
3.对比其他注册中心
八. 配置中心功能
1.创建nacos-client然后测试配置中心
(1)pom.xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--SpringCloud Alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
(2)application.yml
spring:
profiles:
active: dev #表示开发环境
bootstrap.yml
# nacos配置
server:
port: 7777
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务注册中心
config:
server-addr: localhost:8848 #nacos作为配置中心
file-extension: yaml #指定yaml格式的配置
# Data Id: ${spring.application.name}-#{spring.profiles.active}.${file-extension}
# nacos-config-client-dev.yaml
(3)启动类上加@EnableDiscoveryClient
(4) controller
@RestController
@RequestMapping("/config")
@RefreshScope //支持nacos进行动态刷新
public class ConfigClientController {
@Value("${username}")
private String username;
@GetMapping("/info")
public String getInfo(){
return username;
}
}
username随便测试一下:
.
改下username:
.
2.配置文件分类
.
问题1:实际开发中,通常一个系统会准备dev/test/prod环境。如何保证环境启动时服务能正确读取nacos上相应环境的配置文件?
答:namespace区分
问题2:一个大型分布式微服务系统有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境。那怎么对微服务配置进行管理呢?
答: 用group把不同的微服务划分到同一个分组里面去
Service就是微服务,一个service可以包含多个cluster集群,nacos默认cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
dgn方案
1.DataId (nacos的文件名)
指定spring.profile.active和配置文件的dataID来使不太环境下读取不同的配置 配置空间+配置分组+新建dev和test两个dataid:就是创建-后不同的两个文件名nacos-config-client-dev.yaml、nacos-config-client-test.yaml 通过IDEA里的spring.profile.active属性就能进行多环境下配置文件的读取。
2.Group
可以在IDEA内配置GROUP
3.Namespace
在IDEA的yml中指定命名空间namespace: e79f32ec-974a-4e90-9086-3ae5c64db7e3
spring: application: name: nacos-config-client cloud: nacos: discovery: server-addr: localhost:8848 #Nacos服务注册中心地址 config: server-addr: localhost:8848 #Nacos作为配置中心地址 file-extension: yaml #指定yaml格式的配置 group: BJ_GROUP namespace: e79f32ec-974a-4e90-9086-3ae5c64db7e3
九、集群和持久化配置(重要)
数据我们要进行持久化,所以我们需要将他保存到mysql
1、Nacos部署环境
Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。
以下文档中提及的VIP,网卡等所有网络相关概念均处于内部网络环境。
2、Nacos支持三种部署模式
- 单机模式 - 用于测试和单机试用。
- 集群模式- 用于生产环境,确保高可用。
- 多集群模式 - 用于多数据中心场景。
3、单机模式下运行Nacos
Linux/Unix/Mac: startup.sh -m standalone
windows : startup.cmd -m standalone
4、单机模式支持mysql
(1)由于支持的sql版本要5.6.5+,打开navcat查询
.
(2) 新建数据库:nacos_dectest
(3)初始化mysql数据库,数据库初始化文件:conf/mysql-schema.sql 拉到那个数据库,执行sql语句即可
.
(4)修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码,
告诉nacos不用内嵌的数据库,现在用mysql
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=1234
再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql
新添加namspace和配置,查看数据库
5.集群部署
SLB:其实就是负载均衡
模拟出三台机器的nacos,这里用到虚拟机
(1) 起虚拟机
.
(28条消息) VM上安装的CentOS7 NAT模式不能上网怎么解决(怎么静态分配IP)(操作,命令都详细列出)__Lichee_的博客-CSDN博客
(2) nocos(linux)的安装包安装到虚拟机上
地址: https://github.com/alibaba/nacos/releases
然后在虚拟机上解压:
tar -zxvf nacos-server-2.1.2.tar.gz
cd nacos/bin
(3) 配置集群配置文件
在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点)
我的虚拟机的ssh端口为:192.168.61.129
192.168.61.129:6666
192.168.61.129:7777
192.168.61.129:8888
(4)确定数据源
第二十章 SpringCloud Alibaba Sentinel实现熔断与限流
一.简介
Sentinelt充当哨兵,替代hystrix
Sentinel : 分布式系统的流量防卫兵
Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持
A和B服务都引入sentinel,只要A,B进行远程调用,就可以被sentiment进行远程监控
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。相当于hystrix的运维监控
二.Sentinel下载
1.下载:https://github.com/alibaba/Sentinel/releases
2.运行:java -jar E:\springboot2\softwore\nacos\sentinel-dashboard-1.8.5.jar
3.localhost:8080 进入控制台 账密:sentinel11
三.初始化演示工程
.
A服务如果要将数据放入dashbord的话就需要访问8719,而外部用户要访问这个控制台才是访问8080,这要区分开
cloudalibaba-sentinel-service8000
(1)导入pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(2) yml
server:
port: 8000
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719
(3)主启动类
(4) controller 随便测试一下
@RestController
public class FlowLimitController {
@GetMapping("/test1")
public String test1()
{
return "test1";
}
@GetMapping("/test2")
public String test2()
{
return "test2";
}
}
(5)启动8080然后刷新sentinel后台页面(因为sentinel采用懒加载策略,所以需要调用服务后才在后台显示) 在浏览器分别输入,然后刷新sentinel后台页面: http://localhost:8000/test1 http://localhost:8000/test2
四.流量监控
-
- 资源名:唯一名称,默认请求路径 - 针对来源:sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源) - 阈值类型/单机值:
- 是否集群:不需要集群
- 流控模式:
- 直接:api达到限流条件时,直接限流。分为QPS和线程数
- 关联:当关联的资到阈值时,就限流自己。别人惹事,自己买单
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 流控效果:
- 快速失败:直接抛异常 Blocked by Sentinel (flow limiting)
- warm up:根据codeFactor(冷加载因子,默认3)的值,从阈值codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以均匀的速度通过,阈值类型必须设置为QPS,否则无效
刷新比较快就会被限制!
(2) 并发直接
@GetMapping("/test1")
public String test1()
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "test1";
}
浏览器打开两个tab进行快速访问
(3) 关联失败
a.首先a去关联b,且设置阈值100
b.b设置阈值1,等下模拟b出错
c.模拟postman 500ms访问b,让b失败
(4) qps链路
我们的service只对A进行限流
我们可以对不同controller来的调用service方法限流,更加精细化
a. GoodService
@Service
public class GoodsService {
@SentinelResource("queryGoods")
public void query(){
System.out.println("查询商品");
}
}
b. controller
@RestController
public class FlowLimitController {
@Autowired
private GoodsService goodsService;
@GetMapping("/test1")
public String test1()
{
goodsService.query();
return "test1";
}
@GetMapping("/test2")
public String test2()
{
goodsService.query();
return "test2";
}
}
c.配置文件
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,导致链路限流不生效。 从1.7.0版本开始,官方在CommonFilter引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context,将其配置为false即可根据不同的URL进行链路限流。
连续刷A,A会报错,连续刷B,B不会
(5)warm up
它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
Warm Up(
RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
比如凌晨的时候访问量是0,到了早上7点有秒杀活动,访问量突增到10w,系统容易崩坏,所以需要进行冷启动
上面的意思就是: 刚开始,原先的冷启动因子是3(10/3)阈值, 经过5s后才到达10阈值
(6) 匀速排队
当我每秒收到10w个订单的时候,我只能以1w/s的速度进行处理,那么其他订单需要等待而不是退回
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
匀速排队模式不支持QPS > 1000 的场景
连续刷新B看一下打印时间即可
五.熔断降级
(1) 概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。
例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在**客户端(调用端)**进行配置。
(2) 熔断策略
Sentinel提供以下几种熔断策略:类似hystrix的熔断机制
1.慢调用策略:选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。
当单位统计时长内请求数目 大于 设置的最小请求数目,并且慢调用的比例大于阈值(访问次数),则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入 探测恢复状态(半开状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断(复活
!),若大于设置的慢调用 RT 则会再次被熔断。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aH91945z-1668410728260)(https://gitee.com/wu-xiapeng/images/raw/master/images/202210301231234.png)]
测试:
a.
@GetMapping("test3/{id}")
public String test3(@PathVariable("id")Integer id){
if(id==10){
//复杂的业务逻辑
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return "test3: "+id;
}
b.
上面的意思:是最大响应时间是500ms, 1000ms内有10次请求,有5次(10*0.5)挂掉了,那么就进行熔断,当1s过后就进入半开状态
连续刷新id=10
2.异常调用
和上面的慢调用比例类似
a.
@GetMapping("test4/{id}")
public String test4(@PathVariable("id")Integer id){
if(id==10){
int i=1/0;
}
return "test4";
}
b.
上面的解释是:1000ms内发送5次请求,如果只要有一次失败(0.2*5),就打开熔断,熔断到半开为1s
c. 连续刷新id=10,当异常一直错后会进入熔断,1s后进入半开后重试正常后复活
3.异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
a.还是test4
b.
上面的意思是,单位时间内我发送5次请求,如果最少有3次请求异常的话进入熔断
c.连续刷新id=10的地址
六.热点参数 限流
https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
1.概述
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用LRU策略统计最近常访问的热点参数,结合令牌桶算法进行参数级别的流控,热点参数限流支持集群模式、
2.基本使用
(1)导入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.6</version>
</dependency>
(2) controller层
@GetMapping("/order")
@SentinelResource(value="hotKeys") //设置资源名为hotKeys,默认的话就是/order
public String order(@RequestParam("goodsId") String goodsId,@RequestParam("userId") String userId){
//业务逻辑
return "用户下单成功!";
}
参数索引:0是第一个参数,1是第二个,1s内只能执行1次,否则限流
测试:http://localhost:8000/order?goodsId=1&userId=10 注意我们限流的是goodsId
1s一次刷新是正常的,但是你1s多次刷新就会报错
但是我们不应该返回这样的一个页面给用户,所以在这方面我们需要改进、
仿照htstrix的fallback,返回一个我们定义的异常对象,类似于以前的responseResult
@GetMapping("/order")
@SentinelResource(value="hotKeys",blockHandler = "block_order")
public String order(@RequestParam(value = "goodsId",required = false) String goodsId,@RequestParam(value = "userId",required = false) String userId){
//业务逻辑
return "用户下单成功!";
}
public String block_order(@RequestParam(value = "goodsId",required = false) String goodsId,
@RequestParam(value = "userId",required = false) String userId,
BlockException ex){
//记录日志
//logger.error(ex.getMessage());
return "用户下单失败,请稍后重试!";
}
这次报错返回我们自定义的fallback是热点限流的报错,而不是我们程序的报错,如果我们程序的报错的话,那么还是这个页面
(3) 例外情况-----针对具体的key(例外项)
秒杀情况下,某个参数goodsId=100,最新手机id,单独设置阈值1000.
上面的解释是:对于0号索引元素(也就是goodsId,String类型),一般情况下对于所有的goodsId,1s限流10000次,但是这里的话如果goodsId=100的话,我们每1s限流1次
测试:范围goodsid=1可以随便刷,访问goodsid=100的话只能1次/s
七.系统自适应限流
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
上面的解释是:所有的请求的qps为1 ,无论哪个请求,都是只能1次/s
八[email protected]
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。
@SentinelResource
注解包含以下属性:
-
:资源名称,必需项(不能为空)value
-
:entry 类型,可选项(默认为entryType
)EntryType.OUT
-
/blockHandler
:blockHandlerClass
对应处理blockHandler
的函数名称,可选项。blockHandler 函数访问范围需要是BlockException
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为public
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定BlockException
为对应的类的blockHandlerClass
对象,注意对应的函数必需为 static 函数,否则无法解析。Class
-
/fallback
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了fallbackClass
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:exceptionsToIgnore
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个
类型的参数用于接收对应的异常。Throwable
- fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
为对应的类的fallbackClass
对象,注意对应的函数必需为 static 函数,否则无法解析。Class
-
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了defaultFallback
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:exceptionsToIgnore
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要为空,或者可以额外多一个
类型的参数用于接收对应的异常。Throwable
- defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
为对应的类的fallbackClass
对象,注意对应的函数必需为 static 函数,否则无法解析。Class
-
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。exceptionsToIgnore
1.8.0 版本开始,
defaultFallback
支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常( DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出
BlockException
时只会进入
blockHandler
处理逻辑。若未配置
blockHandler
、
fallback
和
defaultFallback
,则被限流降级时会将
BlockException
直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层
UndeclaredThrowableException
)。
对于每个接口方法controller我们都写fallback兜底方法的话,那么代码量就会增多,blockHandle—>blockhandleClass
演示一下:
a.controller
@GetMapping("/ronsume")
@SentinelResource(value="resume",blockHandlerClass = CommonBlockHandle.class,blockHandler = "handlerBlock")
public String consume(@RequestParam(value = "goodsId",required = false) String goodsId,@RequestParam(value = "userId",required = false) String userId){
//业务逻辑
return "减库存成功!";
}
b.blockhandleClass
//一般都是Result<code,msg,data>,从简的话我直接string
public class CommonBlockHandle {
public static String handlerBlock(@RequestParam(value = "goodsId",required = false) String goodsId,
@RequestParam(value = "userId",required = false) String userId,
BlockException ex){
//logger.error("限流了");
return "系统出错了,请稍后再试!";
}
}
c.策略配置
测试:连续点击http://localhost:8000/resume?goodsId=10&userId=100
九.降级
a.熔断:微服务自己限流了,不可用了
b.降级:作为调用方,我们去调用提供方,提供方错了,返回给客户一个友好对象
那我们进行实践!
1.新建三个项目
cloudalibaba-sentinel-provider9001
a.导入依赖
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
b. yml
server:
port: 9001
spring:
application:
name: cloudalibaba-sentinal-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的端口
port: 8719
web-context-unify: false #关闭收敛URL
c.启动类
d.controller
@RestController
@RequestMapping("/goods")
public class goodsController {
@Value("${server.port}")
private Integer port;
@GetMapping("/findById/{id}")
public Goods findById(@PathVariable("id") Integer id){
Goods goods = new Goods();
goods.setGoodId(id);
goods.setPrice(123);
goods.setTitle("手机.port:"+port);
goods.setStock(10);
return goods;
}
}
domain.Goods自己写咯!
2.同样的,创建cloudalibaba-sentinal-provider9002
3.cloudalibaba-sentinel-consumer8000
a.pom
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions><!--由于最新的alibaba抛弃了ribbon,所以这个必配-->
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
b.yml
server:
port: 8000
spring:
application:
name: cloudalibaba-sentinal-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos
sentinel:
transport:
dashboard: localhost:8080 #sentinel
port: 8719
#激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
c,配置类
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
d.controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/add/{id}")
public Goods add(@PathVariable("id") Integer id){
String url = "http://cloudalibaba-sentinal-provider/goods/findById/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods;
}
}
e.复制Goods,启动类
测试:
由于ribbon被cloudalibaba启用,所以之后我们要用feign来替换它,以后不会再用ribbon
4.不同情况下 blockHandler与fallback情况
这里针对的是consumer
a.不配置
if(id<0){
throw new IllegalArgumentException("非法参数");
}else if(id>100){
throw new NullPointerException("查无此商品");
}
b.只配blockHandler
handler.BlockHandler,写下fail_add方法,记得方法是要static的,md
public class BlockHandler {
public static Goods fail_add(@PathVariable("id") Integer id, BlockException ex){
Goods goods =new Goods();
goods.setGoodId(-1);
goods.setPrice(-1);
goods.setStock(-1);
goods.setTitle("限流之后的特殊对象");
return goods;
}
}
在orderController的add接口上注上
策略配置
测试:localhost:8000/order/add/1
c.只配置fallback,这次就只定义在controller下面
fallback_add方法
public Goods fallback_add(@PathVariable("id")Integer id,Throwable ex){
Goods goods =new Goods();
goods.setGoodId(-2);
goods.setPrice(-2);
goods.setStock(-2);
goods.setTitle("业务出错之后的特殊对象");
return goods;
}
controller的add接口方法加上注解
测试:id给-1,模拟业务出错
d.blockhandller和fallback一起配置
测试:
1.限流
2.业务出错
十.feign调用
1.pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.yml
#前面也已经添加了
#激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
3.引导类上加
4.feign接口
@FeignClient(value = "cloudalibaba-sentinal-provider", fallback = GoodsFeignImpl.class)
public interface GoodsFeign {
@GetMapping("goods/findById/{id}")
public Goods findById(@PathVariable("id") Integer id);
}
5.feign实现类,充当fallback
@Component //一定加上,才能通过实现类找到对应接口
public class GoodsFeignImpl implements GoodsFeign {
@Override
public Goods findById(Integer id) {
Goods goods =new Goods();
goods.setGoodId(-3);
goods.setPrice(-3);
goods.setStock(-3);
goods.setTitle("feign出错之后的特殊对象");
return goods;
}
}
6.controller 之前的blockhandler可以保留,但是feign接口完成了fallback的功能,所以可以把fallback的相关注解和方法删除掉
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private GoodsFeign goodsFeign;
@GetMapping("/add/{id}")
@SentinelResource(value="add",blockHandlerClass = BlockHandler.class,blockHandler = "fail_add")
public Goods add(@PathVariable("id") Integer id){
if(id<0){
throw new IllegalArgumentException("非法参数");
}else if(id>100){
throw new NullPointerException("查无此商品");
}
Goods goods = goodsFeign.findById(id);
return goods;
}
}
测试:
1.正常访问
2.限流,之前做的blockHandler正常
3.业务错误,停掉provider
十一.配置持久化
sentinel的流控配置是临时的,所以我们可以把配置持久化到nacos,但是之前的nacos的配置持久化到数据库,所以其实是间接持久化到数据库了其实
也就是说,每次重启服务器后之前的策略配置都会清空,所以我们需要对这些配置进行持久化
1.pom.xml
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.yml
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848 #nacos
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow #流控规则
3.nacos添加配置
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群
[
{
"resource": "/order/add/{id}",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
4.测试
设置对consumer的流控测试一下后重启consumer,最后再测试一遍
一直都在:
interException(“查无此商品”);
}
Goods goods = goodsFeign.findById(id);
return goods;
}
}
测试:
1.正常访问
[外链图片转存中...(img-pMNzqiU6-1668410728273)]
2.限流,之前做的blockHandler正常
[外链图片转存中...(img-85JxOqTg-1668410728273)]
3.业务错误,停掉provider
[外链图片转存中...(img-9GP53pL6-1668410728273)]
### 十一.配置持久化
> sentinel的流控配置是临时的,所以我们可以把配置持久化到**nacos**,但是之前的nacos的配置持久化到数据库,所以其实是间接持久化到数据库了其实
>
> 也就是说,每次重启服务器后之前的策略配置都会清空,所以我们需要对这些配置进行**持久化**
1.pom.xml
```xml
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.yml
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848 #nacos
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow #流控规则
3.nacos添加配置
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群
[
{
"resource": "/order/add/{id}",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
[外链图片转存中…(img-YiXRQwSC-1668410728273)]
4.测试
设置对consumer的流控测试一下后重启consumer,最后再测试一遍
[外链图片转存中…(img-zcmZrcjg-1668410728274)]
[外链图片转存中…(img-COv0KfXL-1668410728274)]
一直都在: