SpringCloud Alibaba
SpringCloud Alibaba
0.入门
- 服务限流降级:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 - 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
1.Nacos服务注册和配置中心
1.1 简介
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos: Dynamic Naming and Configuration Service
Nacos = Eureka+Config +Bus
下载解压 ------ 直接运行bin目录下的startup.cmd ----访问http://localhost:8848/nacos
默认账号密码都是nacos
1.2 服务注册中心
服务提供者
父工程POM
<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
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
YML
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
主启动 @EnableDiscoveryClient 业务类
服务消费者
POM nacos支持负载均衡 ------ Ribbon
YML
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
主启动 @EnableDiscoveryClient ----- 业务类RestTemplate ----- 配置类
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
服务注册中心对比
Nacos 支持AP和CP模式的切换
何时选择使用何种模式?
- 如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。
- 当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
- 如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
- CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
1.3 服务配置中心
1.3.1 基础配置
POM
<!--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>
bootstrap优先级高于application
bootstrap
# 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格式的配置
group: DEV_GROUP
namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4
application
spring:
profiles:
active: dev # 表示开发环境
业务 ------- @RefreshScope //支持Nacos的动态刷新功能。
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天,此外还有一键回滚功能,回滚操作将会触发配置更新
1.3.2 分类配置
默认情况:
Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT
- Namespace主要用来实现隔离
- Group可以把不同的微服务划分到同一个分组里面去
- Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分
- Instance,就是微服务的实例
三种方案加载配置:
DataID方案
-
默认空间+默认分组+DataID
-
通过spring.profile.active属性就能进行多环境下配置文件的读取
-
spring: profiles: active: dev #DataID
Group方案
-
通过Group实现环境区分
-
spring: cloud: nacos: config: group:DEV_GROUP -
spring: profiles: active: info #DataID
Namespace方案
-
新建 ----- 根据命名空间ID配置
-
spring: cloud: nacos: config: namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4 -
spring: profiles: active: info #DataID
1.4 集群与持久化配置!
默认Nacos使用嵌入式数据库derby实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
derby到mysql切换配置:
- nacos-server-1.1.4\nacos\conf目录下找到sql脚本 ------ 建表
- nacos-server-1.1.4\nacos\conf目录下找到application.properties
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
db.user=root
db.password=123456
-------------------------Linux Nacos+MySQL生产环境配置-------------------------
Linux服务器上mysql数据库配置
- nacos-mysql.sql
application.properties 配置
- 同上
Linux服务器上nacos的集群配置cluster.conf
- 不同服务端口号
- 必须是 Linux命令hostname -i能够识别的IP
编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口
- /mynacos/nacos/bin 目录下有startup.sh
- 传递不同的端口号启动不同的nacos实例-----命令:./startup.sh -p 3333
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;
}
截止到此处,1个Nginx+3个nacos注册中心+1个mysql
2.Sentinel实现熔断与限流
2.1 Sentinel
分布式系统的流量防卫兵
Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
2.2 安装使用
后台 :直接运行 java -jar sentinel-dashboard-1.7.0.jar
前台 :http://localhost:8080 登录账号密码均为sentinel
2.3 演示工程
nacos + sentinel
建模块 -------- 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>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
主启动 @EnableDiscoveryClient
Sentinel采用的懒加载
2.4 流控规则
资源名:唯一名称,默认请求路径
针对来源:sentinel可以针对调用者进行限流,填写微服务名,默认default
阈值类型/单机阈值:
- QPS:每秒的请求数量 当调用该api的QPS达到阈值时进行限流
- 线程数:当调用该api的线程数达到阈值时进行限流
不需要集群
流控模式:
- 直接:api达到条件直接限流
- 关联:关联的资源达到阈值时限流
- 链路:只记录指定链路上的流量(指定资源从入口进来的流量,达到阈值就限流)
流控效果:
- 快速失败:直接失败抛异常
- Warm Up:根据codeFactor冷加载因子(默认3)
- 排队:阈值必须设置为QPS
2.4.1 流控模式
直接----------系统默认
- QPS直接失败 ----- 请求拦截在外
- 线程数直接失败 ----- 进入运行的线程数
- 表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
- Blocked by Sentinel (flow limiting) ------ 自定义处理
关联
- 当关联的资源达到阈值时,就限流自己
- Blocked by Sentinel (flow limiting)
链路
- 多个请求调用了同一个微服务
2.4.2 流控效果
直接->快速失败(默认的流控处理)
预热
- 默认coldFactor为3
- 请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值
排队等待
- 匀速排队,阈值必须设置为QPS
2.5 降级规则
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),限制对这个资源的调用,让请求快速失败,避免影响其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
Sentinel的断路器是没有半开状态的
半开的状态系统自动去检测是否请求有异常:没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix
2.5.1 RT(秒级)
- 平均响应时间
- 超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
- 窗口期过后关闭断路器
- RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
2.5.2 异常比列(秒级)
- QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级
- 时间窗口结束后,关闭降级
2.5.3 异常数(分钟级)
- 异常数(分钟统计)超过阈值时,触发降级
- 时间窗口结束后,关闭降级
- 时间窗口一定要大于等于60秒
2.6 热点key限流
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作。
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
- 限流模式只支持QPS模式
- @SentinelResource注解的方法参数索引从0开始
- 方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
参数例外项
- 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
- 热点参数的注意点,参数必须是基本类型或者String
2.7 系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt估算得出。设定参考值一般是CPU cores * 2.5。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
2.8 @SentinelResource
按资源名限流
- 配置流控规则 ---- 自定义限流-----流控规则非持久
按URL地址限流
通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息
- 系统默认的,没有体现我们自己的业务要求。
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
- 每个业务方法都添加一个兜底的,那代码膨胀加剧。
- 全局统一的处理方法没有体现。
客户自定义限流处理逻辑
- 创建CustomerBlockHandler类用于自定义限流处理逻辑
所有的代码都要用try-catch-finally方式进行处理
Sentinel主要有三个核心Api:
- SphU定义资源
- Tracer定义统计
- ContextUtil定义了上下文
2.9 服务熔断
sentinel整合ribbon+openFeign+fallback
Ribbon
- 提供者 + 消费者
- blockHandler 断路器打开,断电跳闸,系统被保护
- fallback
- 业务类:
- @SentinelResource(value = "fallback") 没有配置
- @SentinelResource(value = "fallback",fallback = "handlerFallback") fallback只负责业务异常
- @SentinelResource(value = "fallback",blockHandler = "blockHandler") blockHandler 只负责sentinel 控制台配置违规
- @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler") 若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
- 忽略属性exceptionsToIgnore = {IllegalArgumentException.class} 程序异常打到前台
Feign
-
消费者调用提供者
-
# 激活Sentinel对Feign的支持 feign: sentinel: enabled: true -
业务:
- 带@FeignClient注解的业务接口
- fallback = PaymentFallbackService.class
- 添加@EnableFeignClients启动Feign的功能
2.10 规则持久化
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
POM
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
添加Nacos数据源配置
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
添加Nacos业务规则配置
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群
3.Seata处理分布式事务
单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
3.1 Seata
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
典型分布式事务
- Transaction ID XID 全局唯一的事务ID
- 3组件:
- Transaction Coordinator (TC) 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
- Transaction Manager (TM) 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
- Resource Manager (RM) 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
处理过程
- TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
- XID 在微服务调用链路的上下文中传播;
- RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
- TM 向 TC 发起针对 XID 的全局提交或回滚决议;
- TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
使用
- 本地@Transactional
- 全局@GlobalTransactional
3.2 Seata-Server安装
下载解压
修改conf目录下的file.conf配置文件
- 先备份原始file.conf文件
- 主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接信息
新建数据库seata -----db_store.sql
修改conf目录下的registry.conf配置文件:
- 指明注册中心为nacos,及修改nacos连接信息
启动seata-server ------ softs\seata-server-0.9.0\seata\bin\seata-server.bat
3.3 订单、库存、账户案例
分布式事务业务说明:一个订单服务,一个库存服务,一个账户服务
- 当用户下单时,会在订单服务中创建一个订单,
- 然后通过远程调用库存服务来扣减下单商品的库存,
- 再通过远程调用账户服务来扣减用户账户里面的余额,
- 最后在订单服务中修改订单状态为已完成。
该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。
3.3.1 数据库
创建业务数据库:
- seata_order:存储订单的数据库
- seata_storage:存储库存的数据库
- seata_account:存储账户信息的数据库
按照上述3库分别建对应的回滚日志表
3.3.2 微服务业务
下订单->减库存->扣余额->改(订单)状态
Order模块
- seata-order-service2001 --- POM --- yml --- file.conf、registry.conf
- domain --- Dao接口及实现 mapper ---Service接口及实现 --- Controller
- Config配置 --- MyBatisConfig --- DataSourceProxyConfig使用Seata对数据源进行代理
- 主启动 --- @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 取消数据源的自动创建
Storage模块
Account模块
3.3.3 测试
超时异常,没加@GlobalTransactional
- 当库存和账户金额扣减后,订单状态并没有设置为已经完成,没有从零改为1
- 而且由于feign的重试机制,账户余额还有可能被多次扣减
超时异常,添加@GlobalTransactional
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
下单后数据库数据并没有任何改变
3.4 原理
分布式事务的执行流程
- TM 开启分布式事务(TM 向 TC 注册全局事务记录);
- 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 )
- TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务)
- TC 汇总事务信息,决定分布式事务是提交还是回滚
- TC 通知所有 RM 提交/回滚 资源,事务二阶段结束
AT模式如何做到对业务的无侵入
- 一阶段,Seata 会拦截“业务 SQL”
- 解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”
- 执行“业务 SQL”更新业务数据,在业务数据更新之后,其保存成“after image”,最后生成行锁。
- 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
- 二阶段如是顺利提交的话,
- 因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
- 二阶段回滚:
- 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。
- 回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,
- 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

浙公网安备 33010602011771号