前言
人到洛陽花似錦,偏我來時不逢春
17.商城業務-秒殺服務
17.1後臺新增秒殺商品
主要步驟:
- 匯入每日秒殺前端介面
gulimall-gateway
閘道器服務配置gulimall-coupon
路由- 最佳化秒殺關聯商品介面
匯入每日秒殺前端介面
gulimall-gateway
閘道器服務配置gulimall-coupon
路由
最佳化秒殺關聯商品介面
介面
17.2定時任務&Cron表示式
建立gulimall-seckill
秒殺服務
建立gulimall-seckill
秒殺服務
匯入依賴,主要有rabbitmq
、redis
、springsession
配置application.yaml
gulimall-seckill
開啟SpringSession
、OpenFeign
、nacos
,禁用datasource
定時任務
語法:秒 分 時 日 月 周 年(Spring 不支援)
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html
cron 示例
17.3SpringBoot整合定時任務與非同步任務
開啟定時任務
@EnableScheduling
【spring 預設是使用自己的定時任務,如果想整合Quartz,參考官方】@Scheduled
- 定時任務配置類:
TaskSchedulingAutoConfiguration
@Slf4j
@Component
@EnableScheduling
public class HelloScheduled {
// 每s執行
@Scheduled(cron = "0/1 * * * * ? ")
public void hello() {
log.info("hello...");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
定時任務+非同步任務
- 方案1:業務方法自己作非同步編排【
CompletableFuture.runAsync
】 - 方案2:修改定時任務執行緒池的執行緒個數【
spring.task.scheduling.pool.size=5
】(不一定生效,有BUG) - 方案3:讓定時任務非同步執行
方案3:讓定時任務非同步執行
- 1.
@EnableAsync
- 2.
@Async
- 3.非同步配置類:
TaskExecutionAutoConfiguration
spring.task.execution.pool.core-size=5
:核心執行緒數spring.task.execution.pool.max-size=50
:最大執行緒數
@EnableAsync
和@Async
@Slf4j
@Component
@EnableAsync
@EnableScheduling
public class HelloScheduled {
// @Scheduled(cron = "*/5 * * ? * 4")
// 每s執行
@Async
@Scheduled(cron = "0/1 * * * * ? ")
public void hello() {
log.info("hello...");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
非同步配置類
spring:
task:
execution:
pool:
core-size: 5 # 核心執行緒數
max-size: 50 # 最大執行緒數
17.4時間日期處理
主要步驟:
gulimall-seckill
秒殺服務遠端呼叫gulimall-coupon
優惠卷服務查詢最近三天的活動gulimall-coupon
優惠卷服務實現查詢最近三天的活動- 處理最近三天的日期
gulimall-seckill
秒殺服務遠端呼叫gulimall-coupon
優惠卷服務查詢最近三天的活動
gulimall-coupon
優惠卷服務實現查詢最近三天的活動
- 處理最近三天的日期
測試程式碼
@Test
public void testTime(){
LocalDate now = LocalDate.now();
LocalDate plus1 = now.plusDays(1);
LocalDate plus2 = now.plusDays(2);
System.out.println(now);
System.out.println(plus1);
System.out.println(plus2);
LocalTime min = LocalTime.MIN;
LocalTime max = LocalTime.MAX;
System.out.println(min);
System.out.println(max);
LocalDateTime start = LocalDateTime.of(now,min);
LocalDateTime end = LocalDateTime.of(plus2,max);
System.out.println(start);
System.out.println(end);
// 格式化最近三天的日期
String format1 = start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String format2 = end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("開始時間:"+format1);
System.out.println("結束時間:"+format2);
}
17.5秒殺商品上架-1
主要步驟:
- 遠端呼叫
gulimall-coupon
查詢最近三天的活動 - 在
redis
中快取活動資訊 - 在
redis
中快取活動關聯的商品資訊- 商品資訊,遠端呼叫
gulimall-product
,獲取商品sku
資訊
- 商品資訊,遠端呼叫
在redis
中快取活動資訊
在redis
中快取活動關聯的商品資訊
- 商品資訊
17.6秒殺商品上架-2
主要步驟:
- 遠端呼叫
gulimall-coupon
查詢最近三天的活動 - 在
redis
中快取活動資訊 - 在
redis
中快取活動關聯的商品資訊- 商品資訊,遠端呼叫
gulimall-product
,獲取商品sku
資訊 - 秒殺資訊
- 秒殺時間資訊
- 設定商品的隨機碼(防止惡意攻擊)
- 使用庫存作為分散式
Redisson
訊號量(限流)
- 商品資訊,遠端呼叫
17.7秒殺商品上架-3
主要步驟:
- 測試秒殺商品上架
啟動定時任務開啟秒殺商品上架,設定的cron
表示式每分鐘執行一次
@Slf4j
@Service
public class SeckillScheduled {
@Autowired
private SeckillService seckillService;
// TODO 保證冪等性問題
// @Scheduled(cron = "*/5 * * * * ?")
@Scheduled(cron = "0 * * * * ?")
public void uploadSeckillSkuLatest3Days() {
log.info("上架秒殺的商品...");
seckillService.uploadSeckillSkuLatest3Days();
}
}
快取活動資訊的key
:SESSION_CACHE_PREFIX + startTime + "_" + endTime
快取活動資訊的value
:活動場次Id + "_" + 商品skuId
快取秒殺商品的的key
:seckill:skus
快取秒殺商品的的value
key
:活動場次Id + "_" + 商品skuId
value
:商品sku
資訊、秒殺資訊、秒殺時間資訊、商品的隨機碼(防止惡意攻擊)、使用秒殺總量作為分散式Redisson
訊號量(限流)
使用秒殺總量作為分散式Redisson
訊號量(限流)
分散式Redisson
訊號量的key
:seckill:stock:
分散式Redisson
訊號量的value
:秒殺總量
17.8冪等性保證
主要步驟:
- 秒殺商品上架時新增分散式鎖
redis
儲存活動資訊,判斷seckill:sessions: + startTime + "_" + endTime
是否已經存在redis
儲存秒殺商品資訊時,判斷seckill:skus
中是否存在活動場次Id + "-" + 商品skuId
秒殺商品上架時新增分散式鎖
redis
儲存活動資訊,判斷seckill:sessions: + startTime + "_" + endTime
是否已經存在
redis
儲存秒殺商品資訊時,判斷seckill:skus
中是否存在活動場次Id + "-" + 商品skuId
17.9查詢秒殺商品
主要步驟:
-
管理後臺建立一個當前時間範圍內的秒殺活動,並關聯商品
-
實現獲取到當前可以參加秒殺商品的資訊
- 獲取當前時間,獲取活動場次的所有
key
,因為key
中包含活動場次開始時間和結束時間,查詢出活動場次中包含的所有秒殺商品的key
(場次Id + "-" + 商品skuId
) - 根據開始時間和結束時間獲取所有秒殺商品的
key
,再去seckill:skus
獲取所有的秒殺商品
- 獲取當前時間,獲取活動場次的所有
-
秒殺商品介面渲染
-
管理員執行
SwitchHost
,配置seckill.gulimall.com
轉發 -
gulimall-gateway
閘道器服務配置gulimall-seckill
秒殺服務路由
管理後臺建立一個當前時間範圍內的秒殺活動,並關聯商品
實現獲取到當前可以參加秒殺商品的資訊
- 獲取當前時間,獲取活動場次的所有
key
,因為key
中包含活動場次開始時間和結束時間,查詢出活動場次中包含的所有秒殺商品的key
(場次Id + "-" + 商品skuId
) - 根據開始時間和結束時間獲取所有秒殺商品的
key
,再去seckill:skus
獲取所有的秒殺商品
秒殺商品介面渲染
主介面秒殺商品渲染完成
管理員執行SwitchHost
,配置seckill.gulimall.com
轉發
gulimall-gateway
閘道器服務配置gulimall-seckill
秒殺服務路由
- id: gulimall-seckill_route
uri: lb://gulimall-seckill
predicates:
- Host=seckill.gulimall.com
Bug:資料庫時間和介面顯示時間相差8小時
主要配置serverTimezone=Asia/Shanghai
和time-zone
spring:
application:
name: gulimall-coupon
cloud:
nacos:
discovery:
server-addr: 192.168.188.180:8848 # nacos地址
datasource:
url: jdbc:mysql://192.168.188.180:3306/mall_sms?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
17.10秒殺頁面渲染
主要步驟:
gulimall-seckill
實現skuId
查詢商品秒殺資訊- 查詢
seckill:skus
(所有秒殺商品資訊),獲取所有keys
- 遍歷
seckill:skus
,判斷key
是否包含skuId
- 如果包含
skuId
,判斷當前時間是否在秒殺活動時間區間
- 查詢
gulimall-product
使用OpenFeign
新增gulimall-seckill
的商品秒殺資訊介面gulimall-product
的skuItem
方法獲取sku
商品詳情時,非同步編排呼叫gulimall-seckill
查詢商品秒殺資訊item.html
商品詳情介面新增秒殺資訊
gulimall-seckill
實現skuId
查詢商品秒殺資訊
- 查詢
seckill:skus
(所有秒殺商品資訊),獲取所有keys
- 遍歷
seckill:skus
,判斷key
是否包含skuId
- 如果包含
skuId
,判斷當前時間是否在秒殺活動時間區間
gulimall-product
使用OpenFeign
新增gulimall-seckill
的商品秒殺資訊介面
gulimall-product
的skuItem
方法獲取sku
商品詳情時,CompletableFuture
非同步編排呼叫gulimall-seckill
查詢商品秒殺資訊
item.html
商品詳情介面新增秒殺資訊
商品詳情介面
17.11秒殺系統設計
秒殺系統設計
- 單一職責
- 秒殺連結加密
- 隨機碼,秒殺開始才暴露
- 庫存預熱+快速扣減(redis儲存庫存訊號量,最終正常進入購物車的流量最多是庫存數)
- 按照庫存訊號量原子扣減
- 動靜分離
- nginx/CDN
- 惡意請求攔截
- 閘道器層按照訪問次數攔截指令碼請求【異常請求】
- 流量錯峰
- 【最重要是體現在秒殺開始的那一刻的錯峰】判斷登入狀態、輸入驗證碼、加入購物車、提交訂單
- 限流&熔斷&降級
- 前端限流:間隔1秒允許點選
- 後端限流:
- 限制次數:同一個使用者10次放行2次
- 限制總量:秒殺服務峰值處理能力10萬,閘道器層放行不得超過10萬,超過的等待兩秒放行
- 熔斷:A->B->C,鏈路中B總是失敗,則下次呼叫時直接返回錯誤不呼叫B
- 降級:流量太大,秒殺模組將流量引導到降級頁面,服務繁忙頁【正常請求】
- 佇列削峰(殺手鐧)
- 扣減庫存訊號量成功的秒殺資訊存入佇列,訂單系統監聽佇列建立訂單(按照自己的處理能力消費
17.12登入檢查
主要步驟:
gulimall-product
商品服務的item.html
商品詳情頁面,如果商品在秒殺活動時段顯示立即搶購,否則顯示加入購物車- 立即搶購需要引數:
killId
(場次活動Id + 商品skuId
)、秒殺隨機碼、商品數量 gulimall-seckill
呼叫秒殺介面/kill
,需要登入操作,因此需要配置攔截器和springsession
gulimall-product
商品服務的item.html
商品詳情頁面,如果商品在秒殺活動時段顯示立即搶購,否則顯示加入購物車
立即搶購需要引數:killId
(場次活動Id + 商品skuId
)、秒殺隨機碼、商品數量
gulimall-seckill
呼叫秒殺介面/kill
,需要登入操作,因此需要配置攔截器和springsession
配置登入攔截器
配置springsession
17.13秒殺流程
主要步驟:
- 實現秒殺介面
- 從
從Redis中獲取
中獲取當前秒殺商品的詳細資訊 - 合法性效驗:判斷當前這個秒殺請求是否在活動時間區間內(效驗時間的合法性)
- 效驗隨機碼和商品id
- 驗證購物數量是否合理和庫存量是否充足:判斷訊號量是否大於0,並且買的數量不能超過限購數量
- 驗證這個人是否已經買過了(冪等性處理),如果秒殺成功,就去佔位。
userId-sessionId-skuId
- 從
17.14秒殺效果完成
流程圖:
兩種方案:
- 方案一:
- 加入購物車(仍然走購物車流程,但價格按照秒殺價格計算),建立訂單、鎖定庫存
- 優點:只需要做好適配,無大改動
- 缺點:將秒殺的流量帶給了其他模組
- 方案二:【採用方案二,佇列削峰】
- 直接傳送MQ訊息,訂單根據訊息建立訂單(不需要鎖定庫存,庫存預熱了【訊號量】),訂單關閉增加訊號量
-
加訊號量
- 優點:沒有將秒殺的壓力分擔給其他模組,只有校驗合法性沒有遠端呼叫、db操作
- 缺點:訂單等模組需要提供監聽消費資訊建立訂單,如果訂單崩了,會導致支付失敗
-
結果:
- 假設一個請求50ms,一個執行緒1s能處理20個請求
- Tomcat開啟500個執行緒,1s能處理10000個請求
主要步驟:
-
實現秒殺介面
- 從
從Redis中獲取
中獲取當前秒殺商品的詳細資訊 - 合法性效驗:判斷當前這個秒殺請求是否在活動時間區間內(效驗時間的合法性)
- 效驗隨機碼和商品id
- 驗證購物數量是否合理和庫存量是否充足:判斷訊號量是否大於0,並且買的數量不能超過限購數量
- 驗證這個人是否已經買過了(冪等性處理),如果秒殺成功,就去佔位。
userId-sessionId-skuId
- 完成以上驗證,向
order-event-exchange
傳送訊息,routingKey
設定為order.seckill.order
- 從
-
gulimall-order
訂單服務建立佇列order.seckill.order.queue
,並和order-event-exchange
建立繫結關係 -
gulimall-order
監聽order.seckill.order.queue
建立秒殺訂單
實現秒殺介面
- 從
從Redis中獲取
中獲取當前秒殺商品的詳細資訊 - 合法性效驗:判斷當前這個秒殺請求是否在活動時間區間內(效驗時間的合法性)
- 效驗隨機碼和商品id
- 驗證購物數量是否合理和庫存量是否充足:判斷訊號量是否大於0,並且買的數量不能超過限購數量
- 驗證這個人是否已經買過了(冪等性處理),如果秒殺成功,就去佔位。
userId-sessionId-skuId
- 完成以上驗證,向
order-event-exchange
傳送訊息,routingKey
設定為order.seckill.order
gulimall-order
訂單服務建立佇列order.seckill.order.queue
,並和order-event-exchange
建立繫結關係
gulimall-order
監聽order.seckill.order.queue
建立秒殺訂單
17.15秒殺頁面完成
主要步驟:
- 秒殺成功,跳轉到秒殺成功頁面,然後進行支付-
gulimall-seckill
匯入thymeleaf
,並關閉快取- 秒殺成功介面渲染
秒殺成功,跳轉到秒殺成功頁面,然後進行支付
gulimall-seckill
匯入thymeleaf
,並關閉快取
秒殺成功介面渲染
18.Sentinel
18.1高併發方法論&簡介
熔斷
A 服務呼叫 B 服務的某個功能,由於網路不穩定問題,或者 B 服務卡機,導致功能時 間超長。如果這樣子的次數太多。我們就可以直接將 B 斷路了(A 不再請求 B 介面),凡是 呼叫 B 的直接返回降級資料,不必等待 B 的超長執行。 這樣 B 的故障問題,就不會級聯影 響到 A。
降級
整個網站處於流量高峰期,伺服器壓力劇增,根據當前業務情況及流量,對一些服務和 頁面進行有策略的降級[停止服務,所有的呼叫直接返回降級資料]。以此緩解伺服器資源的 的壓力,以保證核心業務的正常執行,同時也保持了客戶和大部分客戶的得到正確的相應。
異同:
相同點:
-
1、為了保證叢集大部分服務的可用性和可靠性,防止崩潰,犧牲小我
-
2、使用者最終都是體驗到某個功能不可用
不同點:
- 1、熔斷是被呼叫方故障,觸發的系統主動規則
- 2、降級是基於全域性考慮,停止一些正常服務,釋放資源
限流
對打入服務的請求流量進行控制,使服務能夠承擔不超過自己能力的流量壓力
18.2基本概念
Sentinel 簡介
官方文件:https://github.com/alibaba/Sentinel/wiki/介紹
專案地址:https://github.com/alibaba/Sentinel
隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量為切入點, 從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
Sentinel 具有以下特徵:
- 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場 景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、集 群流量控制、實時熔斷下游不可用應用等。
- 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入 應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。
- 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如 與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配 置即可快速地接入 Sentinel。
- 完善的 SPI 擴充套件點:Sentinel 提供簡單易用、完善的 SPI 擴充套件介面。您可以透過 實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。
Sentinel 分為兩個部分:
- 核心庫(Java 客戶端)不依賴任何框架/庫,能夠執行於所有 Java 執行時環境,同時 對 Dubbo / Spring Cloud 等框架也有較好的支援。
- 控制檯(Dashboard)基於 Spring Boot 開發,打包後可以直接執行,不需要額外的 Tomcat 等應用容器。
Sentinel 基本概念
資源
資源是 Sentinel 的關鍵概念。它可以是 Java 應用程式中的任何內容,例如,由應用程式提 供的服務,或由應用程式呼叫的其它應用提供的服務,甚至可以是一段程式碼。在接下來的文 檔中,我們都會用資源來描述程式碼塊。
只要透過 Sentinel API 定義的程式碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下, 可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。
規則
圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規 則。所有規則可以動態實時調整。
Hystrix 與 Sentinel 比較
18.3整合SpringBoot
主要步驟:
-
docker
安裝sentinel
-
SpringBoot
整合sentinel
-
測試
docker安裝sentinel
如果拉取映象失敗,更換一下映象地址,這種方式最方便
拉取到映象後,後面可以把sentinel
新增到docker-compose.yml
cat <<EOF > /etc/docker/daemon.json
{
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"default-shm-size": "1G",
"debug": true,
"experimental": false,
"registry-mirrors":[
"https://x9r52uz5.mirror.aliyuncs.com",
"https://dockerhub.icu",
"https://docker.chenby.cn",
"https://docker.1panel.live",
"https://docker.awsl9527.cn",
"https://docker.anyhub.us.kg",
"https://dhub.kubesre.xyz"
]
}
EOF
systemctl daemon-reload
systemctl restart docker
docker pull bladex/sentinel-dashboard
docker run --name sentinel -p 8858:8858 -td bladex/sentinel-dashboard
訪問http://你的虛擬機器ip:8858/#/dashboard
使用者名稱:sentinel
密碼:sentinel
整合SpringBoot
Spring Cloud Alibaba Sentinel
文件說明:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
這裡以gulimall-seckill
秒殺服務為主,正常來說所有服務都需要引用sentinel
匯入依賴
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
不知道為什麼這裡現在需要匯入hibernate-validator
,要不會報錯,記錄一下
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
配置application.yaml
,sentinel
在spring.cloud
節點下
spring:
cloud:
sentinel:
transport:
# 控制檯資訊
dashboard: 192.168.188.180:8858
測試
啟動專案,設定/getCurrentSeckillSkus
的QPS
為1,就是每s只允許1個請求
訪問介面http://seckill.gulimall.com/getCurrentSeckillSkus,連續多點幾次,發現請求會直接返回失敗
後臺控制檯發現介面只被呼叫2次,說明收到了流控控制
18.4自定義流控響應
主要步驟:
-
自定義流控響應
-
application.yaml
配置,暴露的endpoint
路徑為/actuator/sentinel
-
/getCurrentSeckillSkus
新增QPS
官方地址:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#endpoint-支援
bug:匯入Sentinel無WebCallbackManager包
https://blog.csdn.net/m0_70651612/article/details/125365554
自定義流控響應
我用的最新版的sentinel
,之前的WebCallbackManager
實現的自定義響應好像不太好使,查詢資料後發現需要實現sentinel-spring-webmvc-adapter
包的BlockExceptionHandler
匯入spring-boot-starter-actuator
,是審計框架,計算springboot應用健康狀況資訊、請求的呼叫資訊,dashboard可以拿到actuator資料作實時監控統計
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
匯入sentinel-spring-webmvc-adapter
,實現自定義響應
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
</dependency>
實現BlockExceptionHandler
@Configuration
public class MySentinelConfig implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMsg());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(error));
}
}
application.yaml
配置,暴露的 endpoint
路徑為 /actuator/sentinel
management:
endpoints:
web:
exposure:
include: '*'
/getCurrentSeckillSkus
新增QPS
因為Sentinel
儲存在服務記憶體中,每次重啟服務都需要重新配置
訪問介面:http://seckill.gulimall.com/getCurrentSeckillSkus,F5
多重新整理幾次
審計介面
18.5全服務引入
主要步驟:
- 所有的服務匯入
sentinel
相關包 - 所有的服務
application.yml
配置sentinel
- 把商城服務的所有的流程走一遍
所有的服務匯入sentinel
相關包
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--sentinel統計審計資訊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
</dependency>
所有的服務配置sentinel
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.188.180:8848 # nacos地址
sentinel:
transport:
# 應用開啟埠,接收dashboard限流規則
# port: 8719
# 控制檯資訊
dashboard: 192.168.188.180:8858
management:
endpoints:
web:
exposure:
include: '*'
把服務的所有的流程走一遍,到支付即可
Sentinel
成功監控了所有的服務
18.6流控模式&效果
官方文件:https://github.com/alibaba/Sentinel/wiki/流量控制
18.7熔斷降級
主要步驟:
- 呼叫方的熔斷保護:
feign.sentinel.enable=true
- 呼叫方手動指定遠端服務的降級策略。遠端服務被降級處理。觸發我們的熔斷回撥方法
- 超大流量的時候,必須犧牲一些遠端服務。在服務的提供方(遠端服務)指定降級策略;提供方是在執行,但是不允許自己的業務邏輯,返回的是預設的降級資料(限流的資料)
呼叫方的熔斷保護
Feign
支援文件:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#feign-支援
匯入依賴,使 Sentinel starter
中的自動化配置類生效
<!--sentinel Feign支援-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
新增配置,開啟 Sentinel
對 Feign
的支援
feign:
sentinel:
enabled: true
我們發現gulimall-product
商品服務中呼叫的openfeign
遠端和介面也顯示出來了
建立SeckillFeignServiceFallBack
,實現SeckillFeignService
當遠端呼叫SeckillFeignService
失敗時,會呼叫這個介面進行返回
因為商品詳情會遠端呼叫gulimall-seckill
判斷是夠是秒殺商品,關掉gulimall-seckill
服務模擬當機,發現介面依然顯示,沒有出錯
呼叫方手動指定遠端服務的降級策略。遠端服務被降級處理,觸發我們的熔斷回撥方法
熔斷策略文件:https://github.com/alibaba/Sentinel/wiki/熔斷降級#熔斷策略
修改測試程式碼,/sku/seckill/{skuId}
睡眠500ms,使該介面觸發熔斷
配置熔斷規則:
-
最大 RT:這個值表示請求的最大響應時間閾值(單位為毫秒)。在這個配置中,最大 RT 設定為
1 ms
,這意味著如果某個請求的響應時間超過 1 毫秒,就會觸發統計。 -
熔斷時長:熔斷器觸發後的持續時間。在這裡,熔斷時長設定為
5 秒
,即一旦熔斷觸發,該資源在 5 秒內將會被熔斷,所有請求將直接返回異常。 -
統計時長:這是用於統計的時間視窗(單位為毫秒)。在這個配置中,統計時長設定為
1000 ms
(即 1 秒)。在這個時間視窗內,Sentinel 將會統計請求的響應時間,並根據配置的閾值來決定是否觸發熔斷。 -
比閾值:表示在統計時長內,請求的 RT 超過最大 RT 閾值的比例閾值。在這裡設定為
0.1
(10%),意味著如果在統計時長內,有 10% 的請求的響應時間超過了最大 RT,則觸發熔斷。 -
最小請求數:這是觸發熔斷所需的最小請求數。在這個配置中,設定為
1
,意味著只要有一個請求超過了最大 RT 且滿足比閾值的條件,熔斷就會觸發。
總結來說,這個配置的意思是:如果在 1 秒內有至少 1 個請求且其中 10% 以上的請求的響應時間超過了 1 毫秒,那麼這個資源將被熔斷 5 秒。
超大流量的時候,必須犧牲一些遠端服務。在服務的提供方(遠端服務)指定降級策略;提供方是在執行。但是不執行自己的業務邏輯,返回的是預設的降級資料(限流的資料)
我們給gulimall-seckill
服務的/getCurrentSeckillSkus
的介面配置熔斷策略
- 最大 RT:設定為
1 ms
,表示如果請求的響應時間超過 1 毫秒,這個請求將會被納入統計。 - 熔斷時長:設定為
5 秒
,表示一旦觸發熔斷,這個資源會在接下來的 5 秒內直接返回異常,不會真正執行請求。 - 統計時長:設定為
1000 ms
,表示 Sentinel 會在 1 秒的時間視窗內統計請求的響應時間。 - 比閾值:設定為
0.3
(30%),表示在統計時長內,如果有 30% 以上的請求響應時間超過了最大 RT(1 ms),將會觸發熔斷。 - 最小請求數:設定為
3
,表示在統計時長內,至少有 3 個請求時,才會開始計算並判斷是否觸發熔斷。
總結來說,這個配置表示:如果在 1 秒內,至少有 3 個請求被處理,並且其中 30% 的請求響應時間超過了 1 毫秒,那麼這個資源將會熔斷 5 秒。
快速重新整理請求會觸發熔斷策略
給所有的服務配置Sentinel
對 Feign
的支援和Sentinel
的配置
18.8自定義受保護資源
主要步驟:
- 基於程式碼 :
try (Entry entry = SphU.entry("seckillSkus")) {}catch (BlockException e) { log.error("資源被限流{}", e.getMessage()); }
- 官方文件:https://github.com/alibaba/Sentinel/wiki/如何使用#方式二丟擲異常的方式定義資源
- 基於註解:
@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")
- 官方文件:https://github.com/alibaba/Sentinel/wiki/如何使用#方式二丟擲異常的方式定義資源
基於程式碼 :try (Entry entry = SphU.entry("seckillSkus")) {}catch (BlockException e) { log.error("資源被限流{}", e.getMessage()); }
配置seckillSkus
流控規則,每s最多一個請求
官方文件
基於註解:@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")
配置getCurrentSeckillSkusResource
流控規則,每s最多一個請求
官方文件
18.9閘道器流控
主要步驟:
-
官網地址:https://github.com/alibaba/Sentinel/wiki/閘道器限流
-
匯入
spring-cloud-alibaba-sentinel-gateway
-
配置閘道器流控
官網地址:https://github.com/alibaba/Sentinel/wiki/閘道器限流
匯入spring-cloud-alibaba-sentinel-gateway
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
配置閘道器流控
快速訪問http://seckill.gulimall.com/getCurrentSeckillSkus,發現
配置gulimall-seckill_route
請求Header
必須帶上hello
,並且值必須是world
瀏覽器請求http://seckill.gulimall.com/getCurrentSeckillSkus
不會被攔截
使用postman
帶上Header
的hello=world
請求會被限流
18.10定製閘道器流控返回
主要步驟:
- 官方文件:https://github.com/alibaba/Sentinel/wiki/閘道器限流
gulimall-gateway
閘道器服務重寫GatewayCallbackManager.setBlockHandler
- 配置
gulimall-seckill_route
流控規則 - 測試
gulimall-gateway
閘道器服務重寫GatewayCallbackManager.setBlockHandler
配置gulimall-seckill_route
流控規則
測試
19.Sleuth-鏈路追蹤
19.1基本概念&整合
sleuth文件: https://spring.io/projects/spring-cloud-sleuth
started: https://docs.spring.io/spring-cloud-sleuth/docs/3.1.0/reference/html/getting-started.html#getting-started
為什麼用
微服務架構是一個分散式架構,它按業務劃分服務單元,一個分散式系統往往有很多個服務 單元。由於服務單元數量眾多,業務的複雜性,如果出現了錯誤和異常,很難去定位。主要 體現在,一個請求可能需要呼叫很多個服務,而內部服務的呼叫複雜性,決定了問題難以 定位。所以微服務架構中,必須實現分散式鏈路追蹤,去跟進一個請求到底有哪些服務參與, 參與的順序又是怎樣的,從而達到每個請求的步驟清晰可見,出了問題,很快定位。
鏈路追蹤元件有 Google 的 Dapper,Twitter 的 Zipkin,以及阿里的 Eagleeye (鷹眼)等,它 們都是非常優秀的鏈路追蹤開源元件。
基本術語
- Span(跨度):基本工作單元,傳送一個遠端排程任務 就會產生一個 Span,Span 是一 個 64 位 ID 唯一標識的,Trace 是用另一個 64 位 ID 唯一標識的,Span 還有其他資料信 息,比如摘要、時間戳事件、Span 的 ID、以及進度 ID。
- Trace(跟蹤):一系列 Span 組成的一個樹狀結構。請求一個微服務系統的 API 介面, 這個 API 介面,需要呼叫多個微服務,呼叫每個微服務都會產生一個新的 Span,所有 由這個請求產生的 Span 組成了這個 Trace。
- Annotation(標註):用來及時記錄一個事件的,一些核心註解用來定義一個請求的開 始和結束 。這些註解包括以下:
- cs - Client Sent -客戶端傳送一個請求,這個註解描述了這個 Span 的開始
- sr - Server Received -服務端獲得請求並準備開始處理它,如果將其 sr 減去 cs 時間戳 便可得到網路傳輸的時間。
- ss - Server Sent (服務端傳送響應)–該註解表明請求處理的完成(當請求返回客戶 端),如果 ss 的時間戳減去 sr 時間戳,就可以得到伺服器請求的時間。
- cr - Client Received (客戶端接收響應)-此時 Span 的結束,如果 cr 的時間戳減去 cs 時間戳便可以得到整個請求所消耗的時間。
官方文件:https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started
如果服務呼叫順序如下
那麼用以上概念完整的表示出來如下:
整合Sleuth
所有的服務提供者與消費者匯入依賴
<!-- sleuth 鏈路追蹤-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
開啟 debug 日誌
logging:
level:
com.peng: debug
org.springframework.cloud.openfeign: debug
org.springframework.cloud.sleuth: debug
19.2整合Zipkin效果
docker安裝zipkin
安裝命令
docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin
整合zipkin
匯入依賴
我用的spring-cloud
版本沒有Zipkin
所以要自己指定版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
zipkin
中包含sleuth
,所以只需要配置zipkin
即可
配置zipkin
和sleuth
訪問:http://192.168.188.180:9411
zipkin資料持久化
持久化型別選擇elasticsearch
,因為elasticsearch
查詢速度更快
docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.188.180:9200 openzipkin/zipkin-dependencies
19.3Zipkin介面分析
查詢介面
提交訂單
訂單完成,向rabbitmq
傳送訊息
訂單確認頁,顯示非同步編排呼叫介面
檢視所有服務依賴
檢視gulimall-gateway
服務依賴
20.分散式高階篇總結
01-EleasticSearch、商品上架、首頁、nginx動靜分離、Apache JMeter使用、快取、Redisson、SpringCache
02-檢索服務、CompletableFuture非同步編排、商品詳情
03-認證服務、SpringSession、單點登入、購物車
04-RabbitMQ、訂單確認
05-Seata、RabbitMQ延時佇列、支付寶支付、訂單服務
06-秒殺服務、Sentinel、Sleuth、Zipkin
創作不易,感謝支援。