❤ 作者主页: 欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于 Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得 关注、 点赞、 收藏、 评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉
第一章 SpringCloud Alibaba入门简介
一、为什么使用Alibaba
- 因为:spring netflix进入维护模式
什么是维护模式: 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为主。
- 所以:spring cloud alibaba来了
官方文档: https://spring.io/projects/spring-cloud-alibaba
主要功能:
服务限流降级
:默认支持 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.1.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 | Sentinel Version | Nacos Version | RocketMQ Version | Dubbo Version | Seata Version |
---|---|---|---|---|---|
2021.0.1.0* | 1.8.3 | 1.4.2 | 4.9.2 | 2.7.15 | 1.4.2 |
Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
---|---|---|
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
第二章 服务注册和配置中心
一、是什么
官方文档: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/
2. Nacos 概念
- 地域 物理的数据中心,资源创建成功后不能更换。
- 可用区 同一地域内,电力和网络互相独立的物理区域。同一可用区内,实例的网络延迟较低。
- 接入点 地域的某个服务的入口域名。
- 命名空间 用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
- 配置 在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段。
- 配置管理 系统配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动。
- 配置项 一个具体的可配置的参数与其值域,通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。
- 配置集 一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
- 配置集 ID Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
- 配置分组 Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。
- 配置快照 Nacos 的客户端 SDK 会在本地生成配置的快照。当客户端无法连接到 Nacos Server 时,可以使用配置快照显示系统的整体容灾能力。配置快照类似于 Git 中的本地 commit,也类似于缓存,会在适当的时机更新,但是并没有缓存过期(expiration)的概念。
- 服务 通过预定义接口网络访问的提供给客户端的软件功能。
- 服务名 服务提供的标识,通过该标识可以唯一确定其指代的服务。
- 服务注册中心 存储服务实例和服务负载均衡策略的数据库。
- 服务发现 在计算机网络上,(通常使用服务名)对服务下的实例的地址和元数据进行探测,并以预先定义的接口提供给客户端进行查询。
- 元信息 Nacos数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。
- 应用 用于标识服务提供方的服务的属性。
- 服务分组 不同的服务可以归类到同一分组。
- 虚拟集群 同一个服务下的所有服务实例组成一个默认集群, 集群可以被进一步按需求划分,划分的单位可以是虚拟集群。
- 实例 提供一个或多个服务的具有可访问网络地址(IP:Port)的进程。
- 权重 实例级别的配置。权重为浮点数。权重越大,分配给该实例的流量越大。
- 健康检查 以指定方式检查服务下挂载的实例 (Instance) 的健康度,从而确认该实例 (Instance) 是否能提供服务。根据检查结果,实例 (Instance) 会被判断为健康或不健康。对服务发起解析请求时,不健康的实例 (Instance) 不会返回给客户端。
- 健康保护阈值 为了防止因过多实例 (Instance) 不健康导致流量全部流向健康实例 (Instance) ,继而造成流量压力把健康实例 (Instance) 压垮并形成雪崩效应,应将健康保护阈值定义为一个 0 到 1 之间的浮点数。当域名健康实例数 (Instance) 占总服务实例数 (Instance) 的比例小于该值时,无论实例 (Instance) 是否健康,都会将这个实例 (Instance) 返回给客户端。这样做虽然损失了一部分流量,但是保证了集群中剩余健康实例 (Instance) 能正常工作。
3. 架构
数据模型
Nacos 数据模型 Key 由三元组唯一确定, Namespace默认是空串,公共命名空间(public),分组默认是 DEFAULT_GROUP。
服务领域模型
配置领域模型
围绕配置,主要有两个关联的实体,一个是配置变更历史,一个是服务标签(用于打标分类,方便索引),由 ID 关联。
二、Nacos2.0
新增长连接功能
Nacos2.0版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成。
- 新增 鉴权插件
- 新增 配置加密
三、与其他注册中心对比
服务注册与服务框架 | 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'
五、下载
进入bin目录,双击startup.cmd
进行启动。
- 端口号8848
- 可访问 :http://localhost:8848/nacos/index.html 地址,默认账号密码都是nacos
六、注册中心功能
1. 服务提供者1
- 新建模块
nacos-provider8000
pom
<dependencies> <!-- spring boot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringCloud Alibaba nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
application.yml
server: port: 8000 spring: application: name: nacos-provider cloud: nacos: discovery: server-addr: localhost:8848
创建启动类
@SpringBootApplication @EnableDiscoveryClient public class NacosProvider8000Application { public static void main(String[] args) { SpringApplication.run(NacosProvider8000Application.class, args); } }
controller
@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; } }
- 启动项目
2. 服务提供者2
- 新建模块
nacos-provider8001
pom
<dependencies> <!-- spring boot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringCloud Alibaba nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
application.yml
server: port: 8001 spring: application: name: nacos-provider cloud: nacos: discovery: server-addr: localhost:8848
创建启动类
@SpringBootApplication @EnableDiscoveryClient public class NacosProvider8001Application { public static void main(String[] args) { SpringApplication.run(NacosProvider8001Application.class, args); } }
controller
@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; } }
- 启动项目
3. 服务消费者
- 新建模块
nacos-conusmer9000
pom
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringCloud Alibaba nacos--> <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> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> <version>3.1.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
application.yml
server: port: 9000 spring: application: name: nacos-consumer cloud: loadbalancer: ribbon: enabled: false nacos: discovery: server-addr: localhost:8848
创建启动类
@SpringBootApplication @EnableDiscoveryClient public class NacosConsumer9000Application { public static void main(String[] args) { SpringApplication.run(NacosConsumer9000Application.class, args); } }
注册RestTemplate
@Configuration public class RestTemplateConfig { @Bean @LoadBalanced //loadbalancer 客户端负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); } }
controller
@RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/add/{id}") public String add(@PathVariable("id")Integer id){ //业务逻辑 String url="http://nacos-provider/goods/findById/"+id; String result = restTemplate.getForObject(url, String.class); return result; } }
- 测试
访问:[http://localhost:9000/order/add/1](http://localhost:9000/order/add/1)
4、整合feign
引入依赖
<!-- openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
主启动类上添加注解
@EnableFeignClients
新建feign接口
@FeignClient("nacos-provider") public interface GoodsFeign { @GetMapping("/goods/findById/{id}") public String findById(@PathVariable("id")Integer id); }
controller
@Autowired GoodsFeign goodsFeign; @GetMapping("/add2/{id}") public String add2(@PathVariable("id")Integer id){ //feign String str = goodsFeign.findById(id); return str; }
- 测试
访问:http://localhost:9000/order/add2/1
七、服务注册中心对比
1. Nacos 生态图
2. Nacos和CAP
3. 对比其他注册中心
A:可用性 C:一致性 P:分区容错性
Nacos默认AP。
切换CP:
- A:可用性
- C:一致性
- P:分区容错性
Nacos默认AP。
切换CP:
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
八、配置中心功能
1.创建工程
- 创建新模块
nacos-client7777
引入依赖
<dependencies> <!-- spring boot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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> </dependencies>
- 添加配置类
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格式的配置
#${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
```
启动类
@SpringBootApplication @EnableDiscoveryClient @RefreshScope //开启刷新功能 public class NacosClient7777Application { public static void main(String[] args) { SpringApplication.run(NacosClient7777Application.class, args); } }
controller
@RestController @RequestMapping("/config") @RefreshScope // 开启刷新功能 public class ConfigClientController { @Value("${name}") String name; @GetMapping("/name") public String name() { return name; } }
- 在Nacos中添加配置信息
https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
- 测试
- 更改配置
- 再次访问接口。说明bus的功能也实现了。
2. 分类配置
问题1:实际开发中,通常一个系统会准备dev/test/prod环境。如何保证环境启动时服务能正确读取nacos上相应环境的配置文件?
答案:namespace区分。
问题2:一个大型分布式微服务系统有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境。那怎么对微服务配置进行管理呢?
答案:用group把不同的微服务划分到同一个分组里面去。
Service就是微服务,一个service可以包含多个cluster集群,nacos默认cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将service微服务分别部署在了北京机房和上海机房,这是就可以给北京机房的service微服务起一个集群名称BJ,给上海的service微服务起一个集群名称SH,还可以尽量让同一个机房的微服务互相调用,以提升效率。
dgn方案
- dataid方案(就是nacos的文件名)
指定spring.profile.active和配置文件的dataID来使不太环境下读取不同的配置 配置空间+配置分组+新建dev和test两个dataid:就是创建-后不同的两个文件名nacos-config-client-dev.yaml、nacos-config-client-test.yaml 通过IDEA里的spring.profile.active属性就能进行多环境下配置文件的读取。
- Group方案(默认DEFAULT_GROUP)
在nacos创建配置文件时,给文件指定分组。 在IDEA中该group内容 实现的功能:当修改开发环境时,只会从同一group中进行切换。
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方案(默认public)
这个是不允许删除的,可以创建一个新的命名空间,会自动给创建的命名空间一个流水号。 在nacos新建命名空间,自动出现e79f32ec-974a-4e90-9086-3ae5c64db7e3
在IDEA的yml中指定命名空间namespace: e79f32ec-974a-4e90-9086-3ae5c64db7e3
pring:
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
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/f672672cddc74cac93f45df580b2cd63.png)
九、集群和持久化配置
第三章 SpringCloud Alibaba Sentinel实现熔断与限流
一、简介
一句话: 就是hystrix的替代!
官网:https://github.com/alibaba/sentinel
中文文档:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
Sentinel 的开源生态:
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
二、下载
下载地址:https://github.com/alibaba/Sentinel/releases
运行:
java -jar sentinel-dashboard-1.8.6.jar
http://localhost:8080
账号和密码都是sentinel
三、初始化演示工程
- 新建模块
cloudalibaba-sentinel-service8000
pom
<!-- SpringCloud ailibaba nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!-- SpringCloud ailibaba sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
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
主启动类
@SpringBootApplication @EnableDiscoveryClient public class CloudalibabaSentinelService8000Application { public static void main(String[] args) { SpringApplication.run(CloudalibabaSentinelService8000Application.class, args); } }
controller
@RestController public class FlowLimitController { @GetMapping("/testA") public String testA() { return "----testA"; } @GetMapping("/testB") public String testB() { return "----testB"; } }
- 测试
启动8050,然后刷新sentinel后台页面(因为sentinel采用懒加载策略,所以需要调用服务后才在后台显示) 在浏览器分别输入,然后刷新sentinel后台页面: http://localhost:8000/demo/testA http://localhost:8000/demo/testB
四、熔断降级
官方文档:
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
1. 概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
注意: 本文档针对 Sentinel 1.8.0 及以上版本。1.8.0 版本对熔断降级特性进行了全新的改进升级,请使用最新版本以更好地利用熔断降级的能力。
2. 熔断策略
Sentinel 提供以下几种熔断策略:
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
测试:
代码
@GetMapping("/testC/{id}") public String testC(@PathVariable("id")Integer id){ if(id==10){ //复杂的业务逻辑 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } return "itlils testC"; }
- 设置策略
- 一直访问10
- 过一会儿,访问10
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
测试:
代码
@GetMapping("/testD/{id}") public String testD(@PathVariable("id")Integer id){ if(id==10){ //异常调用 int a=1/0; } return "itlils testD"; }
- 设置策略
- 测试
- 疯狂访问
- 过2秒后
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
测试:
代码
@GetMapping("/testE/{id}") public String testE(@PathVariable("id")Integer id){ if(id==10){ //异常调用 int a=1/0; } return "itlils testE"; }
- 设置策略
五、热点参数限流
官方文档:
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. 基本使用
引入依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>1.8.4</version> </dependency>
controller
@GetMapping("/order") @SentinelResource(value = "hotKeys") public String order(@RequestParam("goodsId")String goodsId,@RequestParam("userId")String userId){ //业务逻辑 return "用户下单成功"; }
- 设置规则
- 快速访问http://localhost:8000/demo/order?goodsId=1&userId=10
- 快速访问http://localhost:8000/demo/order?userId=10
说明设置限制源代码第一个参数,成功了。
为了给调用方一个正确的值,改造
@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 "用户下单失败,请稍后重试"; }
3. 例外情况
秒杀情况下,某个参数goodsId=100,最新手机id,单独设置阈值1000。
- 配置
- 疯狂访问http://localhost:8000/demo/order?goodsId=100&userId=10 不会降级
注意:参数例外项,仅支持基本类型和字符串类型
注意:
加一个:int a=1/0;
- 业务上的错,sentinel不会管
- 热点key限制,sentinel才会管,疯狂访问
六、降级
1. 解释
- 熔断:微服务自己限流,不可用了。
- 降级:调用方,提供方错了,返回给客户一个友好对象。
2. 新建3个项目
cloudalibaba-sentinal-provider9001
pom
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- SpringCloud ailibaba sentinel--> <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>
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
启动类
@EnableDiscoveryClient
controller
@RestController @RequestMapping("/goods") public class GoodsController { @Value("${server.port}") Integer port; @GetMapping("/findById/{id}") public Goods findById(@PathVariable("id") Integer id){ //servive-->dao/mapper Goods goods=new Goods(); goods.setGoodId(id); goods.setPrice(123); goods.setTitle("手机.port:"+port); goods.setStock(10); return goods; } }
domain
public class Goods implements Serializable { private Integer goodId; private String title; private double price; private Integer stock; public Goods() { } public Goods(Integer goodId, String title, double price, Integer stock) { this.goodId = goodId; this.title = title; this.price = price; this.stock = stock; } @Override public String toString() { return "Goods{" + "goodId=" + goodId + ", title='" + title + '\'' + ", price=" + price + ", stock=" + stock + '}'; } public Integer getGoodId() { return goodId; } public void setGoodId(Integer goodId) { this.goodId = goodId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public Integer getStock() { return stock; } public void setStock(Integer stock) { this.stock = stock; } }
- 测试
cloudalibaba-sentinal-provider9002
同理。创建。
cloudalibaba-consumer-nacos-consumer8000
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> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> <version>3.1.1</version> </dependency> <!-- SpringCloud ailibaba sentinel--> <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> </dependencies>
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
配置类
@Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
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; } }
实体类
public class Goods implements Serializable { private Integer goodId; private String title; private double price; private Integer stock; public Goods() { } public Goods(Integer goodId, String title, double price, Integer stock) { this.goodId = goodId; this.title = title; this.price = price; this.stock = stock; } @Override public String toString() { return "Goods{" + "goodId=" + goodId + ", title='" + title + '\'' + ", price=" + price + ", stock=" + stock + '}'; } public Integer getGoodId() { return goodId; } public void setGoodId(Integer goodId) { this.goodId = goodId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public Integer getStock() { return stock; } public void setStock(Integer stock) { this.stock = stock; } }
- 测试
出现负载均衡
3. 不同情况下 blockHandler与fallback情况
不配置
if(id<0){ throw new IllegalArgumentException("非法参数"); }else if(id>100){ throw new NullPointerException("查无此商品"); }
只配blockHandler
public 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 go
正常访问错id
疯狂访问只配fallback
@SentinelResource(value = "add",fallback = "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; }
都配置
@SentinelResource(value = "add",blockHandler = "fail_add",fallback = "fallback_add")
-1 慢慢访问
-1 疯狂访问
忽略某些异常
@SentinelResource(value = "add",blockHandler = "fail_add",fallback = "fallback_add", exceptionsToIgnore = {IllegalArgumentException.class})
**
七、feign调用
pom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
yml
#前面也已经添加了 #激活Sentinel对Feign的支持 feign: sentinel: enabled: true
主启动类
@EnableFeignClients
feign接口
@FeignClient(value = "cloudalibaba-sentinal-provider",fallback = GoodsFeignImpl.class) public interface GoodsFeign { @GetMapping("/goods/findById/{id}") public Goods findById(@PathVariable("id") Integer id); }
实现类PaymentFallbackService
@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; } }
controller
@Autowired GoodsFeign goodsFeign; @GetMapping("/add1/{id}") public Goods add1(@PathVariable("id")Integer id){ Goods goods = goodsFeign.findById(id); return goods; }
- 测试出错,直接停了provider
八、配置持久化
sentinel的流控配置是临时的,所以我们可以把配置持久化到nacos。
pom
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
yml
sentinel: datasource: ds1: nacos: server-addr: localhost:8848 #nacos dataId: ${spring.application.name} groupId: DEFAULT_GROUP data-type: json rule-type: flow
nacos添加配置
[ { "resource": "/order/add1/{id}", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群
- 测试
创作不易,如果有帮助到你,请给文章==点个赞和收藏==,让更多的人看到!!!
==关注博主==不迷路,内容持续更新中。