19.SpringCloud Alibaba入门简介
19.1.为什么会出现SpringCloud alibaba
Spring Cloud Netflix项目进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now
![](https://i-blog.csdnimg.cn/blog_migrate/43da0c1ee0bb4d88507e483bfb817ea2.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a15d434e55c3f5a683fd825e30dc5358.png)
Spring Cloud Netflix Projects Entering Maintenance Mode
-
什么是维护模式
将模块置于维护模式,意味着 Spring Cloud 团队将不会再向模块添加新功能。
我们将修复 block 级别的 bug 以及安全问题,我们也会考虑并审查社区的小型 pull request。 -
进入维护模式意味着什么呢?
-
Spring Cloud Netflix 将不再开发新的组件
我们都知道Spring Cloud 版本迭代算是比较快的,因而出现了很多重大ISSUE都还来不及Fix就又推另一个Release了。进入维护模式意思就是目前一直以后一段时间Spring Cloud Netflix提供的服务和功能就这么多了,不在开发新的组件和功能了。以后将以维护和Merge分支Full Request为主
-
新组件功能将以其他替代平代替的方式实现
-
19.2.SpringCloud alibaba带来了什么
19.2.1.是什么
官网:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
诞生:
2018.10.31,Spring Cloud Alibaba 正式入驻了 Spring Cloud 官方孵化器,并在 Maven 中央库发布了第一个版本。
![](https://i-blog.csdnimg.cn/blog_migrate/772694f73c1b569099e64ef8464565b4.png)
19.2.2.主要功能
- 服务限流降级:默认支持 WebServlet、WebFlux、OpenFeign、RestTemplate、Spring Cloud Gateway、Zuul、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
- 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
- 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
19.2.3.下载
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
19.2.4.组件
![](https://i-blog.csdnimg.cn/blog_migrate/6ade395d928952b9e7ac874fa6bdf22d.png)
19.3.SpringCloud alibaba参考文档
19.3.1.官网
https://spring.io/projects/spring-cloud-alibaba#overview
![](https://i-blog.csdnimg.cn/blog_migrate/dfaa832690dc68c68a14db8eb8ec8828.png)
19.3.2.英文文档
https://github.com/alibaba/spring-cloud-alibaba
![](https://i-blog.csdnimg.cn/blog_migrate/83cde25e5b91e7a4e5d2ea0370f83a1a.png)
英文文档
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
![](https://i-blog.csdnimg.cn/blog_migrate/b3cbbfff37d6dafd08f3b4fc2e0d0197.png)
19.3.3.中文文档
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
![](https://i-blog.csdnimg.cn/blog_migrate/ccc3876175290f176643a0aab065b92d.png)
20.SpringCloud Alibaba Nacos服务注册和配置中心
20.1.Nacos简介
20.1.1.为什么叫Nacos
前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。
20.1.2.是什么
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos: Dynamic Naming and Configuration Service
Nacos就是注册中心 + 配置中心的组合等价于Nacos = Eureka+Config +Bus
20.1.3.主要功能
- 替代Eureka做服务注册中心
- 替代Config做服务配置中心
20.1.4.发布以及文档地址
下载:https://github.com/alibaba/Nacos
官方文档:
-
中文地址:https://nacos.io/zh-cn/index.html
-
英文地址:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery
20.1.5.各种注册中心比较
![04163839267](https://i-blog.csdnimg.cn/blog_migrate/76460c91b5f26f89b52c7d320934ecbd.png)
据说 Nacos 在阿里巴巴内部有超过 10 万的实例运行,已经过了类似双十一等各种大型流量的考验
20.2.安装并运行Nacos
本地Java8+Maven环境
20.2.1.官网下载Nacos
https://github.com/alibaba/nacos/releases
![](https://i-blog.csdnimg.cn/blog_migrate/291b8353372da90cc52279413b902a86.png)
20.2.2.安装
-
下载后解压
-
解压安装包,直接运行bin目录下的startup.cmd
startup.cmd -m standalone
-
命令运行成功后直接访问http://localhost:8848/nacos
默认账号密码都是nacos
20.3.Nacos作为服务注册中心
20.3.1.官网文档
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_config
20.3.2.基于Nacos的服务提供者
20.3.2.1.新建Module
![](https://i-blog.csdnimg.cn/blog_migrate/95862f848c43100aa29755ca379a15fa.png)
20.3.2.2.pom.xml
-
父项目pom.xml
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency>
-
本项目pom.xml
<dependencies> <!--SpringCloud ailibaba nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--日常通用jar包配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
20.3.2.4.application.yaml
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
20.3.2.5.主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class, args);
}
}
20.3.2.6.业务类
@Controller
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/payment/nacos/{id}")
@ResponseBody
public String getPayment(@PathVariable("id") Integer id) {
return "nacos registry, serverPort: " + serverPort + "\t id" + id;
}
}
20.3.2.7.测试
http://localhost:9001/payment/nacos/1
![](https://i-blog.csdnimg.cn/blog_migrate/ac2594bce3a4fe41164f0a87d22ad2d4.png)
nacos控制台
![](https://i-blog.csdnimg.cn/blog_migrate/646b2ac797df556b3c413010c4443084.png)
20.3.2.8.参照9001新建9002
为了下一章节演示nacos的负载均衡,参照9001新建9002
![](https://i-blog.csdnimg.cn/blog_migrate/eb8b0466cf24a4dde4c2d8e251e8ef96.png)
20.3.3.基于Nacos的服务消费者
20.3.3.1.新建Module
cloudalibaba-consumer-nacos-order83
![](https://i-blog.csdnimg.cn/blog_migrate/ccdeee013074e84f6d90e267af8a8729.png)
20.3.3.2.pom.xml
com.alibaba.cloud
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.stonebridge.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
为什么nacos支持负载均衡
![04181645348](https://i-blog.csdnimg.cn/blog_migrate/9e7e73a0c05d22dc219fb4c5e9afc94f.png)
20.3.3.3.apllication.yaml
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
20.3.3.4.主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain83.class, args);
}
}
20.3.3.5.业务类
-
config
负载均衡一定要加@LoadBalanced注解
@Configuration public class ApplicationContextBean { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
-
controller
@Controller public class OrderNacosController { @Resource private RestTemplate restTemplate; @Value("${service-url.nacos-user-service}") private String serverURL; @RequestMapping("/consumer/payment/nacos/{id}") @ResponseBody public String paymentInfo(@PathVariable("id") Long id) { return restTemplate.getForObject(serverURL + "/payment/nacos/" + id, String.class); } }
20.3.3.6.测试
启动
![](https://i-blog.csdnimg.cn/blog_migrate/fb115e87da03ec4bc6ca8746c559051a.png)
访问:http://localhost:83/consumer/payment/nacos/13
83访问9001/9002,轮询负载OK
![](https://i-blog.csdnimg.cn/blog_migrate/53f8b7153a844efa8246fd2d6ee67a28.png)
20.3.4.服务注册中心对比
20.3.4.1.Nacos全景图所示
![](https://i-blog.csdnimg.cn/blog_migrate/b4f67fd0575edd020902dc8ccca955f3.png)
20.3.4.2.Nacos和CAP
![](https://i-blog.csdnimg.cn/blog_migrate/6aa52ffcab81b73a01b03ca52ceb4b2f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c0cc838841d9702700e3595774a6f1fd.png)
20.3.4.3.切换
Nacos 支持AP和CP模式的切换
C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
一般来说,
如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
20.4.Nacos作为服务配置中心
20.4.1.Nacos作为配置中心-基础配置
20.4.1.1.新建Module
cloudalibaba-config-nacos-client3377
![](https://i-blog.csdnimg.cn/blog_migrate/a890ec2cd0bf29fd1d4841d16c5e262f.png)
20.4.1.2.pom.xml
<dependencies>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
20.4.1.3.application.yaml
Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
-
bootstrap.yaml
# nacos配置 server: port: 3377 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}
-
application.yaml
spring: profiles: active: dev # 表示开发环境
20.4.1.4.业务类
通过Spring Cloud 原生注解@RefreshScope实现配置自动更新:
@Controller
@RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@RequestMapping("/config/info")
@ResponseBody
public String getConfigInfo() {
return configInfo;
}
}
20.4.1.5.在Nacos中添加配置信息
Nacos中的匹配规则
-
理论
Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则
官网
最后公式:
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
根据公式拼接的dataId为nacos-config-client-dev.yaml。因此在Nacos注册中心配置的配置新的dataId为nacos-config-client-dev.yaml,便可以被该应用获取。
-
实操
-
配置新增
nacos-config-client-dev
-
Nacos界面配置对应
设置DataId
-
公式:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
-
prefix 默认为 spring.application.name 的值
-
spring.profile.active 即为当前环境对应的 profile,可以通过配置项 spring.profile.active 来配置。
-
file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置
-
-
历史配置
Nacos会记录配置文件的历史版本默认保留30天,此外还有一键回滚功能,回滚操作将会触发配置更新
-
20.4.1.6.测试
注意应用的配置的是spring.cloud.nacos.config.file-extension是yaml还是yml
启动前需要在nacos客户端-配置管理-配置管理栏目下有对应的yaml配置文件
运行cloud-config-nacos-client3377的主启动类
调用接口查看配置信息:http://localhost:3377/config/info
![](https://i-blog.csdnimg.cn/blog_migrate/7c342af10dac56159e3505477c15d277.png)
20.4.1.7.自带动态刷新
修改下Nacos中的yaml配置文件,再次调用查看配置的接口,就会发现配置已经刷新
![](https://i-blog.csdnimg.cn/blog_migrate/1e5958a3d69aee7c51d71539e355c395.png)
20.4.2.Nacos作为配置中心-分类配置
20.4.2.1.问题
多环境多项目管理
-
问题1:
实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢? -
问题2:
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…
那怎么对这些微服务配置进行管理呢?
20.4.2.2.Nacos的图形化管理界面
-
配置管理
-
命名空间
20.4.2.3.Namespace+Group+Data ID三者关系、设计思想
-
是什么
类似Java里面的package名和类名
最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
-
三者情况
默认情况:
Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT
Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去
Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,
这时就可以给杭州机房的Service微服务起一个集群名称(HZ),
给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
最后是Instance,就是微服务的实例。
20.4.2.4.DataID方案
-
指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置
-
默认空间+默认分组+新建dev和test两个DataID
-
新建dev配置DataID
-
新建test配置DataID
-
-
通过spring.profile.active属性就能进行多环境下配置文件的读取
-
测试
重启配置spring.profile.active的3377应用
访问:http://localhost:3377/config/info
20.4.2.5.Group方案
-
通过Group实现环境区分
新建Group
-
在nacos图形界面控制台上面新建配置文件DataID
-
bootstrap+application
在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP
20.4.2.6.Namespace方案
-
新建dev/test的Namespace
-
回到服务管理-服务列表查看
-
在命名空间下增加配置
访问:http://localhost:3377/config/info
-
20.5.Nacos集群和持久化配置
20.5.1.官网说明
https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
![](https://i-blog.csdnimg.cn/blog_migrate/8695b397a7697eab04f2c68ea0c14796.png)
20.5.1.1.官网架构图
![](https://i-blog.csdnimg.cn/blog_migrate/0879716f8300e380e377991d19b60638.png)
VIP的意思是virtual IP,就是Nginx
20.5.1.2.上图官网翻译,真实情况
![](https://i-blog.csdnimg.cn/blog_migrate/684c9232163b24aabf5a8305779a6b5a.png)
20.5.1.3.说明
默认Nacos使用嵌入式数据库实现数据的存储(比如现在的单机模式中,保存的配置信息就保存在内嵌数据库Derby中)。
所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。数据源只有一份就可以保持数据的一致性
https://nacos.io/zh-cn/docs/deployment.html
![](https://i-blog.csdnimg.cn/blog_migrate/29bf1f67ce3dbc4c98966af971eafc5d.png)
20.5.2.Nacos持久化配置解释
20.5.2.1.Nacos默认自带的是嵌入式数据库derby
https://github.com/alibaba/nacos/blob/develop/config/pom.xml
![](https://i-blog.csdnimg.cn/blog_migrate/8a7553c943590b4de1f6d583d86b4018.png)
20.5.2.2.derby到mysql切换配置步骤
-
nacos\conf目录下找到sql脚本
nacos-mysql.sql
先建库结构nacos_config
/* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info */ /******************************************/ CREATE TABLE `config_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) DEFAULT NULL, `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `c_desc` varchar(256) DEFAULT NULL, `c_use` varchar(64) DEFAULT NULL, `effect` varchar(64) DEFAULT NULL, `type` varchar(64) DEFAULT NULL, `c_schema` text, PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_aggr */ /******************************************/ CREATE TABLE `config_info_aggr` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) NOT NULL COMMENT 'group_id', `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', `content` longtext NOT NULL COMMENT '内容', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_beta */ /******************************************/ CREATE TABLE `config_info_beta` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_tag */ /******************************************/ CREATE TABLE `config_info_tag` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_tags_relation */ /******************************************/ CREATE TABLE `config_tags_relation` ( `id` bigint(20) NOT NULL COMMENT 'id', `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `nid` bigint(20) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`nid`), UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = group_capacity */ /******************************************/ CREATE TABLE `group_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_group_id` (`group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = his_config_info */ /******************************************/ CREATE TABLE `his_config_info` ( `id` bigint(64) unsigned NOT NULL, `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `data_id` varchar(255) NOT NULL, `group_id` varchar(128) NOT NULL, `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL, `md5` varchar(32) DEFAULT NULL, `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `src_user` text, `src_ip` varchar(50) DEFAULT NULL, `op_type` char(10) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`nid`), KEY `idx_gmt_create` (`gmt_create`), KEY `idx_gmt_modified` (`gmt_modified`), KEY `idx_did` (`data_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = tenant_capacity */ /******************************************/ CREATE TABLE `tenant_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; CREATE TABLE `tenant_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `kp` varchar(128) NOT NULL COMMENT 'kp', `tenant_id` varchar(128) default '' COMMENT 'tenant_id', `tenant_name` varchar(128) default '' COMMENT 'tenant_name', `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; CREATE TABLE `users` ( `username` varchar(50) NOT NULL PRIMARY KEY, `password` varchar(500) NOT NULL, `enabled` boolean NOT NULL ); CREATE TABLE `roles` ( `username` varchar(50) NOT NULL, `role` varchar(50) NOT NULL, UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE ); CREATE TABLE `permissions` ( `role` varchar(50) NOT NULL, `resource` varchar(255) NOT NULL, `action` varchar(8) NOT NULL, UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE ); INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
-
nacos\conf目录下找到application.properties
添加数据库配置
nacos.istio.mcp.server.enabled=false ############################################ spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC db.user=root db.password=123456
20.5.2.3.启动Nacos
startup.cmd -m standalone
![](https://i-blog.csdnimg.cn/blog_migrate/1613f5c5f809cee1ff9ef9978906787b.png)
20.5.2.4.访问http://localhost:8848/nacos
![](https://i-blog.csdnimg.cn/blog_migrate/efca4164fa441ca3686c13bb50974788.png)
新建配置
![](https://i-blog.csdnimg.cn/blog_migrate/01ada1268ee332fc43fb444e9300f85f.png)
查看数据库
![](https://i-blog.csdnimg.cn/blog_migrate/76f243ea21448ca13275dd61a3217b62.png)
20.5.3.Linux配置Nacos+MySQL生产环境配置
20.5.3.1.CentOS7安装MySQL
-
查看Liunx版本
[root@192 bin]# cat /etc/redhat-release
-
下载MySQL
https://dev.mysql.com/downloads/mysql/5.7.html#downloads
-
解压
安装目录:/usr/local/mysqldb/mysql
数据目录:/usr/local/mysqldb/data
[root@192 Downloads]# tar -zxvf mysql-5.7.29-linux-glibc2.12-x86_64.tar.gz
-
重命名到usr/local/mysqldb目录下,名字为mysql
[root@192 Downloads]# mkdir -p /usr/local/mysqldb && mv mysql-5.7.29-linux-glibc2.12-x86_64 /usr/local/mysqldb/mysql
-
增加一个mysql用户并赋权限
[root@192 Downloads]# useradd mysql
[root@192 Downloads]# chown -R mysql /usr/local/mysqldb/
[root@192 Downloads]# chgrp -R mysql /usr/local/mysqldb/
-
切换到mysql安装目录
[root@192 Downloads]# cd /usr/local/mysqldb/mysql/bin
-
生成初始化密码并记住这个初始化密码
[root@192 bin]# mkdir -p /usr/local/mysqldb/data && ./mysqld --initialize --user=mysql --basedir=/usr/local/mysqldb/mysql --datadir=/usr/local/mysqldb/data
-
在/etc/目录新增my.cnf文件并配置如下
mysql5.7.29从之前的某个版本开始,在/usr/local/mysqldb/mysql/support-files目录下面呢,就没有名为
my_default.cnf这个文件了,该文件作为配置my.cnf的配置模板
[root@192 bin]# rm -rf /etc/my.cnf [root@192 bin]# vim /etc/my.cnf
[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 socket=/var/lib/mysql/mysql.sock [mysqld] sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES skip-name-resolve #设置3306端口 port = 3306 socket=/var/lib/mysql/mysql.sock # 设置mysql的安装目录 basedir=/usr/local/mysqldb/mysql # 设置mysql数据库的数据的存放目录 datadir=/usr/local/mysqldb/data # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB lower_case_table_names=1 max_allowed_packet=16M #mysql 5.6占用内存400多兆,太大了,配置优化 performance_schema_max_table_instances=600 table_definition_cache=400 table_open_cache=256
创建上面用到的文件夹
[root@192 bin]# mkdir -p /var/lib/mysql&&chmod 777 /var/lib/mysql
-
mysqld
[root@192 bin]# mv /usr/local/mysqldb/mysql/support-files/mysql.server /etc/init.d/mysqld
增加可执行权限
[root@192 bin]# chmod +x /etc/init.d/mysqld
-
配置/etc/init.d/mysqld文件,修改内容如下:
vim /etc/init.d/mysqld
basedir=/usr/local/mysqldb/mysql datadir=/usr/local/mysqldb/data
-
配置mysql开机自动启动(可选:不想开机启动可以不执行)
chkconfig --add mysqld chkconfig --list mysqld chkconfig --level 35 mysqld on
-
启动mysql
[root@192 bin]# systemctl start mysqld
-
登录
[root@192 bin]# ln -s /usr/local/mysqldb/mysql/bin/mysql /usr/bin
[root@192 bin]# mysql -uroot -p
输入临时密码
-
重置默认密码
mysql> set password = password('itcast');
-
后续修改密码
-- 改root的默认生成密码 alter user 'root'@'localhost' identified by '123456'; flush privileges; -- 修改用户密码的方式 -- 方式一 SET PASSWORD FOR 'username'@'host' = PASSWORD('newpassword'); flush privileges; -- 方式二:5.7版本没有password这个字段了,改成了authentication_string update user set authentication_string=password('123') where user='root'; flush privileges; -- 方式三 alter user 'root'@'%' IDENTIFIED BY '123456'; flush privileges; -- 创建新用户格式 host可以是localhost、%、具体的ip CREATE USER 'username'@'host' IDENTIFIED BY 'password'; -- 创建新用户举例: create user 'aaa'@'%' IDENTIFIED BY '123'; -- 对用户的操作命令其实是操作的mysql.user表 flush privileges;
-
授权操作
查看用户信息,HOST为localhost,root用户只能本机登录
-- 授权语法 privileges可以是select、INSERT,UPDATE等,如果要授予所的权限则使用ALL -- 所有数据库的所有表: *.* -- []代表此用户可以再给其他用户授权 GRANT privileges ON databasename.tablename TO 'username'@'host' [WITH GRANT OPTION]; flush privileges; -- 授权后需要刷新 -- 举例: 会新增一条记录 grant all on *.* to 'root'@'%'; flush privileges; -- 授权后需要刷新
或者
-
开放3306端口
sudo firewall-cmd --add-port=3306/tcp --permanent
防火墙重启
firewall-cmd --reload
查看开放的端口号是否存在3306
firewall-cmd --list-all
-
连接
-
重启
[root@localhost conf]# cd /usr/local/mysqldb/mysql/bin [root@localhost bin]# systemctl start mysqld
20.5.3.2.Linux配置Nacos集群
-
下载
https://github.com/alibaba/nacos/releases/tag/1.1.4
-
解压
tar -zxvf nacos-server-1.1.4.tar.gz
-
切换root,将解压后的文件转义到opt目录
[root@localhost Downloads]# cp -r nacos /mynacos/
-
进入bin
-
修改startup.sh
-
startup.sh修改内容
-
复制出cluster.conf
修改内容
这个IP不能写127.0.0.1,必须是Linux命令hostname -i能够识别的IP
-
startup.sh修改完成后集群启动
./startup.sh -p 3333
ps -ef|grep nacos |grep -v grep|wc -l
-
关闭nacos集群
20.5.3.3.Linux配置Nginx
安装Nginx必须具备的组件
![](https://i-blog.csdnimg.cn/blog_migrate/bb4fe677a118fd90cf3dd4940ae059dc.png)
-
安装 pcre 依赖
-
联网下载 pcre 压缩文件依赖
wget http://downloads.sourceforge.net/project/pcre/pcre/8.37/pcre-8.37.tar.gz
-
解压该文件
tar -zxvf pcre-8.37.tar.gz
-
将解压文件复制到/usr/local/pcre目录下
mkdir -p /usr/local/pcre && mv pcre-8.37 /usr/local/pcre
-
进入/usr/local/pcre/pcre-8.37/
cd /usr/local/pcre/pcre-8.37/
-
执行 ./configure编译
./configure
执行./configure后如果出现
需要执行
yum install -y gcc gcc-c++
再次执行
./configure
./configure执行正常
-
安装
make && make install
-
校验是否安装成功,查看版本
pcre-config --version
-
-
安装 openssl 、zlib 、 gcc 依赖
yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel
-
安装Nginx
-
下载Nginxhttp://nginx.org/
-
下载成功
-
解压Nginx
tar -zxvf nginx-1.12.2.tar.gz
-
将nginx移动到指定文件夹
此处最好创建/usr/local/mynginx文件夹,因此安装成功会创建/usr/local/nginx文件夹
mkdir -p /usr/local/nginx && mv nginx-1.12.2 /usr/local/nginx
-
切换到nginx目录
cd /usr/local/nginx/nginx-1.12.2
-
检查
./configure
-
编译安装
make && make install
-
安装成功后在/usr/local多出一个文件nginx文件夹
在里面sbin有启动脚本
-
启动
-
查看配置文件的监听端口
-
访问:
-
修改nginx的配置文件访问nacos集群
修改内容
upstream cluster{ server 127.0.0.1:3333; server 127.0.0.1:4444; server 127.0.0.1:5555; } server { listen 1111; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { #root html; #index index.html index.htm; proxy_pass http://cluster; }
-
根据配置启动nginx
-
开放1111端口
-
Linux关于防火墙端口管理
-
查看防火墙状态
systemctl status firewalld
-
开启防火墙
systemctl start firewalld
-
关闭防火墙
systemctl stop firewalld
-
重启防火墙
firewall-cmd --reload
-
查看开放的端口号
firewall-cmd --list-all
-
设置开放端口
sudo firewall-cmd --add-port=80/tcp --permanent
增加开放端口后要重启防火墙
-
-
关闭nginx
./nginx -s stop
-
20.5.4.Linux集群配置步骤(重点)
20.5.4.1.Linux服务器上mysql数据库配置
![](https://i-blog.csdnimg.cn/blog_migrate/271b1c2434e3170f951748fdc24fb8e1.png)
执行sql脚本
![](https://i-blog.csdnimg.cn/blog_migrate/bb75d505150f55e85b086e2090b9c646.png)
20.5.4.2.application.properties 配置
![](https://i-blog.csdnimg.cn/blog_migrate/e53e806443e912765dc413bcd021f867.png)
nacos.istio.mcp.server.enabled=false
############################################
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
db.user=root
db.password=123456
20.5.4.3.Linux服务器上nacos的集群配置cluster.conf
梳理出3台nacos集器的不同服务端口号
-
复制出cluster.conf
-
修改内容
这个IP不能写127.0.0.1,必须是Linux命令hostname -i能够识别的IP
20.5.4.4.编辑Nacos的启动脚本startup.sh
使它能够接受不同的启动端口
思考
/mynacos/nacos/bin 目录下有startup.sh
平时单机版的启动,都是./startup.sh即可。
但是集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例。
命令:./startup.sh -p 3333 表示启动端口号为3333的nacos服务器实例,和上一步的cluster.conf配置的一致。
修改内容
![](https://i-blog.csdnimg.cn/blog_migrate/d70ee3b299cc20581df181563e5b5f4f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/63c94fe60c3dc37ae856d0839c3d6d52.png)
20.5.4.5.Nginx的配置,由它作为负载均衡器
-
修改nginx的配置文件
-
nginx.conf
修改位置
upstream cluster{ server 127.0.0.1:3333; server 127.0.0.1:4444; server 127.0.0.1:5555; } server { listen 1111; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { #root html; #index index.html index.htm; proxy_pass http://cluster; }
20.5.4.6.启动Nginx以及nacos集群
截止到此处,1个Nginx+3个nacos注册中心+1个mysql已经准备就绪
-
集群启动nacos
-
启动nginx
-
开启1111端口
20.5.4.7.访问nacos集群注册中心
![](https://i-blog.csdnimg.cn/blog_migrate/b97d381466a936085df492f5752f566e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3ca0b9e59c1632106165d36331553d71.png)
发布新配置
![](https://i-blog.csdnimg.cn/blog_migrate/c0e52ff2631eb4d29d946a0798e0a8af.png)
![](https://i-blog.csdnimg.cn/blog_migrate/151485236190975a395f1eb819010859.png)
20.5.4.8.测试注册到nacos集群注册中心
微服务cloudalibaba-provider-payment9002启动注册进nacos集群
![](https://i-blog.csdnimg.cn/blog_migrate/f694ebdd332a85f9e0d9c276d057345f.png)
启动
![](https://i-blog.csdnimg.cn/blog_migrate/3cfb68812498a9b3899d123c5777467f.png)
20.5.4.9.关闭集群
-
关闭nginx
./nginx -s stop
-
关闭nacos集群
20.5.4.10.高可用小总结
![](https://i-blog.csdnimg.cn/blog_migrate/5d5c209c0da816bd6a0f50e6c92f91bb.png)
21.SpringCloud Alibaba Sentinel实现熔断与限流
21.1.Sentinel
Sentinel作为一个组件,可以独立出来。可以直接界面化的细粒度统一配置管理。
21.1.1官网
-
GitHub
https://github.com/alibaba/Sentinel
-
中文文档
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
21.1.2.是什么
A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)
一句话解释,之前我们讲解过的Hystrix
21.1.3.去哪下
https://github.com/alibaba/Sentinel/releases
![](https://i-blog.csdnimg.cn/blog_migrate/a5e8cf548034e22ce70f477efe5c8fc4.png)
21.1.4.能干嘛
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
![](https://i-blog.csdnimg.cn/blog_migrate/c4697aac3290ac227ca7f80e67e65669.png)
21.1.5.怎么玩
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
服务使用中的各种问题
- 服务雪崩
- 服务降级
- 服务熔断
- 服务限流
21.2.安装Sentinel控制台
21.2.1.sentinel组件由2部分构成
Sentinel 分为两个部分:
- 核心库(Java客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo /Spring Cloud等框架也有较好的支持。
- 控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
21.2.2.安装步骤
21.2.2.1.下载
https://github.com/alibaba/Sentinel/releases
![](https://i-blog.csdnimg.cn/blog_migrate/c4c51cfb378c73fff11c5669b6b698c9.png)
下载到本地sentinel-dashboard-1.7.0.jar
21.2.2.2.运行命令
前提: java8环境、8080端口不能被占用
java -jar sentinel-dashboard-1.7.0.jar
21.2.2.3.访问sentinel管理界面
http://localhost:8080
登录账号密码均为sentinel
![](https://i-blog.csdnimg.cn/blog_migrate/da6c26f00cc1f999c95353a8a733f690.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3e0cb82b19805fe02b46c7e557525287.png)
21.3.初始化演示工程
21.3.1.启动Nacos8848成功
http://localhost:8848/nacos/#/login
![](https://i-blog.csdnimg.cn/blog_migrate/6604cc6174b3b8dc95c24e23f68ca9c0.png)
21.3.2.新建Module
新建的cloudalibaba-sentinel-service8401项目注册到Nacos8848,并且被Sentinel8080保护。
21.3.2.1.新建cloudalibaba-sentinel-service8401
![](https://i-blog.csdnimg.cn/blog_migrate/48a102230bdb716aaac21adbe27d2550.png)
21.3.2.2.pom.xml
主要是:spring-cloud-starter-alibaba-sentinel
<dependencies>
<!--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>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
21.3.2.3.application.yaml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址,即8080会监控8401
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
21.3.2.4.主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
21.3.2.5.业务类FlowLimitController
@Controller
public class FlowLimitController {
@RequestMapping("/testA")
@ResponseBody
public String testA() {
return "------testA";
}
@RequestMapping("/testB")
@ResponseBody
public String testB() {
return "------testB";
}
}
21.3.3.启动Sentinel8080
java -jar sentinel-dashboard-1.7.0.jar
21.3.4.启动微服务8401
21.3.4.1.nacos注册中心
![](https://i-blog.csdnimg.cn/blog_migrate/6e753ded209212c31a676c8e1f1a04a5.png)
21.3.4.2.sentienl控制台
-
直接访问sentinel时,没有任何显示
Sentinel采用的懒加载说明,执行一次访问8401即可http://localhost:8401/testA
-
访问8401后继续访问
21.4.流控规则
21.4.1.基本介绍
簇点链路会显示所有的访问的路径
![](https://i-blog.csdnimg.cn/blog_migrate/f719f9562766b704758aca69f2e699a3.png)
流控规则
![](https://i-blog.csdnimg.cn/blog_migrate/c71fae6d015c2b3edde2b1c6dcd74681.png)
Sentinel名词解释
-
资源名:唯一名称,默认请求路径
-
针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)
-
阈值类型/单机阈值:
-
QPS(每秒钟的请求数量):当调用该接口的QPS达到了阈值的时候,进行限流;
-
线程数:当调用该接口的线程数达到阈值时,进行限流
-
-
是否集群:不需要集群
-
流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)[api级别的针对来源]
-
流控来源
- 快速失败:直接失败,抛异常
- Warm Up:默认coldFactor为3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。
21.4.2.流控模式
流控模式是只设置流控时的具体模式
![](https://i-blog.csdnimg.cn/blog_migrate/bbfb6600965f41ed3f7ef54f80655c21.png)
资源名的解释:目前的流控规则首先拿请求地址(@RequestMapping映射)进行控制,即为唯一名称,资源名,默认请求路径。
21.4.2.1.直接模式1(默认)-OPS
QPS(每秒钟的请求数量):当调用该接口的QPS达到了阈值的时候,进行限流;
-
现象:直接->快速失败
-
配置及说明
表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
-
测试
快速点击访问http://localhost:8401/testA
结果:Blocked by Sentinel (flow limiting)
-
思考
直接调用默认报错信息,技术方面OK。but,是否应该有我们自己的后续处理?类似有个fallback的兜底方法?
21.4.2.2.直接模式2-线程数
线程数:当调用该接口的线程数达到阈值时,进行限流
当线程数设置为2时,表示现在只有两个线程对该路径的请求进行处理,如果发起的请求时,两个线程正在处理之前的请求,则会返回Blocked by Sentinel (flow limiting)
![](https://i-blog.csdnimg.cn/blog_migrate/c1bcddcf74ff9da1d5a7fa58bd06d6ab.png)
21.4.2.3.关联模式
-
基本概念:当关联的资源达到阈值时,就限流自己。即当与A关联的资源B达到阀值后,就限流A自己。比如支付接口达到阈值,就限流下订单的接口。
-
设置效果
当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名
-
postman模拟并发密集访问testB
大批量线程高并发访问B,导致A失效了
21.4.2.4.链路模式
略
21.4.3.流控效果
21.4.3.1.直接->快速失败(默认的流控处理)
直接失败,抛出异常:Blocked by Sentinel (flow limiting)
源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
21.4.3.2.预热
-
说明
默认 coldFactor 为 3,即请求QPS从(threshold / 3) 开始,经多少预热时长才逐渐升至设定的 QPS 阈值。
案例,阀值为10+预热时长设置5秒。
系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10 -
官网
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
默认coldFactor为3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
限流 冷启动
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8
-
源码
com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
-
WarmUp配置
默认 coldFactor 为 3,即请求QPS从(threshold / 3) 开始,经多少预热时长才逐渐升至设定的 QPS 阈值。案例,阀值为10+预热时长设置5秒。
系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10
-
测试
多次点击http://localhost:8401/testB,刚开始不行,后续慢慢OK
-
应用场景
如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。
21.4.3.3.排队等待
匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。
设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。
-
官网
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
-
com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
-
配置
-
测试
-
结果
21.5.降级规则
21.5.1.官网
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
![](https://i-blog.csdnimg.cn/blog_migrate/569e0457d794ff1d8b8db092f952a9f4.png)
21.5.2.基本介绍
![](https://i-blog.csdnimg.cn/blog_migrate/b5fa97ccd38b88588d528b0986ff3694.png)
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,
让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
注意:Sentinel的断路器是没有半开状态的
半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix
![](https://i-blog.csdnimg.cn/blog_migrate/b3952bd7031c873e4e3a2d4a96864ef1.png)
RT(平均响应时间,秒级)
一秒钟的请求数大于或者等于5个时,如果所有请求的平均响应时间大于阈值。在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
异常比列(秒级)
QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
21.5.3.RT-降级策略实战
21.5.3.1.基本概念
一秒钟的请求数大于或者等于5个时,如果所有请求的平均响应时间大于阈值。在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
![](https://i-blog.csdnimg.cn/blog_migrate/b88d11bf96ddfa7a7f45eb1d84b1eac9.png)
21.5.3.2.配置测试
-
代码
@Controller @Slf4j public class FlowLimitController { @RequestMapping("/testD") @ResponseBody public String testD() { //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.info("testD 测试RT"); return "------testD"; } }
-
配置
-
jmeter压测
开启Jmeter循环请求后,断路器开启
关闭Jmeter循环请求后,断路器关闭
-
结论
按照上述配置,
永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复。
21.5.4.异常比例-降级策略实战
21.5.4.1.基本概念
![](https://i-blog.csdnimg.cn/blog_migrate/b2a21d1a58b711ab7aca4b93dffde411.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cadeac55cbf1cc6599765e612cc85854.png)
21.5.4.2.配置测试
-
代码
@Controller @Slf4j public class FlowLimitController { @RequestMapping("/testE") @ResponseBody public String testE() { System.out.println("testE 测试RT"); int age = 10 / 0; return "------testE"; } }
-
配置
-
jmeter配置
开启Jmeter循环请求后,断路器开启
关闭Jmeter循环请求后,断路器关闭
-
结论
按照上述配置,
单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次;开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。
21.5.5.异常数-降级策略实战
21.5.5.1.基本概念
![](https://i-blog.csdnimg.cn/blog_migrate/45924eb08cc5bc822771a763588292fc.png)
时间窗口一定要大于等于60秒。
异常数是按照分钟统计的
21.5.5.2.配置测试
-
代码
@Controller @Slf4j public class FlowLimitController { @RequestMapping("/testF") @ResponseBody public String testF() { System.out.println("testF 测试RT"); int age = 10 / 0; return "------testF"; } }
-
配置
-
浏览器访问http://localhost:8401/testF五次
访问五次后一分钟的异常数目达到5次,再次访问时,Sentinel的断路器打开。
21.6.热点key限流
21.6.1.基本介绍
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作。
21.6.2.官网
https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。
比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
21.6.3.对比Hystrix
兜底方法
分为系统默认方法和用户自定义义方法两种
之前的case,限流出问题后,都是用sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
我们能不能自定?类似hystrix,某个方法出问题了,就找对应的兜底降级方法?
结论
从Hystrix的@Command 到@SentinelResource
21.6.4.配置限流处理方法
默认:com.alibaba.csp.sentinel.slots.block.BlockException
sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
@Controller
@Slf4j
public class FlowLimitController {
@RequestMapping("/testHotKey")
@ResponseBody
@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException exception) {
return "-----dealHandler_testHotKey";
}
}
@SentinelResource注解标签类似于Hystrix的@Command。value是资源名称;blockHandler是如果触发Sentinel的限流规则就会触发的取代testHotKey的兜底函数。
21.6.5.配置热点限流规则
![](https://i-blog.csdnimg.cn/blog_migrate/e3068182effb168322b01d345ad5a66b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ea2747158fb79c41ccae18e772c0ce89.png)
21.6.5.1.配置说明1
配置内容
@RequestMapping("/testHotKey")
@ResponseBody
@SentinelResource(value = "testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "------testHotKey";
}
异常打到了前台用户界面看到,不友好。
![](https://i-blog.csdnimg.cn/blog_migrate/63cb9e142c096e210c090399d4163041.png)
21.6.5.2.配置说明2
@RequestMapping("/testHotKey")
@ResponseBody
@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "------testHotKey";
}
方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理。调用自定义的降级处理方法dealHandler_testHotKey
![](https://i-blog.csdnimg.cn/blog_migrate/518c7bb9787b83abfe8c800f38a5009d.png)
21.6.6.测试
正常访问,低于配置的降级规则:(携带了对应函数中的第一个参数,且在一秒访问次数超过1次就会触发)
![](https://i-blog.csdnimg.cn/blog_migrate/0cd4593cde267a188cab135c0ca1487e.png)
触发降级规则调用降级处理函数:
![](https://i-blog.csdnimg.cn/blog_migrate/5d6cbf8efe083df3ca48a8ccced6c685.png)
21.6.7.参数例外项
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
21.6.7.1.新增要求
![11212934283](https://i-blog.csdnimg.cn/blog_migrate/a8dc4849590d83d14592f7de19f757ed.png)
21.6.7.2.配置及其说明
![](https://i-blog.csdnimg.cn/blog_migrate/0dd0a5f08b38a33c6e517ca95cc2214d.png)
热点参数的注意点,参数必须是基本类型
资源名为testHotKey请求,索引序号为0的参数,参数值为5时,限流阈值为10,当不为5时,限流阈值为1。
添加按钮不能忘记
21.6.7.3.测试
![11213131582](https://i-blog.csdnimg.cn/blog_migrate/b46e7e203b3696059e73fd5906a05ed4.png)
21.6.8.@SentinelResource不处理程序异常
@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException,int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管
总结:@SentinelResource主管配置出错,运行出错该走异常走异常
21.7.系统规则
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
21.7.1.基础概念
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
21.7.2.各项配置参数说明
![](https://i-blog.csdnimg.cn/blog_migrate/09b113cf7ca55a6685b6232050ae27c7.png)
21.8.@SentinelResource
21.8.1.按资源名称限流+后续处理
21.8.1.1.启动Nacos、Sentinel
http://localhost:8848/nacos/#/login
java -jar sentinel-dashboard-1.7.0.jar
21.8.1.2.修改Module cloudalibaba-sentinel-service8401
-
pom.xml
新增引入cloud-api-commons
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.stonebridge.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
application.yaml不变
-
业务类
@Controller public class RateLimitController { @RequestMapping("/byResource") @ResponseBody @SentinelResource(value = "byResource", blockHandler = "handleException") public CommonResult byResource() { return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用"); } }
21.8.1.3.配置流控规则
-
配置
-
图形配置和代码关系
表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流
21.8.1.4.测试
-
1秒钟点击1下
-
超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生
21.8.1.5.额外问题
此时关闭问服务8401,Sentinel控制台,流控规则消失。
21.8.2.按照Url地址限流+后续处理
通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息
21.8.2.1.业务类RateLimitController
@Controller
public class RateLimitController {
@RequestMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl() {
return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
}
}
21.8.2.2.Sentinel控制台配置
![](https://i-blog.csdnimg.cn/blog_migrate/0e45520e65908589470339aee942d6fb.png)
21.8.2.3.测试
频繁点击http://localhost:8401/rateLimit/byUrl
会返回Sentinel自带的限流处理结果
21.8.3.上面兜底方案面临的问题
- 系统默认的,没有体现我们自己的业务要求。
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
- 每个业务方法都添加一个兜底的,那代码膨胀加剧。
- 全局统一的处理方法没有体现。
21.8.4.用户自定义限流处理逻辑
21.8.4.1.创建CustomerBlockHandler类自定义限流处理类
定义的方法必须在是public static修饰
public class CustomerBlockHandler {
public static CommonResult handleException1(BlockException exception) {
return new CommonResult(1234, "自定义的限流处理信息......CustomerBlockHandler");
}
public static CommonResult handleException2(BlockException exception) {
return new CommonResult(4567, "自定义的限流处理信息......CustomerBlockHandler");
}
}
21.8.4.2.RateLimitController
@Controller
public class RateLimitController {
@RequestMapping("/rateLimit/TestBlockHandler")
@ResponseBody
@SentinelResource(value = "TestBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult TestBlockHandler() {
return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
}
}
21.8.4.3.启动微服务后先调用一次
http://localhost:8401/rateLimit/customerBlockHandler
21.8.4.4.Sentinel控制台配置
![](https://i-blog.csdnimg.cn/blog_migrate/c8706e0bf5b1866c4c608eddb6fdd27d.png)
21.8.4.5.测试
-
正常访问
-
触发限流
21.8.4.6.说明
![](https://i-blog.csdnimg.cn/blog_migrate/046eed0167fb7f0bec715e0ad164229d.png)
21.8.5.更多注解属性说明
![](https://i-blog.csdnimg.cn/blog_migrate/763834b105d1054599472d6bc2264fb5.png)
21.9.服务熔断功能
sentinel整合ribbon+openFeign+fallback
21.9.1.测试环境:创建服务提供者
创建项目cloudalibaba-provider-payment9003/9004
21.9.1.1.新建项目cloudalibaba-provider-payment9003
![](https://i-blog.csdnimg.cn/blog_migrate/48c55c135f3a00cc98f905e873f8f79c.png)
21.9.1.2.pom.xml
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
21.9.1.3.application.yaml
server:
port: 9002
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
# 换成nginx的1111端口,做集群
server-addr: 192.168.174.148:1111
management:
endpoints:
web:
exposure:
include: '*'
21.9.1.4.主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
21.9.1.5.业务类
@Controller
public class PaymentController {
@Value("${server.port}")
private String serverPort;
public static HashMap<Long, Payment> hashMap = new HashMap<>();
static {
hashMap.put(1L, new Payment(1L, "28a8c1e3bc2742d8848569891fb42181"));
hashMap.put(2L, new Payment(2L, "bba8c1e3bc2742d8848569891ac32182"));
hashMap.put(3L, new Payment(3L, "6ua8c1e3bc2742d8848569891xt92183"));
}
@RequestMapping(value = "/paymentSQL/{id}")
@ResponseBody
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
Payment payment = hashMap.get(id);
CommonResult<Payment> result = new CommonResult(200, "from mysql,serverPort: " + serverPort, payment);
return result;
}
}
21.9.1.6.启动访问测试
http://localhost:9003/paymentSQL/1
![](https://i-blog.csdnimg.cn/blog_migrate/c03ef17e68ac50b522d407b085e2d415.png)
21.9.1.7.创建项目cloudalibaba-provider-payment9004
参照cloudalibaba-provider-payment9003创建项目cloudalibaba-provider-payment9004
21.9.2.测试环境:创建服务消费者
21.9.2.1.新建cloudalibaba-consumer-nacos-order84
![](https://i-blog.csdnimg.cn/blog_migrate/a846bf2f5607f0407c66158ccdf47343.png)
21.9.2.2.pom.xml文件
<dependencies>
<!--SpringCloud ailibaba nacos -->
<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>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.stonebridge.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
21.9.2.3.application.yaml
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
21.9.2.4.主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
21.9.2.5.配置类
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
21.9.2.6.Controller
@Controller
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@ResponseBody
@SentinelResource(value = "fallback")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
}
21.9.2.7.启动测试
访问:http://localhost:84/consumer/fallback/1
![](https://i-blog.csdnimg.cn/blog_migrate/de580667edaeab3ecf28b30d2726c394.png)
21.9.3.Ribbon系列
sentinel整合ribbon+openFeign+fallback
21.9.3.1.没有任何配置
访问:http://localhost:84/consumer/fallback/4
![](https://i-blog.csdnimg.cn/blog_migrate/082565fc6af5c0fbbd6d41337fbb80c0.png)
没有任何配置,给客户error页面,不友好
21.9.3.2.只配置fallback
fallback负责业务异常
-
代码实现
@RequestMapping("/consumer/fallback/{id}") @ResponseBody @SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback负责业务异常 public CommonResult<Payment> fallback(@PathVariable Long id) { CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id); if (id == 4) { throw new IllegalArgumentException("IllegalArgumentException,非法参数异常...."); } else if (result.getData() == null) { throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } public CommonResult handlerFallback(@PathVariable Long id, Throwable e) { Payment payment = new Payment(id, "null"); return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 " + e.getMessage(), payment); }
-
图解
-
测试
-
正常访问:http://localhost:84/consumer/fallback/2
-
非法参数异常http://localhost:84/consumer/fallback/4
-
空指针异常:http://localhost:84/consumer/fallback/7
-
21.9.3.3.只配置blockHandler
-
代码实现
@RequestMapping("/consumer/fallback/{id}") @ResponseBody @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流 public CommonResult<Payment> fallback(@PathVariable Long id) { CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id); if (id == 4) { throw new IllegalArgumentException("非法参数异常...."); } else if (result.getData() == null) { throw new NullPointerException("NullPointerException,该ID没有对应记录"); } return result; } public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) { Payment payment = new Payment(id, "null"); return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水: blockException " + blockException.getMessage(), payment); }
-
图解配置
-
配置限流
-
测试
-
访问:http://localhost:84/consumer/fallback/7两次,出现异常两次进入限流
-
再次访问:http://localhost:84/consumer/fallback/7
-
21.9.3.4.fallback和blockHandler都配置
-
代码实现
@Controller @Slf4j public class CircleBreakerController { public static final String SERVICE_URL = "http://nacos-payment-provider"; @Resource private RestTemplate restTemplate; @RequestMapping("/consumer/fallback/{id}") @ResponseBody @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler") public CommonResult<Payment> fallback(@PathVariable Long id) { CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id); if (id == 4) { throw new IllegalArgumentException("IllegalArgumentException,非法参数异常...."); } else if (result.getData() == null) { throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } //本例是fallback public CommonResult handlerFallback(@PathVariable Long id, Throwable e) { Payment payment = new Payment(id, "null"); return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 " + e.getMessage(), payment); } //本例是blockHandler public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) { Payment payment = new Payment(id, "null"); return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水: blockException " + blockException.getMessage(), payment); } }
-
配置图解
-
配置流控规则
-
访问测试
-
正常访问:http://localhost:84/consumer/fallback/1
-
频繁快速点击访问触发流控规则
触发流控规则调用blockHandler配置的函数
-
访问会抛出异常的地址触发fallback
抛出异常的地址触发fallback配置的函数
-
-
说明
若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
21.9.3.5.忽略属性exceptionsToIgnore
-
代码实现
@RequestMapping("/consumer/fallback/{id}") @ResponseBody @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) public CommonResult<Payment> fallback(@PathVariable Long id) { CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id); if (id == 4) { throw new IllegalArgumentException("IllegalArgumentException,非法参数异常...."); } else if (result.getData() == null) { throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; }
-
配置图解
-
测试
访问会报IllegalArgumentException的地址:http://localhost:84/consumer/fallback/4
21.9.4.Feign系列
21.9.4.1.创建Feign的服务消费服务
-
创建项目cloudalibaba-consumer-nacos-order85
-
pom.xml
<dependencies> <!--SpringCloud ailibaba nacos --> <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> <!--SpringCloud openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.stonebridge.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--日常通用jar包配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
application.yaml
激活Sentinel对Feign的支持
server: port: 85 spring: application: name: nacos-order-consumer cloud: nacos: discovery: #Nacos服务注册中心地址 server-addr: localhost:8848 sentinel: transport: #配置Sentinel dashboard地址 dashboard: localhost:8080 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719 management: endpoints: web: exposure: include: '*' # 激活Sentinel对Feign的支持 feign: sentinel: enabled: true
-
主启动类
添加@EnableFeignClients启动Feign的功能
@EnableDiscoveryClient @SpringBootApplication @EnableFeignClients public class OrderNacosMain85 { public static void main(String[] args) { SpringApplication.run(OrderNacosMain85.class, args); } }
-
业务类
-
controller
@Controller @Slf4j public class CircleBreakerController { @Resource private PaymentService paymentService; @RequestMapping(value = "/consumer/openfeign/{id}") @ResponseBody public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) { if (id == 4) { throw new RuntimeException("没有该id"); } return paymentService.paymentSQL(id); } }
-
service
-
PaymentService
@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackService.class) public interface PaymentService { @RequestMapping(value = "/paymentSQL/{id}") CommonResult<Payment> paymentSQL(@PathVariable("id") Long id); }
-
PaymentFallbackService
PaymentService对应的异常处理处理方法
@Component public class PaymentFallbackService implements PaymentService { @Override public CommonResult<Payment> paymentSQL(Long id) { return new CommonResult<>(444, "服务降级返回,没有该流水信息", new Payment(id, "errorSerial......")); } }
-
-
-
测试
启动的项目消费侧微服务(端口85),服务提供微服务(端口9004)
访问:http://localhost:85/consumer/openfeign/1
21.9.4.2.测试消费侧自动降级
因为启动的项目只有消费侧端口为85,服务侧服务提供者9004。此时关闭服务侧服务提供者,触发消费侧降级。
![](https://i-blog.csdnimg.cn/blog_migrate/48a3734f86881f63e3e1c90ff09973e1.png)
21.9.5.熔断框架比较
![](https://i-blog.csdnimg.cn/blog_migrate/6712e237f56fcdcf6b70b0de078aaa52.png)
21.10.规则持久化
21.10.1.是什么
一旦我们重启应用,在sentinel为其接口创建的流控规则,生产环境需要将配置规则进行持久化。
21.10.2.怎么用
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效。
21.10.3.具体实现步骤
修改cloudalibaba-sentinel-service8401
21.10.3.1.pom.xml新增引入
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
21.10.3.2.application.yaml
添加Nacos数据源配置
spring:
cloud:
sentinel:
transport:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
![](https://i-blog.csdnimg.cn/blog_migrate/d306cd6533a5949ecc7a390cde2e661d.png)
21.10.3.3.添加Nacos业务规则配置
![](https://i-blog.csdnimg.cn/blog_migrate/78161e95e8511ea02e01f49f16ce0266.png)
[
{
"resource": "/rateLimit/byUrl",
"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:是否集群。
21.10.3.4.启动8401后刷新sentinel发现业务规则有了
![](https://i-blog.csdnimg.cn/blog_migrate/e3db142778aae91d06568c94eedcd8bd.png)
21.10.3.5.快速访问测试接口
http://localhost:8401/rateLimit/byUrl
出现限流
![](https://i-blog.csdnimg.cn/blog_migrate/cdef0aa223c1bec5c615d0c5d0211b91.png)
21.10.3.6.停止8401再看sentinel
![](https://i-blog.csdnimg.cn/blog_migrate/9e6156c206f463f0b160d5b37abc076e.png)
21.10.3.7.重新启动8401再看sentinel
多次调用:http://localhost:8401/rateLimit/byUrl
重新配置出现了,持久化验证通过
22.SpringCloud Alibaba Seata处理分布式事务
22.1.分布式事务问题
分布式前:单机单库没这个问题
我们的系统从1:1 (单机单库) 到 1:N(多台数据库) 到现在的 N:N多系统多数据库的
分布式之后
单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
![](https://i-blog.csdnimg.cn/blog_migrate/0908a7305c3027af47889ccb64ba36f6.png)
一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题。
22.2.Seata简介
22.2.1.是什么
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
官网地址:http://seata.io/zh-cn/
![](https://i-blog.csdnimg.cn/blog_migrate/cf2cbcdd4c4d076b6d4b2c67a5a3d4fa.png)
22.2.2.功能
一个典型的分布式事务过程
2.2.2.1.分布式事务处理的三组件模型
分布式事务处理过程的一ID+三组件模型
-
Transaction ID XID
全局唯一的事务ID。
比如一次事务的开始,比如一次操作涉及三个数据库,三个数据源。每一次事务的所有操作都有全局唯一的相同的事务id
-
3组件概念
-
Transaction Coordinator (TC)
事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
-
Transaction Manager ™
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
-
Resource Manager (RM)
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
-
2.2.2.2.处理过程
- TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
- XID 在微服务调用链路的上下文中传播;
- RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
- TM 向 TC 发起针对 XID 的全局提交或回滚决议;
- TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
![](https://i-blog.csdnimg.cn/blog_migrate/220c916171208659646da3a003a55652.png)
直白解释:
一个班60个同学,将Microservices想象成分为20人一组,一共三组。TC是授课老师,TM是班主任。
首先TM(班主任)向TC(授课老师)申请开启一个全局事务(新的课程),XID就是新的课程的微信群号,TM(班主任)将XID(课程的微信群号)上下传播。
所有的同学要向TC(授课老师)注册分支事务,将其纳入全局事务管理。
TM(班主任)向TC(授课老师)发起全局提交或者回滚事务。
上课完毕TC(授课老师)在XID(课程的微信群号)下管辖的全部分支事务完成提交或回滚请求。
22.2.3.下载
发布说明: https://github.com/seata/seata/releases
22.2.4.怎么用
本地@Transactional
全局@GlobalTransactional
SEATA 的分布式交易解决方案
![](https://i-blog.csdnimg.cn/blog_migrate/ff5b50c15038fef174df2c5bb24f54ca.png)
22.3.Seata-Server安装
22.3.1.官网地址
http://seata.io/zh-cn/
22.3.2.下载版本
https://github.com/seata/seata/releases
22.3.3.配置文件
seata-server-1.4.0.zip解压到指定目录并修改conf目录下的file.conf配置文件
-
先备份原始file.conf文件
-
修改file.conf
主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接信息
22.4.订单/库存/账户业务数据库准备
22.5.订单/库存/账户业务微服务准备
22.5.1.业务需求
下订单->减库存->扣余额->改(订单)状态
22.5.2.新建订单Order-Module
![18210535185](https://i-blog.csdnimg.cn/blog_migrate/e400e24c13ac07ac96f1a971a3d94d02.png)
<strong></strong>
<font color=red><strong></strong></font>
<font color=red> </font>
rMode": false
}
]
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
#### 21.10.3.4.启动8401后刷新sentinel发现业务规则有了
<img src="https://i-blog.csdnimg.cn/blog_migrate/e3db142778aae91d06568c94eedcd8bd.png" style="zoom:80%;" />
#### 21.10.3.5.快速访问测试接口
http://localhost:8401/rateLimit/byUrl
出现限流
<img src="https://i-blog.csdnimg.cn/blog_migrate/cdef0aa223c1bec5c615d0c5d0211b91.png" />
#### 21.10.3.6.停止8401再看sentinel
<img src="https://i-blog.csdnimg.cn/blog_migrate/9e6156c206f463f0b160d5b37abc076e.png" />
#### 21.10.3.7.重新启动8401再看sentinel
多次调用:http://localhost:8401/rateLimit/byUrl
重新配置出现了,持久化验证通过
# 22.SpringCloud Alibaba Seata处理分布式事务
## 22.1.分布式事务问题
分布式前:单机单库没这个问题
我们的系统从1:1 (单机单库) 到 1:N(多台数据库) 到现在的 N:N多系统多数据库的
分布式之后
单体应用被拆分成微服务应用,原来的三个模块被拆分成**三个独立的应用**,分别使用**三个独立的数据源**,业务操作需要调用三个服务来完成。此时<font color=red><strong>每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。</strong></font>
<img src="https://i-blog.csdnimg.cn/blog_migrate/0908a7305c3027af47889ccb64ba36f6.png" />
<font color=red><strong>一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题。</strong></font>
## 22.2.Seata简介
### 22.2.1.是什么
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
官网地址:http://seata.io/zh-cn/
<img src="https://i-blog.csdnimg.cn/blog_migrate/cf2cbcdd4c4d076b6d4b2c67a5a3d4fa.png" style="zoom:80%;" />
### 22.2.2.功能
一个典型的分布式事务过程
#### 2.2.2.1.分布式事务处理的三组件模型
分布式事务处理过程的一ID+三组件模型
- Transaction ID XID
全局唯一的事务ID。
<font color=red><strong>比如一次事务的开始,比如一次操作涉及三个数据库,三个数据源。每一次事务的所有操作都有全局唯一的相同的事务id</strong></font>
- 3组件概念
1. Transaction Coordinator (TC)
事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
2. Transaction Manager (TM)
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
3. Resource Manager (RM)
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
#### 2.2.2.2.处理过程
1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
2. XID 在微服务调用链路的上下文中传播;
3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
4. TM 向 TC 发起针对 XID 的全局提交或回滚决议;
5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
<img src="https://i-blog.csdnimg.cn/blog_migrate/220c916171208659646da3a003a55652.png" style="zoom:80%;" />
直白解释:
一个班60个同学,将Microservices想象成分为20人一组,一共三组。TC是授课老师,TM是班主任。
首先TM(班主任)向TC(授课老师)申请开启一个全局事务(新的课程),XID就是新的课程的微信群号,TM(班主任)将XID(课程的微信群号)上下传播。
所有的同学要向TC(授课老师)注册分支事务,将其纳入全局事务管理。
TM(班主任)向TC(授课老师)发起全局提交或者回滚事务。
上课完毕TC(授课老师)在XID(课程的微信群号)下管辖的全部分支事务完成提交或回滚请求。
### 22.2.3.下载
发布说明: https://github.com/seata/seata/releases
### 22.2.4.怎么用
本地@Transactional
全局@GlobalTransactional
SEATA 的分布式交易解决方案
<img src="https://i-blog.csdnimg.cn/blog_migrate/ff5b50c15038fef174df2c5bb24f54ca.png" />
## 22.3.Seata-Server安装
### 22.3.1.官网地址
http://seata.io/zh-cn/
### 22.3.2.下载版本
https://github.com/seata/seata/releases
### 22.3.3.配置文件
seata-server-1.4.0.zip解压到指定目录并修改conf目录下的file.conf配置文件
1. 先备份原始file.conf文件
2. 修改file.conf
主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接信息
## 22.4.订单/库存/账户业务数据库准备
## 22.5.订单/库存/账户业务微服务准备
### 22.5.1.业务需求
下订单->减库存->扣余额->改(订单)状态
### 22.5.2.新建订单Order-Module
<img src="https://i-blog.csdnimg.cn/blog_migrate/e400e24c13ac07ac96f1a971a3d94d02.png" alt="18210535185" style="zoom:80%;" />
[外链图片转存中...(img-jQhXNFVr-1635212140008)]
[外链图片转存中...(img-C2jW3One-1635212140009)]
```html
<strong></strong>
<font color=red><strong></strong></font>
<font color=red> </font>