Spring Cloud Alibaba
1.入門簡介
1.1 為什么會出現SpringCloud alibaba
spring cloud Netflix進入維護模式
1.2 是什么
2018.10.31, Spring cloud Alibaba正式入駐了Spring Cloud官方范訓器,并在Maven中央庫發布了第一個版本.
1.3 能干嘛
服務限流降級
默認支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降級功能的接入,可以在運行時通過控制臺實時修改限流降級規則,還支持查看限流降級Metrics監控
服務注冊與發現
適配Spring Cloud服務注冊與發現標準,默認集成了Ribbon的支持
分布式配置管理
支持分布式系統中的外部化配置,配置更改時自動重繪
訊息驅動能力
基于Spring Cloud Stream為微服務應用構建訊息驅動能力
阿里云存盤物件
阿里云提供的海量、安全、低成本、高可靠的云存盤服務.支持在任何應用、任何時間、任何地點存盤和訪問任意型別的資料
分布式任務調度
提供秒級、精準、高可靠、高可用的定時(基于Cron運算式)任務調度服務.同時提供分布式的任務執行模型,如網格任務.網格任務支持海量子任務均勻分配到所有Worker(schedulerx-client)上執行
1.4 怎么用
Sentinel
阿里巴巴開源產品,把流量作為切入點,從流量控制,熔斷降級,系統負載保護等多個維度保護服務的穩定性.
Nacos
阿里巴巴開源產品,一個更易于構建云原生應用的動態服務發現,配置管理和服務管理平臺.
RocketMQ
Apache RocketMQ基于Java的高性能,高吞吐量的分布式訊息和流計算平臺.
Dubbo
Apache Dubbo是一款高性能的Java RPC框架.
Seata
阿里巴巴開源產品,一個易于使用的高性能微服務分布式事務解決方案.
Alibaba Cloud OSS
阿里云物件存盤服務器(Object Storage Service,簡稱OSS),是阿里云提供的海量,安全,低成本,高可靠的云存盤服務.
Alibaba Cloud Schedulerx
阿里中間件團隊開發的一款分布式調度產品,支持周期性的任務與固定時間點觸發任務.
2. SpringCloud Alibaba Nacos服務注冊和配置中心
2.1 Nacos(Naming Configuration Service)簡介
2.1.1 是什么
- 一個更易于構建云遠程應用的動態服務發現,配置管理和服務管理平臺
- Nacos就是注冊中心 + 配置中心的組合 等價于 Nacos = Eureka + Config + Bus
2.1.2 能干嘛
- 替代Eureka做服務注冊中心
- 替代Config做服務配置中心
2.1.3 注冊中心比較
| 服務注冊與發現框架 | CAP模型 | 控制臺管理 | 社區活躍度 |
|---|---|---|---|
| Eureka | AP | 支持 | 低(2.x版本閉源) |
| Zookeeper | CP | 不支持 | 中 |
| Consul | CP | 支持 | 高 |
| Nacos | AP/CP | 支持 | 高 |
- nacos和CAP


2.2 Nacos安裝
- 本地Java8+Maven環境已經OK
- 先從官網下載Nacos
- 解壓安裝包,直接運行bin目錄下的startup.cmd
- 運行成功后訪問http://localhost:8848/nacos
- 默認賬號nacos nacos
2.3 Nacos作為服務注冊中心
2.3.1 pom
<dependencies>
<!--nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--自己的公用jar包-->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
</dependency>
</dependencies>
2.3.2 yml檔案
server:
port: 9001
spring:
application:
name: nacos-payment
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
management:
endpoints:
web:
exposure:
include: '*'
2.3.3 主啟動類
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class NacosPaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(NacosPaymentMain9001.class,args);
}
}
2.3.4 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’
2.4 Nacos作為服務配置中心
2.4.1 配置中心dataID設定
檔案后綴名不能寫yml,要寫yaml

2.4.2 pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-alibaba-nacos-config-client3377</artifactId>
<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>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.4.3 yaml檔案
# bootstrap.yml
# 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 #指定組態檔分組GROUP
namespace: 357392ad-2083-4c4a-afb1-945dc549080f #通過命名空間ID指定命名空間
#${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml
# application.yml
spring:
profiles:
active: dev # 表示開發環境
2.4.4 主啟動類
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigClientMain3377 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3377.class,args);
}
}
2.4.5 業務類
package com.atguigu.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* 通過spring cloud的原生注解@RefreshScope 實作配置的自動重繪功能
* */
@RestController
@Slf4j
@RefreshScope //支持nacos的動態重繪功能
@RequestMapping("/config")
public class ConfignClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/info")
public String getConfigInfo(){
return "config info : " + configInfo;
}
}
2.4.6 分類配置
2.4.6.1 namespace+Group+DataID三者關系
-
是什么
- 類似于Java里面的package名和類名
- 最外層的namespace是可以用于魚粉部署環境的,Group和DataID邏輯上區分兩個目標物件
-
三者情況
![images\image-20201223140229213.png)]](https://img.uj5u.com/2021/01/01/211098011333014.png)
-
默認情況
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,就是微服務的實體.
2.4.6.1 三種法案加載配置
-
DataID方案
-
指定spring.profile.active和組態檔的DataID來使不同環境下讀取不同的配置
-
默認空間+默認分組+新建dev和test兩個DataID
- 新建dev配置DataID----------nacos-config-client-dev.yaml
- 新建test配置DataID----------nacos-config-client-test.yaml
-
通過spring.profile.active屬性就能進行多環境下檔案的讀取
spring: profiles: active: dev # 表示開發環境 #active: test #表示測驗環境
-
-
Group方案
-
通過Group實作環境區分
- 新建Group,在新建組態檔時,將DEFAULT_GROUP改為你要建的分組即可(例如DEV_GROUP)
-
在nacos圖形界面控制臺上面新建組態檔DataID
- 新建dev配置DataID----------nacos-config-client-dev.yaml
-
bootstrap+application
-
bootstrap檔案在config下指定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: DEV_GROUP #指定組態檔分組GROUP -
yml檔案
spring: profiles: active: dev # 表示開發環境
-
-
-
Namespace方案
-
新建dev/test的Namespace
-
回到服務管理-服務串列查看
-
按照域名配置填寫
-
yml
-
bootstrap.yml
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 #指定組態檔分組GROUP namespace: 357392ad-2083-4c4a-afb1-945dc549080f #通過命名空間ID指定命名空間 -
application.yml
spring: profiles: active: dev # 表示開發環境
-
-
2.5 Nacos集群和持久化配置(重要)
2.5.1 說明
-
默認Nacos使用嵌入式資料庫實作資料存盤.所以,如果啟動多個默認配置下的Nacos節點,資料存盤是存在一致性問題的.為了解決這個問題,Nacos采用了集中式存盤的方式來支持集群化部署,目前只支持MySQL的存盤.
-
Nacos支持三種部署模式
-
單機模式 - 用于測驗和單機使用.
-
在0.7版本之前,在單機模式時nacos使用嵌入式資料庫實作資料的存盤,不方便觀察資料存盤的基本情況.
-
0.7版本之后增加了mysql資料源能力,具體的操作步驟:
-
安裝資料庫,版本要求: 5.6.5+
-
初始化mysql資料庫,資料庫初始化檔案:nacos-mysql.sql
-
修改conf/application.properties檔案,增加支持mysql資料源配置(目前只支持mysql),添加mysql資料源的url,用戶名和密碼.
![images\image-.png)]](https://img.uj5u.com/2021/01/01/211098011333015.png)
-
-
4. 再以單機模式啟動nacos,nacos所有寫嵌入式資料庫的資料都寫到了mysql.-
集群模式 - 用于生產環境,確保高可用.
-
多集群模式 - 用于多資料中心場景.
-
2.5.2 Nacos持久化配置解釋
-
Nacos默認自帶的是嵌入式資料庫derby
-
derby到mysql切換配置步驟
-
nacos-server-1.1.4\nacos\conf目錄下找到SQL腳本----nacos-mysql.sql-----并執行腳本
-
nacos-server-1.1.4\nacos\conf目錄下找到application.properties,添加這段配置
spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=root db.password=123456
-
-
啟動Nacos,可以看到是個全新的空記錄界面,以前是記錄進derby
-
MYSQL密碼:u3w)D9Q&wb%w
3. SpringCloud Alibaba Sentinel
| Sentinel | Hystrix |
|---|---|
| 1.單獨一個組件,可以獨立出來 | 1.需要我們程式員自己手工搭建監控平臺 |
| 2.直接界面化的細粒度統一配置 | 2.沒有一套web界面可以給我們進行更加細粒度化的配置流控,速率控制,服務熔斷,服務降級… |
- 約定 > 配置 > 編碼
- 都可以寫在代碼里面,但是我們本次還是大規模學習使用配置和注解的方式,盡量少寫代碼
3.1 安裝Sentinel控制臺
3.1.1 組成
Sentinel分為兩個部分:
- 核心庫(Java客戶端)不依賴任何框架/庫,能夠運行于所有Java運行時環境,同時對Dubbo/SpringCloud等框架也有較好的支持.
- 控制臺(Dashboard)基于SpringBoot開發,打包后可以直接運行,不需要額外的Tomcat等應用容器.
3.1.2 安裝步驟
-
下載 https://github.com/alibaba/Sentinel/releases/download/1.7.0/sentinel-dashboard-1.7.0.jar
-
運行命令
- 前提
- java 8 環境OK
- 8080埠不能被占用
- 命令 java -jar sentinel-dashboard-1.7.0.jar
- 前提
-
訪問sentinel管理界面 localhost:8080
-
pom檔案依賴
<!--alibaba sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--spring cloud alibaba sentinel-datasource-nacos 后續持久化會用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
3.2 流控規則
3.2.1 基本介紹
解釋說明:
- 資源名: 唯一名稱,默認請求路徑
- 針對來源: Sentinel可以針對呼叫者進行限流,填寫微服務名,默認default(不區分來源)
- 閾值型別/單機閾值:
- QPS(每秒鐘的請求數量): 當呼叫該api的QPS達到閾值的時候,進行限流.
- 執行緒數: 當呼叫該api的執行緒數達到閾值的時候,進行限流
- 是否集群模式: 不需要集群
- 流控模式:
- 直接: api達到限流條件時,直接限流
- 關聯: 當關聯的資源達到閾值時,就限流自己
- 鏈路: 只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流) [api級別的針對來源]
- 流控效果:
- 快速失敗: 直接失敗,拋例外
- Warm Up: 根據codeFactor(冷加載因子,默認3)的值,從閾值/codeFactor,經過預熱時長,才達到設定的QPS閾值
- 排隊等待: 勻速排隊,讓請求以勻速的速度通過,閾值型別必須設定為QPS,否則無效
3.2.2 流控模式
-
直接(默認)
表示1秒鐘內查詢一次就是OK,若超過一次,就直接-快速失敗,報默認錯誤
![images\image-20201224170933646.png)]](https://img.uj5u.com/2021/01/01/211098011333016.png)
結果: Blocked by Sentinel (flow limiting)
QPS和執行緒區別:
- QPS把敵人抵御在國門之外,超過閾值,就不讓進來
- 執行緒是放進來關門打狗,超過閾值,你進來,但是你在那給我等著.
-
關聯
設定效果
當關聯資源/testB的QPS閾值超過1時,就限流/testA的Rest訪問地址,當關聯資源達到閾值后限制配置好的資源名

-
鏈路
3.2.2 流控效果
-
直接–>快速失敗(默認的流控處理)
-
Warm up(預熱)
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WAARM_UP)方式,即預熱/冷啟動方式.當系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮.通過"冷啟動",讓通過的流量緩慢增加,在一定的時間內逐漸增加到閾值上線,給冷系統一個預熱的時間,避免冷系統被壓垮.詳細檔案可以參考流量控制 - Warm Up 檔案
-
說明: 公式: 閾值除以coldFactor(默認值為3),經過預熱時長后才會達到閾值
-
官網
- 默認coldFactor為3,即請求QPS從threshold/3開始,經預熱時長逐漸升至設定的QPS閾值.
- 限流冷啟動
-
Warm Up配置
-
默認coldFactor為3,即請求QPS從(threshold/3)開始,經多少預熱時長才逐漸升至設定的QPS閾值.
-
案例,閾值為10 + 預熱時長設定為5秒
系統初始化的閾值為10/3約等于3,即閾值剛開始為3;然后過了5秒后閾值才慢慢升高恢復到10

-
-
-
排隊等待
勻速排隊,讓請求以均勻的速度通過,閾值型別必須設定成QPS,否則無效.
設定含義: /testA每秒請求一次,超過的話就排隊等待,等待的超時時間為20000毫秒.

3.3 Sentinel降級
3.3.1 基本介紹
Sentinel熔斷降級會在呼叫鏈路中某個資源出現不穩定狀態時(例如呼叫超時或者例外比例升高),對這個資源的呼叫進行限制,讓請求快速失敗,避免影響到其他的資源而導致級聯錯誤.
擋子源被降級后,在接下來的降級時間視窗之內,對該資源的呼叫都自動熔斷(默認行為是拋出DegradeException).
Sentinel的斷路器是沒有半開狀態的
- 半開的狀態系統自動去檢測是否請求有例外,
- 沒有例外就關閉斷路器恢復使用,
- 有例外則基礎打開斷路器不可用.

-
RT(平均回應時間,秒級)
平均回應時間 超出閾值 且 在時間視窗期內通過的請求>=5, 兩個條件同時滿足后觸發降級
視窗期過后關閉斷路器
RT最大4900(更大的需要通過-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
-
例外比例(秒級)
QPS >= 5 且例外比例(秒級統計)超過閾值時,觸發降級;時間視窗結束后,關閉降級
-
例外數(分鐘級)
例外數(分鐘統計)超過閾值,觸發降級;時間視窗結束后,關閉降級
3.3.2 降級策略實戰
-
RT
-
是什么
平均回應時間(
DEGRADE_GRADE_RT): 當1s內持續進入5個請求,對應時刻的平均回應時間(秒級)均超過閾值(count,以ms為單位),那么在接下來的時間視窗(DegradeRule中的timeWindow,以s為單位)之內,對這個方法的呼叫都會自動的熔斷(拋出DegradeException).注意Sentinel默認統計的RT上線是4900ms,超出此閾值的都會算作4900ms,若需要變更此上限可以通過啟動配置項-Dcsp.sentinel.statistic.max.rt=xxx來配置.
-
測驗
-
測驗代碼
@GetMapping("/testD") public String getD(){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.info("testD 測驗RT"); return "------testD"; } -
測驗

按照上述配置:
- 永遠一秒鐘打進來10個執行緒(大于5個了)呼叫testD,我們希望200毫秒處理完本次的任務,
- 如果超過200毫秒還沒處理完,在未來一秒鐘的視窗期內,斷路器打開(保險絲跳閘)微服務不可用,保險絲跳閘斷電了
- 后續訪問量沒有那么大了,保險絲恢復正常,微服務可以訪問
-
-
-
例外比例
-
是什么
例外比例(
DEGRADE_GRADE_EXCEPTION_RATIO): 當資源的每秒請求量 >= 5,并且每秒例外總數占通過量的比值超過閾值(DegradeRule中的count)之后,資源進入降級狀態,即在接下來的時間視窗(DegradeRule中的timeWindow,以s為單位)之內,對這個方法的呼叫都會自動地回傳.例外比率的閾值范圍[0.0,1.0],代表0% - 100%.
-
配置

@GetMapping("/testE") public String getE(){ log.info("testE 測驗例外比例"); int age = 10/0; return "------testE"; }-
結論
按照上述配置,
單獨訪問一次,必然來一次報錯一次(int age = 10/0),調一次錯一次;
開啟jmeter后,直接高并發發送請求,多次呼叫達到我們的配置條件了.
斷路器開啟(保險絲跳閘),微服務不可用了,不再報錯error而是服務降級.
-
-
例外數
-
是什么
例外數(
DEGRADE_GRADE_EXCEPTION_COUNT): 當資源近1分鐘的例外數目超過閾值之后會進行熔斷.注意由于統計時間視窗是分鐘級別的,若timeWindow小于60s,則結束熔斷狀態后仍可能再進入熔斷狀態.時間視窗一定要大于等于60秒

-
代碼配置

@GetMapping("/testF") public String getF(){ log.info("testF 測驗例外數"); int age = 10/0; return "------testF"; }
-
3.4 熱點key限流
3.4.1 是什么
何為熱點?熱點即經常訪問的資料.很多時候我們希望統計某個熱點資料中訪問頻次最高的Top K資料,并對其訪問進行限制.比如:
- 商品ID為引數,統計一段時間內最常購買的商品ID,并進行限制
- 用戶ID為引數,針對一段時間內頻繁訪問的用戶ID進行限制
熱點引數限流會統計傳入引數中的熱點引數,并根據配置的限流閾值與模式,對包含熱點引數的資源呼叫進行限流.熱點引數限流可以看做是一種特殊的流量控制,僅對包含熱點引數的資源呼叫生效.
3.4.2 回傳的值(系統默認和自定義)
- 兜底方法分為
系統默認和用戶自定義兩種 - 之前的case,限流出問題之后,都是用sentinel系統默認的提示: Blocked by Sentinel (flow limiting)
- 現在我們可以自定義一個 從@HystrixCommand到@SentinelResource
3.4.3 代碼配置
@GetMapping("/getHotKey")
@SentinelResource(value = "getHotKey", blockHandler = "deal_getHotKey")
public String getHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
log.info("getHotKey 測驗熱點key限流");
return "------getHotKey";
}
public String deal_getHotKey(String p1, String p2, BlockException exception){
log.info("deal_getHotKey aaaaaa");
return "------deal_getHotKey";
}

-
第一種
- @SentinelResource(value = “getHotKey”, blockHandler = “deal_getHotKey”)
- 方法getHotKey里面第一個引數只要超過每秒1次,馬上降級處理
- 用了我們自定義的方法.結果:
------deal_getHotKey
-
第二種
-
@SentinelResource(value = “getHotKey”)
-
例外打到了前臺界面,對用戶不友好,結果:

-
-
所以,如果我們用
熱點key限流一定要寫兜底的方法
3.4.4 引數例外項
上述案例演示了第一個引數p1,當QPS超過1秒1次點擊后馬上被限流
-
特例情況
- 普通 超過1秒后,達到閾值1后馬上被限流
- 我們期望P1引數當他是某個特殊值時,它的限流值和平時不一樣
- 特例 例如當p1的值等于5時,它的閾值可以達到200
-
配置
![images\image-.png)]](https://img.uj5u.com/2021/01/01/2110980113330119.png)
-
結論: http://localhost:8401/sentinel/getHotKey?p1=5 閾值在每秒200下,怎么點都可以
http://localhost:8401/sentinel/getHotKey?p1=1 閾值還是每秒1下,報錯
@SentinelResource
處理的是Sentinel控制臺配置的違規情況,有blockHandler方法配置的兜底處理;
RuntimeException
int age = 10/0,這個是java運行時爆出的運行時例外RuntimeException,@SentinelResource不管
總結
@SentinelResource主管配置出錯,運行出錯該走例外走例外
3.5 Sentinel系統規則
3.5.1 是什么
系統保護規則是從應用級別的入口流量進行控制,從單臺機器的
load,cpu使用率,平均RT,入口QPS和并發執行緒數等幾個緯度監控應用指標,讓系統盡可能泡在最大吞吐量的同時保證系統整體的穩定性.系統保護規則是應用整體維度的,而不是資源維度的,并且僅對入口流量生效.入口流量指的是進入應用的流量(
EntryType.IN),比如Web服務或Dubbo服務端接收的請求,都隸屬于入口流量.
3.5.2 引數配置
- 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達到閾值即觸發系統保護.
3.6 @SentinelResource
3.6.1 按資源名稱限流+后續處理
- 按照@SentinelResource資源名稱配置流控規則
- 因為我們寫了兜底的方法,所以按照我們的方法執行
- 產生的問題:關閉8401服務器,系統流控規則消失
3.6.2 按URL地址限流+后續處理
- 按照URL地址進行配置流控規則
- 我們沒有寫兜底的方法,所以就用系統默認自帶的
- 產生的問題:關閉8401服務器,系統流控規則消失
3.6.3 上面兜底方案面臨的問題
- 系統默認的,沒有體現我們自己的業務要求.
- 依照現有調價,我們自定義處理方法和業務代碼耦合在一起,不直觀.
- 每個業務方法都添加一個兜底的方法,代碼膨脹加劇.
- 全域統一的處理方法沒有體現.
3.6.4 客戶自定義限流處理邏輯
-
創建
CustomerBlockHandler類用于自定義限流處理邏輯 -
自定義限流處理類
CustomerBlockHandlerpackage com.atguigu.springcloud.myhandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.atguigu.springcloud.utils.CommonResult; public class CustomerBlockHandler { public static CommonResult handlerException(BlockException exception){ return new CommonResult(444,"用戶自定義--------1"); } public static CommonResult handlerException2(BlockException exception){ return new CommonResult(444,"用戶自定義--------2"); } } -
RateLimitController
package com.atguigu.springcloud.controller; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.atguigu.springcloud.myhandler.CustomerBlockHandler; import com.atguigu.springcloud.utils.CommonResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rate") public class RateLimitController { @GetMapping("/consumerBlockHandler") @SentinelResource(value = "consumerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2") public CommonResult consumerBlockHandler(){ return new CommonResult(200,"用戶自定義----OK","我成功了"); } } -
啟動微服務后先呼叫一次
-
Sentinel控制臺配置

- 測驗后我們自定義的出來了
3.7 服務熔斷功能
Sentinel整合ribbon+OpenFeign+fallback
3.7.1 服務熔斷框架比較
| Sentinel | Hystrix | resilience4j | |
|---|---|---|---|
| 隔離策略 | 信號量隔離(并發執行緒數限流) | 執行緒池隔離/信號量隔離 | 信號量隔離 |
| 熔斷降級策略 | 基于回應時間、一例外比率、例外數 | 基于例外比率 | 基于例外比率、回應時間 |
| 實時統計實作 | 滑動視窗(LeapArray) | 滑動視窗(基于RxJava) | Ring Bit Buffer |
| 動態規則配置 | 支持多種資料源 | 支持多種資料源 | 有限支持 |
| 擴展性 | 多個擴展點 | 插件的形式 | 介面的形式 |
| 擴展性 | 多個擴展點 | 插件的形式 | 介面的形式 |
| 基于注解的支持 | 支持 | 支持 | 支持 |
| 限流 | 基于QPS,支持基于呼叫關系的限流 | 有限的支持 | Rate Limiter |
| 流量整形 | 支持預熱模式、勻速器模式、預熱排隊模式 | 不支持 | 簡單的Rate Limiter模式 |
| 系統自適應保護 | 支持 | 不支持 | 不支持 |
| 控制臺 | 提供開箱即用的控制臺,可配置規則、查看秒級監控、機器發現等 | 簡單的監控查看 | 不提供控制臺,可對接其他監控系統 |
3.8 規則持久化
3.8.1 是什么
一旦我們重啟應用,sentinel規則將消失,生產環境需要將配置規則進行持久化
3.8.2 怎么用
將限流配置規則持久化進Nacos保存,只要重繪8401某個rest地址,sentinel控制臺的流控規則就能看到,只要Nacos里面的配置不洗掉,針對8401上sentinel上的流控規則持續有效
3.8.3 步驟
-
pom檔案
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> -
yml檔案
spring: application: name: nacos-sentinel-service cloud: nacos: discovery: # nacos 服務注冊中心地址 server-addr: 127.0.0.1:8848 sentinel: transport: # 配置中心 sentinel dashboard地址 dashboard: 127.0.0.1:8080 # 默認8719埠,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的埠 port: 8719 datasource: ds1: nacos: server-addr: localhost:8848 dataId: nacos-sentinel-service groupId: DEFAULT_GROUP data-type: json rule-type: flow -
nacos新建配置
[ { "resource":"/rate/byResource", "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: 是否集群
4. Spring Cloud Alibaba Seata處理分布式事務
4.1 分布式事務問題
-
分布式前: 單機單庫沒有任何問題 從1:1 --> 1:N --> N:N
-
分布式之后:
單體應用被拆分成微服務應用,原來的三個模塊被拆分成三個獨立的應用,分別使用三個獨立的資料源,
業務操作需要三個微服務來完成.此時每個服務內部的資料一致性由本地事務來保證,但是全域的資料一致性問題沒有辦法保證
![images\image-.png)]](https://img.uj5u.com/2021/01/01/2110980113330121.png)
![images\image-.png)]](https://img.uj5u.com/2021/01/01/2110980113330122.png)
-
一次業務操作需要跨多個資料源或需要跨多個系統進行遠程呼叫,就會產生分布式事務問題
4.2 Seata簡介
4.2.1 是什么
Seata是一款開源的分布式事務解決方案,致力于在微服務架構下提供高性能和簡單應用的分布式事務服務
http://seata.io/zh-cn
4.2.2 能干嘛
-
一個典型的分布式事務程序
-
分布式事務處理程序的一ID+三組件模型
-
Transaction ID XID 全域唯一的事務ID
-
3組件概念
-
Transaction Coordinator(TC) - 事務協調者
事務協調器,維護全域事務的運行狀態,負責協調并驅動全域事務的提交或回滾;
-
Transaction Manager? - 事務管理器
控制全域事務的邊界,負責開啟一個全域事務,并最終發起全域提交或全域回滾的協議;
-
Resource Manager(RM) - 資源管理器
控制分支事務,負責分支注冊、狀態匯報,并接收事務協調器的指令,驅動分支(本地)事務的提交和回滾.
-
-
-
處理程序
-
TM向TC申請開啟一個全域事務,全域事務創建成功并且生成一個全域唯一的XID;
-
XID在微服務呼叫鏈路的背景關系中傳播;
-
RM向TC注冊分支事務,將其納入XID對應全域事務的管轄;
-
TM向TC發起針對XID的全域提交或回滾決議;
-
TC調度XID下管轄的全部分支事務完成提交或回滾請求.
![images\image-20201228142009465.png)]](https://img.uj5u.com/2021/01/01/2110980113330123.png)
-
-
4.2.3 怎么用
- 本地 @Transactional
- 全域 @GlobalTransactional
4.3 Seata-Server安裝
-
下載 http://seata.io/zh-cn
-
解壓縮
-
修改conf目錄下的file.conf檔案
service { #vgroup->rgroup # vgroup_mapping.my_test_tx_group 修改為自己的名字,默認為default vgroup_mapping.my_test_tx_group = "fsp_tx_group" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" } store { ## store mode: file、db、redis mode = "db" # file修改為 db # 下面的資料庫配置修改為自己的 db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata" user = "root" password = "123456" minConn = 5 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } } -
創建seata資料庫,將conf/db_store.sql粘腳本建表
-
修改conf/registry.conf檔案
registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa # type從file修改為nacos type = "nacos" nacos { #nacos地址修改為自己的地址 serverAddr = "localhost:8848" namespace = "" cluster = "default" }
4.4 訂單/庫存/賬戶業務資料庫準備
4.4.1 業務說明
這里我們會創建三個服務,一個訂單服務,一個庫存服務,一個賬戶服務.
當用戶下單時,會在訂單服務中創建一個訂單,然后通過遠程呼叫庫存服務來扣減下單商品的庫存,
再通過遠程呼叫賬戶服務來扣減用戶賬戶里面的余額,
最后在訂單服務中修改訂單狀態位已完成.
該操作跨越三個資料庫,有兩次遠程呼叫,很明顯會有分布式事務問題.
4.4.2 創建業務資料庫
-
seata_order: 存盤訂單的資料庫;
-- 建表 t_order /* Navicat Premium Data Transfer Source Server : 本地 Source Server Type : MySQL Source Server Version : 50706 Source Host : localhost:3306 Source Schema : seata_order Target Server Type : MySQL Target Server Version : 50706 File Encoding : 65001 Date: 28/12/2020 16:34:14 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_order -- ---------------------------- DROP TABLE IF EXISTS `t_order`; CREATE TABLE `t_order` ( `id` bigint(11) NOT NULL, `user_id` bigint(11) NULL DEFAULT NULL COMMENT '用戶ID', `product_id` bigint(11) NULL DEFAULT NULL COMMENT '產品ID', `count` int(11) NULL DEFAULT NULL COMMENT '數量', `money` decimal(11, 0) NULL DEFAULT NULL COMMENT '金額', `status` int(1) UNSIGNED NULL DEFAULT NULL COMMENT '訂單狀態: 0創建中,1已完結', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '訂單表' ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_order -- ---------------------------- INSERT INTO `t_order` VALUES (51, 1, 1, 10, 100, 1); INSERT INTO `t_order` VALUES (52, 1, 1, 10, 100, 1); INSERT INTO `t_order` VALUES (53, 1, 1, 10, 100, 1); INSERT INTO `t_order` VALUES (54, 1, 1, 10, 100, 0); SET FOREIGN_KEY_CHECKS = 1; -- 建回滾日志表conf/db_undo_log.sql -
seata_storage: 存盤庫存的資料庫;
--建表 t_storage /* Navicat Premium Data Transfer Source Server : 本地 Source Server Type : MySQL Source Server Version : 50706 Source Host : localhost:3306 Source Schema : seata_storage Target Server Type : MySQL Target Server Version : 50706 File Encoding : 65001 Date: 28/12/2020 16:34:34 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_storage -- ---------------------------- DROP TABLE IF EXISTS `t_storage`; CREATE TABLE `t_storage` ( `id` bigint(11) NOT NULL, `product_id` bigint(11) NULL DEFAULT NULL COMMENT '產品ID', `total` int(11) NULL DEFAULT NULL COMMENT '總庫存', `used` int(11) NULL DEFAULT NULL COMMENT '已用庫存', `residue` int(11) NULL DEFAULT NULL COMMENT '剩余庫存', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '庫存' ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_storage -- ---------------------------- INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100); SET FOREIGN_KEY_CHECKS = 1; -- 建回滾日志表conf/db_undo_log.sql -
seata_account: 存盤賬戶資訊的資料庫.
--建表 t_account /* Navicat Premium Data Transfer Source Server : 本地 Source Server Type : MySQL Source Server Version : 50706 Source Host : localhost:3306 Source Schema : seata_account Target Server Type : MySQL Target Server Version : 50706 File Encoding : 65001 Date: 28/12/2020 16:34:27 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_account -- ---------------------------- DROP TABLE IF EXISTS `t_account`; CREATE TABLE `t_account` ( `id` bigint(11) NOT NULL, `user_id` bigint(11) NULL DEFAULT NULL COMMENT '用戶ID', `total` decimal(10, 0) NULL DEFAULT NULL COMMENT '總額度', `used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用額度', `residue` decimal(10, 0) NULL DEFAULT NULL COMMENT '剩余可用額度', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '賬戶資訊' ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_account -- ---------------------------- INSERT INTO `t_account` VALUES (1, 1, 1000, 0, 1000); SET FOREIGN_KEY_CHECKS = 1; -- 建回滾日志表conf/db_undo_log.sql
4.5 訂單/庫存/賬戶業務微服務準備
4.5.1 業務需求
下訂單->減庫存->扣余額->改(訂單)狀態
4.5.2 訂單微服務準備
-
pom檔案
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atguigu.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-order-service2001</artifactId> <dependencies> <!--nacos-discovery--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--alibaba sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--spring cloud alibaba sentinel-datasource-nacos 后續持久化會用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!--alibaba seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <!--版本需匹配一致--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>0.9.0</version> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--Mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <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.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> -
yam檔案
server: port: 2001 spring: application: name: seata-order-service cloud: alibaba: seata: tx-service-group: my_test_tx_group nacos: discovery: server-addr: localhost:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&rewriteBatchedStatements=true&useSSL=true username: root password: 123456 feign: hystrix: enabled: true logging: level: io: seata: info # mybatis配置 mybatis: mapperLocations: classpath:mapper/*.xml type-aliases-package: com.atgui.springcloud.domain #物體類包 -
DataSourceProxyConfig配置類
package com.atgui.springcloud.config; import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; /** * 使用seata對資料源進行代理 * */ @Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource){ return new DataSourceProxy(dataSource); } @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception{ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); } } -
MybatisConfig配置類
package com.atgui.springcloud.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan({"com.atgui.springcloud.dao"}) public class MyBatisConfig { } -
service實作類
package com.atgui.springcloud.service.impl; import com.atgui.springcloud.dao.OrderDao; import com.atgui.springcloud.domain.Order; import com.atgui.springcloud.service.AccountService; import com.atgui.springcloud.service.OrderService; import com.atgui.springcloud.service.StorageService; import io.seata.spring.annotation.GlobalTransactional; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service @Slf4j public class OrderServiceImpl implements OrderService { @Resource private OrderDao dao; @Resource private StorageService storageService; @Resource private AccountService accountService; @Override @GlobalTransactional(name = "order-create",rollbackFor = Exception.class) public void create(Order order) { //1.創建訂單 log.info("--------開始創建訂單---------"); dao.create(order); //2.呼叫庫存服務,扣減數量 log.info("--------訂單微服務呼叫庫存微服務,扣減數量---------"); storageService.decrease(order.getProductId(),order.getCount()); //3.呼叫賬戶服務,扣減money log.info("--------訂單微服務呼叫賬戶微服務,扣減錢---------"); accountService.decrease(order.getUserId(),order.getMoney()); //4.修改訂單狀態 log.info("--------修改訂單狀態開始---------"); dao.update(order.getUserId(),0); log.info("--------修改訂單狀態結束---------"); log.info("--------下訂單結束---------"); } }
4.7 Seata補充
4.7.1 執行流程
- TM開啟分布式事務(TM向TC注冊全域事務記錄);
- 按業務場景,編排資料庫、服務等事務內資源(RM向TC會報資源準備狀態);
- TM結束分布式事務,事務一階段結束(TM通知TC提交/回滾分布式事務);
- TC匯總事務資訊,決定分布式事務是提交還是回滾;
- TC通知所有RM提交/回滾資源,事務二階段結束.
4.7.2 AT模式如何做到對業務的無侵入
Seata是一款開源的分布式事務解決方案,致力于提供高性能和簡易使用的分布式事務服務.Seata將為用戶提供了AT、TCC、SAGA和XA事務模式,為用戶打造一站式的分布式解決方案.
-
AT模式
- 前提
- 基于本地ACID事務的關系型資料庫
- Java應用,通過JDBC訪問資料庫
- 整體機制(兩階段提交協議的演變)
- 一階段: 業務資料和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源.
- 二階段:
- 提交異步化,非常快速地完成.
- 回滾通過一階段的回滾日志進行反向補償.
- 前提
-
一階段加載
在一階段,Seata會攔截"業務SQL",
1 . 決議SQL語意,找到"業務SQL"要更新的業務資料,在業務資料被更新前,將其保存成"before image",
2 . 執行"業務SQL"更新業務資料,在業務資料更新之后,
3 . 將其保存成"after image",最后生成行鎖.
以上操作全部在一個資料庫事務內完成,這樣保證了一階段操作的原子性.
![images\image-20201230153317262.png)]](https://img.uj5u.com/2021/01/01/2110980113330124.png)
-
二階段提交
二階段如果順利提交的話,
因為"業務SQL"在一階段已經提交至資料庫,所以Seata只需將一階段保存的快照資料和行鎖刪掉,完成資料清理即可.
![images\image-20201230153605200.png)]](https://img.uj5u.com/2021/01/01/2110980113330125.png)
-
二階段回滾
二階段如果是回滾的話,Seata就需要回滾一階段已經執行的"業務SQL",還原業務資料.
回滾方式便是用"before image" 還原業務資料;但在還原前要首先要校驗臟寫,對比"資料庫當前業務資料"和"after image",
如果兩份資料完全一致就說明沒有臟寫,可以還原業務資料,如果不一致就說明有臟寫,出現臟寫就需要轉人工處理.
![images\image-20201230154227673.png)]](https://img.uj5u.com/2021/01/01/2110980113330126.png)
-
補充
![mages\image-20201230160139619.png)]](https://img.uj5u.com/2021/01/01/2110980113330127.png)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/243297.html
標籤:其他
上一篇:總結2020:5個月出版兩本書,日更公眾號是一種怎樣的體驗?
下一篇:不容錯過的Java高級面試題
