該筆記整理至尚矽谷周陽老師的SpringCloud課程SpringCloud Alibaba篇 SpringCloud Alibaba入門簡介 Spring Cloud Netflix 項目進入維護模式,Spring Cloud Netflix 將不再開發新的組件。Spring Cloud 版本迭代 ...
該筆記整理至尚矽谷周陽老師的SpringCloud課程SpringCloud Alibaba篇
SpringCloud Alibaba入門簡介
Spring Cloud Netflix 項目進入維護模式,Spring Cloud Netflix 將不再開發新的組件。Spring Cloud 版本迭代算是比較快的,因而出現了很多重大 ISSUE 都還來不及 Fix 就又推另一個 Release 了。進入維護模式意思就是目前一直以後一段時間Spring Cloud Netflix提供的服務和功能就這麼多了,不在開發新的組件和功能了。以後將以維護和Merge分支Full Request為主,新組件功能將以其他替代平代替的方式實現。基於該背景下,誕生了 SpringCloud Alibaba.
官網:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
SpringCloud Alibaba 特性
- 服務限流降級:預設支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降級功能的接入,可以在運行時通過控制台實時修改限流降級規則,還支持查看限流降級 Metrics 監控。
- 服務註冊與發現:適配 Spring Cloud 服務註冊與發現標準,預設集成了 Ribbon 的支持。
- 分散式配置管理:支持分散式系統中的外部化配置,配置更改時自動刷新。
- 消息驅動能力:基於 Spring Cloud Stream 為微服務應用構建消息驅動能力。
- 阿裡雲對象存儲:阿裡雲提供的海量、安全、低成本、高可靠的雲存儲服務。支持在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。
- 分散式任務調度:提供秒級、精準、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務。同時提供分散式的任務執行模型,如網格任務。網格任務支持海量子任務均勻分配到所有 Worker(schedulerx-client)上執行。
Spring Alibaba核心組件
官網:https://spring.io/projects/spring-cloud-alibaba#overview
英文文檔:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
中文文檔:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
Nacos服務註冊和配置中心
Nacos簡介
官網:https://nacos.io/zh-cn/index.html
GitHub:https://github.com/alibaba/Nacos
開發手冊:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
Nacos,全稱 Dynamic Naming and Configuration Service,Nacos = Eureka+Config +Bus,能夠替代 Eureka 做服務註冊中心和 Config 做服務配置中心。
各類註冊中心比較
服務註冊與發現框架 | CAP模型 | 控制台管理 | 社區活躍度 |
---|---|---|---|
Euraka | AP | 支持 | 低(2.x 閉源) |
Zookeeper | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP | 支持 | 高 |
Nacos安裝與運行
- 準備 Java8 + Maven 環境
- 下載 Nocas:https://github.com/alibaba/nacos/releases
- 解壓安裝包,直接運行 bin 目錄下的 startup.cmd
startup.cmd -m standalone
- 命令運行成功後直接訪問:http://localhost:8848/nacos
Nacos服務註冊中心
基於Nacos的服務提供者
-
新建 module:cloudalibaba-provider-payment9001
-
改 POM
- 修改父 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
<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>
-
寫 YML
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
- 主啟動
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class, args);
}
}
- 業務類
@RestController
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id)
{
return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
}
}
- 測試
基於Nacos的服務消費者
- 根據 cloudalibaba-provider-payment9001 新建 cloudalibaba-provider-payment9002 演示負載均衡。
- 新建 module:cloudalibaba-consumer-nacos-order83
- 改 POM
<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.xiaobai.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>
- 寫 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
@SpringBootApplication
public class OrderNacosMain83
{
public static void main(String[] args)
{
SpringApplication.run(OrderNacosMain83.class,args);
}
}
-
業務類
- config
@Configuration public class ApplicationContextBean { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
- OrderNacosController
@RestController public class OrderNacosController { @Resource private RestTemplate restTemplate; @Value("${service-url.nacos-user-service}") private String serverURL; @GetMapping("/consumer/payment/nacos/{id}") public String paymentInfo(@PathVariable("id") Long id) { return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class); } }
-
測試
- 啟動 Nocas 服務、服務提供者 9001、9002
- 訪問:http://localhost:83/consumer/payment/nacos/13,實現 83 訪問 9001/9002,輪詢負載OK
為什麼 Nocas 支持負載輪詢? Nocas 中內置了 Ribbon !
服務註冊中心對比
Nocas 全景圖
Nacos與其他註冊中心特性對比
Nacos | Euraka | Consul | CoreDNS | ZooKeeper | |
---|---|---|---|---|---|
一致性協議 | CP + AP | AP | CP | / | CP |
健康檢查 | TCP/HTTP/MySQL/Client Beat | Client Beat | TCP/HTTP/GRPC/Cmd | / | Client Beat |
負載均衡 | 權重/DSL/metadata/CMDB | Ribbon | Fabio | RR | / |
雪崩保護 | 支持 | 支持 | 不支持 | 不支持 | 不支持 |
自動註銷 | 支持 | 支持 | 不支持 | 不支持 | 不支持 |
訪問協議 | HTTP/DNS/UDP | HTTP | HTTP/DNS | DNS | TCP |
監聽支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
多數據中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
跨註冊中心 | 支持 | 不支持 | 支持 | 不支持 | 不支持 |
SpringCloud集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 |
Dubbon集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 |
K8s集成 | 支持 | 不支持 | 支持 | 支持 | 不支持 |
Nacos 服務發現實例模型
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'
Nacos服務配置中心
基礎配置
- 新建 module:cloudalibaba-config-nacos-client3377
- 改 POM
<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>
-
寫 YML
- 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格式的配置 # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
- application
spring: profiles: active: dev # 表示開發環境
-
主啟動
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class, args);
}
}
-
業務類
- controller
@RestController //通過Spring Cloud原生註解@RefreshScope實現配置自動更新 @RefreshScope //在控制器類加入@RefreshScope註解使當前類下的配置支持Nacos的動態刷新功能。 public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/config/info") public String getConfigInfo() { return configInfo; } }
-
在 Nacos 中添加配置信息
Nacos中的匹配規則:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
設置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天,此外還有一鍵回滾功能,回滾操作將會觸發配置更新。
- 測試
- 啟動前需要在nacos客戶端-配置管理-配置管理欄目下有對應的yaml配置文件;
- 運行cloud-config-nacos-client3377的主啟動類;
- 調用介面查看配置信息:http://localhost:3377/config/info
- 修改下 Nacos 中的yaml配置文件,再次調用查看配置的介面,就會發現配置已經刷新
分類配置
多環境多項目管理中面臨的問題:
- 實際開發中,通常一個系統會準備 dev開發環境、test測試環境、prod生產環境。如何保證指定環境啟動時服務能正確讀取到Nacos上相應環境的配置文件呢?
- 一個大型分散式微服務系統會有很多微服務子項目,每個微服務項目又都會有相應的開發環境、測試環境、預發環境、正式環境......
那怎麼對這些微服務配置進行管理呢?
Nacos 的圖形化管理界面
Namespace+Group+Data ID三者關係
預設情況: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,就是微服務的實例。
三種方案載入配置
-
DataID方案:指定spring.profile.active和配置文件的DataID來使不同環境下讀取不同的配置。
- 新建 dev 配置 DataID
- 同上,新建 test 配置 DataID
- 通過spring.profile.active屬性就能進行多環境下配置文件的讀取
-
Group方案:通過Group實現環境區分
- 新建 Group,在 nacos 圖形界面控制臺上面新建配置文件DataID
- 在config下增加一條group的配置即可。可配置為 DEV_GROUP 或 TEST_GROUP
-
Namespace方案
- 新建dev/test的Namespace
- 回到服務管理-服務列表查看
- 按照功能變數名稱配置填寫
-
修改 YML
- bootstrap:config 添加 namespace 配置
config: server-addr: localhost:8848 #Nacos作為配置中心地址 file-extension: yaml #這裡我們獲取的yaml格式的配置 namespace: 5da1dccc-ee26-49e0-b8e5-7d9559b95ab0 #group: DEV_GROUP group: TEST_GROUP
- application
# Nacos註冊配置,application.yml spring: profiles: #active: test active: dev #active: info
Nacos集群和持久化配置
官網說明
官方文檔:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
預設Nacos使用嵌入式資料庫實現數據的存儲。所以,如果啟動多個預設配置下的Nacos節點,數據存儲是存在一致性問題的。
為瞭解決這個問題,Nacos採用了集中式存儲的方式來支持集群化部署,目前只支持MySQL的存儲。
Nacos持久化配置
Nacos預設自帶的是嵌入式資料庫derby,說明:https://github.com/alibaba/nacos/blob/develop/config/pom.xml
切換配置 MySQL 步驟
- 新建 nacos 資料庫,在 nacos-server-1.1.4\nacos\conf 目錄下找到 sql 腳本,執行:
create database nacos;
use nacos;
source D:\Devware\nacos-server-2.1.0-BETA\conf\nacos-mysql.sql
- nacos-server-1.1.4\nacos\conf 目錄下找到 application.properties,修改資料庫配置
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
- 啟動Nacos,可以看到是個全新的空記錄界面,以前是記錄進derby
Linux版Nacos+MySQL、生產環境配置
-
環境準備
- 下載 Linux 版 Nacos: https://github.com/alibaba/nacos/releases/tag/1.1.4,解壓到 opt 目錄下
tar -xzvf /opt/ nacos-server-1.1.4.tar.gz
-
集群配置
- 新建 nacos 資料庫,將 nacos 安裝目錄里的 nacos-mysql.sql 導入到 mysql 資料庫中
create database nacos; use nacos; source /opt/nacos/confnacos-mysql.sql
- 修改 application.properties 配置
cp application.properties.example application.properties vim application.properties
- Linux伺服器上nacos的集群配置 cluster.conf
cp cluster.conf.example cluster.conf vim cluster.conf
- 編輯Nacos的啟動腳本 startup.sh,使它能夠接受不同的啟動埠
cd /opt/nacos/bin vim startup.sh
#執行方式 ./startup.sh -p 3333 ./startup.sh -p 4444
- Nginx的配置,由它作為負載均衡器
vim /usr/local/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; } ......
啟動 nginx,執行: ./nginx -C /usr/local/nginx/conf/nginx.conf
-
截止到此處,1個Nginx+3個nacos註冊中心+1個mysql
測試通過nginx訪問nacos :http://192.168.4.15:1111/nacos/#/login
新建一個測試配置
伺服器插入一條數據
-
測試
- 修改 cloudablibaba-provider-payment9002 yml
server-addr: 192.168.111.144:1111
- 微服務 cloudalibaba-provider-payment9002 啟動,註冊進 nacos 集群
Sentinel實現熔斷與限流
Sentinel簡介
GitHub:https://github.com/alibaba/Sentinel
Sentinel 是一款功能強大的流量控制組件,以 flow 為突破點,覆蓋流量控制、併發限制、斷路、自適應系統保護等多個領域,保障微服務的可靠性。
Sentinel安裝
Sentinel 分為兩個部分:
- 核心庫(Uava客戶端):不依賴任何框架/庫,能夠運行於所有ava運行時環境,同時對 Dubbo/Spring Cloud 等框架也有較好的支特。
- 控制台(Dashboard):基於Spring Boot開發,打包後可以直接運行,不需要額外的 Tomcat 等應用容器。
- 下載:https://github.com/alibaba/Sentinel/releases
- 保證 Java8 環境且 8080 埠不被占用,運行
java -jar sentinel-dashboard-1.7.0.jar
- 訪問sentinel管理界面:http://localhost:8080 ,登錄賬號密碼均為 sentinel
初始化工程
- 新建 module,cloudalibaba-sentinel-service8401
- 改 POM
<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>
- 寫 YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
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: '*'
- 主啟動
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
- controller
@RestController
@Log4j2
public class FlowLimitController
{
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
return "------testB";
}
}
-
測試
- 啟動 nacos 執行
startup.cmd -m standalone
,訪問:http://localhost:8848/nacos/#/login - 啟動 sentinel,執行
java -jar sentinel-dashboard-1.7.0.jar
,訪問:http://localhost:8080 - 啟動8401微服務,查看 sentienl 控制台,分別訪問:http://localhost:8401/testA、http://localhost:8401/testB
- 啟動 nacos 執行
Sentinel 採用的懶載入方式,只有在微服務被訪問之後 Sentienl 才進行監測。
流控規則
- 資源名:唯一名稱,預設請求路徑
- 針對來源:Sentinel可以針對調用者進行限流,填寫微服務名,預設default(不區分來源)
- 調值類型單機闊值:
- QPS(每秒鐘的請求數量):當調用該 API 的 QPS 達到閾值的時候,進行限流
- 線程數:當調用該p的線程數達到闊值的時候,進行限流
- 是否集群:不需要集群
- 流控模式:
- 直接:API 達到限流條件時,直接限流
- 關聯:當關聯的資源達到閾值時,就限流自己
- 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到城值,就進行限流)【 API 級別的針對來源】
- 流控效果:
- 快速失敗:直接失敗,拋異常
- warm Up:根據 codeFactor (冷載入因數,預設3) 的值,從闊值/codeFactor,經過預熱時長,才達到設置的QPS闊值
- 排隊等待:勻速排隊,讓請求以勻速的速度通過,閾值類型必須設置為QPS,否則無效
流量模式
- 直接模式(預設):直接->快速失敗
表示1秒鐘內查詢1次就是OK,若超過次數1,就直接-快速失敗,報預設錯誤。
快速點擊訪問:http://localhost:8401/testA,結果 Blocked by Sentinel (flow limiting)
- 關聯模式:當關聯的資源達到閾值時,就限流自己
postman 模擬併發密集訪問 testB,大批量線程高併發訪問B,導致A失效了
- 鏈路模式:多個請求調用了同一個微服務
流控效果
-
預設的流控處理:直接->快速失敗
源碼:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
-
預熱:閾值除以coldFactor(預設值為3),經過預熱時長後才會達到閾值
文檔:https://github.com/alibaba/Sentinel/wiki/流量控制
源碼:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
例如:系統初始化的閥值為10 / 3 約等於3,即閥值剛開始為3;然後過了5秒後閥值才慢慢升高恢復到10
測試:多次點擊:http://localhost:8401/testB,剛開始不行,後續慢慢OK
運用場景如:秒殺系統在開啟的瞬間,會有很多流量上來,很有可能把系統打死,預熱方式就是把為了保護系統,可慢慢的把流量放進來,慢慢的把閥值增長到設置的閥值。
- 等待排隊:勻速排隊,讓請求以均勻的速度通過,閥值類型必須設成QPS,否則無效。
官網:https://github.com/alibaba/Sentinel/wiki/流量控制
源碼:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
測試
降級規則
官網:https://github.com/alibaba/Sentinel/wiki/熔斷降級
Sentinel 熔斷降級會在調用鏈路中某個資源出現不穩定狀態時(例如調用超時或異常比例升高),對這個資源的調用進行限制,
讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤。
當資源被降級後,在接下來的降級時間視窗之內,對該資源的調用都自動熔斷(預設行為是拋出 DegradeException)。
註意:Sentinel的斷路器是沒有半開狀態的
降級策略實戰
-
RT(平均響應時間,秒級):平均響應時間 超出閾值 且 在時間視窗內通過的請求>=5,兩個條件同時滿足後觸發降級。視窗期過後關閉斷路器,RT最大4900(更大的需要通過-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)。
- 代碼
@GetMapping("/testD") public String testD() { //暫停幾秒鐘線程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.info("testD 測試RT"); return "------testD"; }
- 配置
- jmeter壓測
-
結論
永遠一秒鐘打進來10個線程(大於5個了)調用testD,我們希望200毫秒處理完本次任務,如果超過200毫秒還沒處理完,在未來1秒鐘的時間視窗內,斷路器打開(保險絲跳閘)微服務不可用,保險絲跳閘斷電了。後續我停止jmeter,沒有這麼大的訪問量了,斷路器關閉(保險絲恢復),微服務恢復OK。
-
異常比列(秒級):QPS >= 5 且異常比例(秒級統計)超過閾值時,觸發降級;時間視窗結束後,關閉降級。
- 代碼
@GetMapping("/testD") public String testD() { log.info("testD 測試RT"); int age = 10/0; return "------testD"; }
- 配置
- jmeter壓測
-
結論
開啟jmeter後,直接高併發發送請求,多次調用達到我們的配置條件了。斷路器開啟(保險絲跳閘),微服務不可用了,不再報錯error而是服務降級了。
-
異常數(分鐘級):異常數(分鐘統計)超過閾值時,觸發降級;時間視窗結束後,關閉降級。
- 代碼
@GetMapping("/testE") public String testE() { log.info("testE 測試異常比例"); int age = 10/0; return "------testE 測試異常比例"; }
- 配置
http://localhost:8401/testE,第一次訪問絕對報錯,因為除數不能為零,我們看到error視窗,但是達到5次報錯後,進入熔斷後降級。
- jmeter壓測
熱點key限流
官網:https://github.com/alibaba/Sentinel/wiki/熱點參數限流
熱點即經常訪問的數據,很多時候我們希望統計或者限制某個熱點數據中訪問頻次最高的TopN數據,並對其訪問進行限流或者其它操作。
@SentinelResource
@GetMapping("/testHotKey")
@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";
}
限流模式只支持QPS模式,固定寫死了(這才叫熱點)。@SentinelResource註解的方法參數索引,0代表第一個參數,1代表第二個參數,以此類推單機閥值以及統計視窗時長表示在此視窗時間超過閥值就限流。上面的抓圖就是第一個參數有值的話,1秒的QPS為1,超過就限流,限流後調用dealHandler_testHotKey支持方法。
測試:
- http://localhost:8401/testHotKey?p1=abc,快速訪問返回異常
- http://localhost:8401/testHotKey?p1=abc&p2=33,快速訪問返回異常
- http://localhost:8401/testHotKey?p2=abc,快速訪問成功返回
參數例外項
我們期望p1參數當它是某個特殊值時,它的限流值和平時不一樣。假如當p1的值等於5時,它的閾值可以達到200
註意:熱點參數的註意點,參數必須是基本類型或者String
系統規則
官網:https://github.com/alibaba/Sentinel/wiki/系統自適應限流
系統保護規則是從應用級別的入口流量進行控制,從單台機器的Iod、CPU使用率、平均RT、入口QPS和併發線程數等幾個維度監控應用指標,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IW),比如Web服務或Dubbo服務端接收的請求,都屬於入口流量。
系統規則支持以下的模式:
- Load自適應(僅對Linux/Unix-ike機器生效):系統的load1作為啟髮指標,進行自適應系統保護。當系統Ioād1超過設定的啟發值,且系統當前的併發線程數超過估算的系統容量時才會觸發系統保護(BBR階段)。系統容量由系統的maxQps*mit估算得出。設定參考值一般是CPU cores 2.5。
- CPU usage(1.5.0+版本):當系統CPU使用率超過閾值即觸發系統保護(取值範圍0.0-1.0),比較靈敏。
- 平均T:當單台機器上所有入口流量的平均T達到闊值即觸發系統保護,單位是毫秒。
- 併發線程數:當單台機器上所有入口流量的併發線程數達到闊值即觸發系統保護。
- 入口QPS:當單台機器上所有入口流量的QPS達到閾值即觸發系統保護。
@SentinelResource
按資源名限流+後續處理
啟動 Nacos,執行:startup.cmd -m standalone
,訪問:http://localhost:8848/nacos/#/login 測試
啟動 Sentinel,執行:java -jar sentinel-dashboard-1.7.0.jar
修改 cloudablibaba-sentinel-service8401
- 改 POM,添加以依賴
<dependencies>
<dependency><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
<groupId>com.xiaobai.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
- controller,新建 RateLimitController
@RestController
public class RateLimitController
{
@GetMapping("/byResource")
@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 服務不可用");
}
}
-
流控規則配置
- 配置步驟
- 圖形配置與代碼關係
-
測試,啟動 8401,訪問:http://localhost:8401/byResource,間隔時間大於等於 1s 訪問成功,間隔時間小於 1s 返回自定義的限流處理信息。
遺留問題: 當 8401 服務關閉,Sentinel 流量控制規則消失。
按Url地址限流+後續處理
- 修改 controller,添加如下方法
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流測試OK",new Payment(2020L,"serial002"));
}
- 初步測試,訪問:http://localhost:8401/rateLimit/byUrl,訪問成功
- 配置流控規則
- 二次測試,連續訪問:http://localhost:8401/rateLimit/byUrl,返回 Sentinel 自帶的限流處理結果
上述兜底方案面臨的問題:
- 系統預設的,沒有體現我們自己的業務要求。
- 依照現有條件,我們自定義的處理方法又和業務代碼耦合在一塊,不直觀。
- 每個業務方法都添加一個兜底的,那代碼膨脹加劇。
- 全局統一的處理方法沒有體現。
客戶自定義限流處理邏輯
- 創建 CustomerBlockHandler 類用於自定義限流處理邏輯
public class CustomerBlockHandler
{
public static CommonResult handleException(BlockException exception){
return new CommonResult(2020,"自定義的限流處理信息......CustomerBlockHandler-----1");
}
public static CommonResult handleException2(BlockException exception){
return new CommonResult(2020,"自定義的限流處理信息......CustomerBlockHandler------2");
}
}
- RateLimitController 添加自定義限流處理邏輯
/**
* 自定義通用的限流處理邏輯,
* blockHandlerClass = CustomerBlockHandler.class
* blockHandler = handleException2
* 上述配置:找CustomerBlockHandler類里的handleException2方法進行兜底處理
*/
/**
* 自定義通用的限流處理邏輯
*/
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客戶自定義限流處理邏輯");
}
- 啟動微服務後調用:http://localhost:8401/rateLimit/customerBlockHandler
- Sentinel 控制台添加配置
服務熔斷
Ribbon系列
服務提供者9003/9004
- 新建cloudalibaba-provider-payment9003/9004
- 改 POM
<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.xiaobai.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>
- 寫 YML(記得該埠號)
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
- 主啟動
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
- 業務類
@RestController
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"));
}
@GetMapping(value = "/paymentSQL/{id}")
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;
}
}
服務消費者84
- 新建 cloudalibaba-consumer-nacos-order84
- 改 POM
<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.xiaobai.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>
- 寫 YML
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
- 主啟動
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
- 配置類
@Configuration
public class ApplicationContextConfig
{
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
- 業務類
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@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,非法