06穀粒商城-高階篇六

peng_boke發表於2024-10-14

前言

人到洛陽花似錦,偏我來時不逢春

17.商城業務-秒殺服務

17.1後臺新增秒殺商品

主要步驟:

  • 匯入每日秒殺前端介面
  • gulimall-gateway閘道器服務配置gulimall-coupon路由
  • 最佳化秒殺關聯商品介面

匯入每日秒殺前端介面

image-20240820010232151

gulimall-gateway閘道器服務配置gulimall-coupon路由

image-20240820010322230

最佳化秒殺關聯商品介面

image-20240820010519374

介面

image-20240820010549678

17.2定時任務&Cron表示式

建立gulimall-seckill秒殺服務

建立gulimall-seckill秒殺服務

image-20240820201135735

匯入依賴,主要有rabbitmqredisspringsession

image-20240820202908624

配置application.yaml

image-20240820202930534

gulimall-seckill開啟SpringSessionOpenFeignnacos,禁用datasource

image-20240820202945858

定時任務

語法:秒 分 時 日 月 周 年(Spring 不支援)

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

image-20240820203200736

cron 示例

image-20240820203225277

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(); }
    }
}

image-20240820204927308

定時任務+非同步任務

  • 方案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 # 最大執行緒數

image-20240820205454470

17.4時間日期處理

主要步驟:

  • gulimall-seckill秒殺服務遠端呼叫gulimall-coupon優惠卷服務查詢最近三天的活動
  • gulimall-coupon優惠卷服務實現查詢最近三天的活動
    • 處理最近三天的日期

gulimall-seckill秒殺服務遠端呼叫gulimall-coupon優惠卷服務查詢最近三天的活動

image-20240820213720100

gulimall-coupon優惠卷服務實現查詢最近三天的活動

  • 處理最近三天的日期

image-20240820213758106

測試程式碼

@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);
    }

image-20240820213630284

17.5秒殺商品上架-1

主要步驟:

  • 遠端呼叫gulimall-coupon查詢最近三天的活動
  • redis中快取活動資訊
  • redis中快取活動關聯的商品資訊
    • 商品資訊,遠端呼叫gulimall-product,獲取商品sku資訊

redis中快取活動資訊

image-20240820215727896

redis中快取活動關聯的商品資訊

  • 商品資訊

image-20240820220014740

17.6秒殺商品上架-2

主要步驟:

  • 遠端呼叫gulimall-coupon查詢最近三天的活動
  • redis中快取活動資訊
  • redis中快取活動關聯的商品資訊
    • 商品資訊,遠端呼叫gulimall-product,獲取商品sku資訊
    • 秒殺資訊
    • 秒殺時間資訊
    • 設定商品的隨機碼(防止惡意攻擊)
    • 使用庫存作為分散式Redisson訊號量(限流)

image-20240820221151247

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();
    }
}

image-20240820223237920

快取活動資訊的keySESSION_CACHE_PREFIX + startTime + "_" + endTime

快取活動資訊的value活動場次Id + "_" + 商品skuId

image-20240820222346268

快取秒殺商品的的keyseckill:skus

快取秒殺商品的的value

  • key活動場次Id + "_" + 商品skuId
  • value:商品sku資訊、秒殺資訊、秒殺時間資訊、商品的隨機碼(防止惡意攻擊)、使用秒殺總量作為分散式Redisson訊號量(限流)

image-20240820222618807

使用秒殺總量作為分散式Redisson訊號量(限流)

分散式Redisson訊號量的keyseckill:stock:

分散式Redisson訊號量的value:秒殺總量

image-20240820223022448

17.8冪等性保證

主要步驟:

  • 秒殺商品上架時新增分散式鎖
  • redis儲存活動資訊,判斷seckill:sessions: + startTime + "_" + endTime是否已經存在
  • redis儲存秒殺商品資訊時,判斷seckill:skus中是否存在活動場次Id + "-" + 商品skuId

秒殺商品上架時新增分散式鎖

image-20240820232431085

redis儲存活動資訊,判斷seckill:sessions: + startTime + "_" + endTime是否已經存在

image-20240820231955454

redis儲存秒殺商品資訊時,判斷seckill:skus中是否存在活動場次Id + "-" + 商品skuId

image-20240820232211591

17.9查詢秒殺商品

主要步驟:

  • 管理後臺建立一個當前時間範圍內的秒殺活動,並關聯商品

  • 實現獲取到當前可以參加秒殺商品的資訊

    • 獲取當前時間,獲取活動場次的所有key,因為key中包含活動場次開始時間和結束時間,查詢出活動場次中包含的所有秒殺商品的key(場次Id + "-" + 商品skuId
    • 根據開始時間和結束時間獲取所有秒殺商品的key,再去seckill:skus獲取所有的秒殺商品
  • 秒殺商品介面渲染

  • 管理員執行SwitchHost,配置seckill.gulimall.com轉發

  • gulimall-gateway閘道器服務配置gulimall-seckill秒殺服務路由

管理後臺建立一個當前時間範圍內的秒殺活動,並關聯商品

image-20240821045806681

實現獲取到當前可以參加秒殺商品的資訊

  • 獲取當前時間,獲取活動場次的所有key,因為key中包含活動場次開始時間和結束時間,查詢出活動場次中包含的所有秒殺商品的key(場次Id + "-" + 商品skuId
  • 根據開始時間和結束時間獲取所有秒殺商品的key,再去seckill:skus獲取所有的秒殺商品

image-20240821005057301

秒殺商品介面渲染

image-20240821005150648

主介面秒殺商品渲染完成

image-20240821012027008

管理員執行SwitchHost,配置seckill.gulimall.com轉發

image-20240821050902272

gulimall-gateway閘道器服務配置gulimall-seckill秒殺服務路由

- id: gulimall-seckill_route
  uri: lb://gulimall-seckill
  predicates:
    - Host=seckill.gulimall.com

image-20240821020332548

Bug:資料庫時間和介面顯示時間相差8小時

主要配置serverTimezone=Asia/Shanghaitime-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

image-20240821011941905

17.10秒殺頁面渲染

主要步驟:

  • gulimall-seckill實現skuId查詢商品秒殺資訊
    • 查詢seckill:skus(所有秒殺商品資訊),獲取所有keys
    • 遍歷seckill:skus,判斷key是否包含skuId
    • 如果包含skuId,判斷當前時間是否在秒殺活動時間區間
  • gulimall-product使用OpenFeign新增gulimall-seckill的商品秒殺資訊介面
  • gulimall-productskuItem方法獲取sku商品詳情時,非同步編排呼叫gulimall-seckill查詢商品秒殺資訊
  • item.html商品詳情介面新增秒殺資訊

gulimall-seckill實現skuId查詢商品秒殺資訊

  • 查詢seckill:skus(所有秒殺商品資訊),獲取所有keys
  • 遍歷seckill:skus,判斷key是否包含skuId
  • 如果包含skuId,判斷當前時間是否在秒殺活動時間區間

image-20240821022103125

gulimall-product使用OpenFeign新增gulimall-seckill的商品秒殺資訊介面

image-20240821022121262

gulimall-productskuItem方法獲取sku商品詳情時,CompletableFuture非同步編排呼叫gulimall-seckill查詢商品秒殺資訊

image-20240821022153944

item.html商品詳情介面新增秒殺資訊

image-20240821022330374

商品詳情介面

image-20240821022351377

17.11秒殺系統設計

秒殺系統設計

  • 單一職責
  • 秒殺連結加密
    • 隨機碼,秒殺開始才暴露
  • 庫存預熱+快速扣減(redis儲存庫存訊號量,最終正常進入購物車的流量最多是庫存數)
    • 按照庫存訊號量原子扣減
  • 動靜分離
    • nginx/CDN
  • 惡意請求攔截
    • 閘道器層按照訪問次數攔截指令碼請求【異常請求】
  • 流量錯峰
    • 【最重要是體現在秒殺開始的那一刻的錯峰】判斷登入狀態、輸入驗證碼、加入購物車、提交訂單
  • 限流&熔斷&降級
    • 前端限流:間隔1秒允許點選
    • 後端限流:
      • 限制次數:同一個使用者10次放行2次
      • 限制總量:秒殺服務峰值處理能力10萬,閘道器層放行不得超過10萬,超過的等待兩秒放行
    • 熔斷:A->B->C,鏈路中B總是失敗,則下次呼叫時直接返回錯誤不呼叫B
    • 降級:流量太大,秒殺模組將流量引導到降級頁面,服務繁忙頁【正常請求】
  • 佇列削峰(殺手鐧)
    • 扣減庫存訊號量成功的秒殺資訊存入佇列,訂單系統監聽佇列建立訂單(按照自己的處理能力消費

image-20240821023625624

image-20240821023631646

17.12登入檢查

主要步驟:

  • gulimall-product商品服務的item.html商品詳情頁面,如果商品在秒殺活動時段顯示立即搶購,否則顯示加入購物車
  • 立即搶購需要引數:killId(場次活動Id + 商品skuId)、秒殺隨機碼、商品數量
  • gulimall-seckill呼叫秒殺介面/kill,需要登入操作,因此需要配置攔截器和springsession

gulimall-product商品服務的item.html商品詳情頁面,如果商品在秒殺活動時段顯示立即搶購,否則顯示加入購物車

image-20240821030212197

立即搶購需要引數:killId(場次活動Id + 商品skuId)、秒殺隨機碼、商品數量

image-20240821030230316

gulimall-seckill呼叫秒殺介面/kill,需要登入操作,因此需要配置攔截器和springsession

配置登入攔截器

image-20240821030309173

配置springsession

image-20240821030414408

17.13秒殺流程

image-20240821043532371

主要步驟:

  • 實現秒殺介面
    • 從Redis中獲取中獲取當前秒殺商品的詳細資訊
    • 合法性效驗:判斷當前這個秒殺請求是否在活動時間區間內(效驗時間的合法性)
    • 效驗隨機碼和商品id
    • 驗證購物數量是否合理和庫存量是否充足:判斷訊號量是否大於0,並且買的數量不能超過限購數量
    • 驗證這個人是否已經買過了(冪等性處理),如果秒殺成功,就去佔位。userId-sessionId-skuId

image-20240821044347519

17.14秒殺效果完成

流程圖:

image-20240821044503294

兩種方案:

  • 方案一:
    • 加入購物車(仍然走購物車流程,但價格按照秒殺價格計算),建立訂單、鎖定庫存
    • 優點:只需要做好適配,無大改動
    • 缺點:將秒殺的流量帶給了其他模組
    • image-20240821052907381

image-20240821052858858

  • 方案二:【採用方案二,佇列削峰】
    • 直接傳送MQ訊息,訂單根據訊息建立訂單(不需要鎖定庫存,庫存預熱了【訊號量】),訂單關閉增加訊號量

image-20240821052925646

  • 加訊號量

    • 優點:沒有將秒殺的壓力分擔給其他模組,只有校驗合法性沒有遠端呼叫、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

image-20240821053022703

gulimall-order訂單服務建立佇列order.seckill.order.queue,並和order-event-exchange建立繫結關係

image-20240821053058111

gulimall-order監聽order.seckill.order.queue建立秒殺訂單

image-20240821053130132

17.15秒殺頁面完成

主要步驟:

  • 秒殺成功,跳轉到秒殺成功頁面,然後進行支付-
  • gulimall-seckill匯入thymeleaf,並關閉快取
  • 秒殺成功介面渲染

秒殺成功,跳轉到秒殺成功頁面,然後進行支付

image-20240821221724185

gulimall-seckill匯入thymeleaf,並關閉快取

image-20240821221926563

秒殺成功介面渲染

image-20240821221803916

image-20240821221949603

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 擴充套件介面。您可以透過 實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。

image-20240822214331035

Sentinel 分為兩個部分:

  • 核心庫(Java 客戶端)不依賴任何框架/庫,能夠執行於所有 Java 執行時環境,同時 對 Dubbo / Spring Cloud 等框架也有較好的支援。
  • 控制檯(Dashboard)基於 Spring Boot 開發,打包後可以直接執行,不需要額外的 Tomcat 等應用容器。

Sentinel 基本概念

資源

資源是 Sentinel 的關鍵概念。它可以是 Java 應用程式中的任何內容,例如,由應用程式提 供的服務,或由應用程式呼叫的其它應用提供的服務,甚至可以是一段程式碼。在接下來的文 檔中,我們都會用資源來描述程式碼塊。

只要透過 Sentinel API 定義的程式碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下, 可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。

規則

圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規 則。所有規則可以動態實時調整。

Hystrix 與 Sentinel 比較

image-20240822213910554

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

image-20240822224249621

訪問http://你的虛擬機器ip:8858/#/dashboard

使用者名稱:sentinel

密碼:sentinel

image-20240822224327944

image-20240822224315418

整合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>

image-20240822231157031

配置application.yamlsentinelspring.cloud節點下

spring:
  cloud:
    sentinel:
      transport:
        # 控制檯資訊
        dashboard: 192.168.188.180:8858

image-20240822231916005

測試

啟動專案,設定/getCurrentSeckillSkusQPS為1,就是每s只允許1個請求

image-20240822232220409

訪問介面http://seckill.gulimall.com/getCurrentSeckillSkus,連續多點幾次,發現請求會直接返回失敗

image-20240822232352858

後臺控制檯發現介面只被呼叫2次,說明收到了流控控制

image-20240822232336810

18.4自定義流控響應

主要步驟:

  • 自定義流控響應

  • application.yaml配置,暴露的 endpoint 路徑為 /actuator/sentinel

  • /getCurrentSeckillSkus新增QPS

官方地址:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#endpoint-支援

image-20240823000643637

bug:匯入Sentinel無WebCallbackManager包

https://blog.csdn.net/m0_70651612/article/details/125365554

image-20240823001853904

自定義流控響應

我用的最新版的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));
    }
}

image-20240823004725135

application.yaml配置,暴露的 endpoint 路徑為 /actuator/sentinel

management:
  endpoints:
    web:
      exposure:
        include: '*'

image-20240823005055418

/getCurrentSeckillSkus新增QPS

因為Sentinel 儲存在服務記憶體中,每次重啟服務都需要重新配置

image-20240823003649549

訪問介面:http://seckill.gulimall.com/getCurrentSeckillSkus,F5多重新整理幾次

image-20240823003742016

審計介面

image-20240823005200631

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>

image-20240823012101728

所有的服務配置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: '*' 

image-20240823012039038

把服務的所有的流程走一遍,到支付即可

image-20240823012256700

Sentinel成功監控了所有的服務

image-20240823012323508

18.6流控模式&效果

官方文件:https://github.com/alibaba/Sentinel/wiki/流量控制

image-20240823013429767

18.7熔斷降級

主要步驟:

  • 呼叫方的熔斷保護:feign.sentinel.enable=true
  • 呼叫方手動指定遠端服務的降級策略。遠端服務被降級處理。觸發我們的熔斷回撥方法
  • 超大流量的時候,必須犧牲一些遠端服務。在服務的提供方(遠端服務)指定降級策略;提供方是在執行,但是不允許自己的業務邏輯,返回的是預設的降級資料(限流的資料)

呼叫方的熔斷保護

Feign支援文件:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#feign-支援

image-20240823021950541

匯入依賴,使 Sentinel starter 中的自動化配置類生效

<!--sentinel Feign支援-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

新增配置,開啟 Sentinel Feign 的支援

feign:
  sentinel:
    enabled: true

image-20240823023646847

我們發現gulimall-product商品服務中呼叫的openfeign遠端和介面也顯示出來了

image-20240823023916230

建立SeckillFeignServiceFallBack,實現SeckillFeignService

當遠端呼叫SeckillFeignService失敗時,會呼叫這個介面進行返回

image-20240823024251624

因為商品詳情會遠端呼叫gulimall-seckill判斷是夠是秒殺商品,關掉gulimall-seckill服務模擬當機,發現介面依然顯示,沒有出錯

image-20240823024014718

呼叫方手動指定遠端服務的降級策略。遠端服務被降級處理,觸發我們的熔斷回撥方法

熔斷策略文件:https://github.com/alibaba/Sentinel/wiki/熔斷降級#熔斷策略

image-20240823025132384

修改測試程式碼,/sku/seckill/{skuId}睡眠500ms,使該介面觸發熔斷

image-20240826014508196

配置熔斷規則:

  • 最大 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 秒。

image-20240826014110157

超大流量的時候,必須犧牲一些遠端服務。在服務的提供方(遠端服務)指定降級策略;提供方是在執行。但是不執行自己的業務邏輯,返回的是預設的降級資料(限流的資料)

我們給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 秒。

image-20240826015704180

快速重新整理請求會觸發熔斷策略

image-20240826015855696

給所有的服務配置Sentinel Feign 的支援和Sentinel的配置

image-20240826020040579

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()); }

image-20240826022512679

配置seckillSkus流控規則,每s最多一個請求

image-20240826022953944

官方文件

image-20240826023543397

基於註解:@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")

image-20240826022845733

配置getCurrentSeckillSkusResource流控規則,每s最多一個請求

image-20240826022928256

官方文件

image-20240826023627295

18.9閘道器流控

主要步驟:

  • 官網地址:https://github.com/alibaba/Sentinel/wiki/閘道器限流

  • 匯入spring-cloud-alibaba-sentinel-gateway

  • 配置閘道器流控

官網地址:https://github.com/alibaba/Sentinel/wiki/閘道器限流

image-20240826030201039

匯入spring-cloud-alibaba-sentinel-gateway

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

image-20240826024704694

配置閘道器流控

image-20240826024949798

快速訪問http://seckill.gulimall.com/getCurrentSeckillSkus,發現

image-20240826025239181

配置gulimall-seckill_route請求Header必須帶上hello,並且值必須是world

image-20240826025526063

瀏覽器請求http://seckill.gulimall.com/getCurrentSeckillSkus不會被攔截

使用postman帶上Headerhello=world請求會被限流

image-20240826025737009

18.10定製閘道器流控返回

主要步驟:

  • 官方文件:https://github.com/alibaba/Sentinel/wiki/閘道器限流
  • gulimall-gateway閘道器服務重寫GatewayCallbackManager.setBlockHandler
  • 配置gulimall-seckill_route流控規則
  • 測試

gulimall-gateway閘道器服務重寫GatewayCallbackManager.setBlockHandler

image-20240826031158140

配置gulimall-seckill_route流控規則

image-20240826031107103

測試

image-20240826031134387

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

如果服務呼叫順序如下

image-20240826234832207

那麼用以上概念完整的表示出來如下:

image-20240826234734915

整合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

image-20240826235302260

19.2整合Zipkin效果

docker安裝zipkin

安裝命令

docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin

image-20240827000558089

整合zipkin

匯入依賴

我用的spring-cloud版本沒有Zipkin

image-20240827015454147

所以要自己指定版本

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

image-20240827015740716

zipkin中包含sleuth,所以只需要配置zipkin即可

image-20240827015700087

配置zipkinsleuth

image-20240827015621410

訪問:http://192.168.188.180:9411

image-20240827015913386

zipkin資料持久化

持久化型別選擇elasticsearch,因為elasticsearch查詢速度更快

docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.188.180:9200 openzipkin/zipkin-dependencies

19.3Zipkin介面分析

查詢介面

image-20240827020623074

提交訂單

image-20240827021148266

訂單完成,向rabbitmq傳送訊息

image-20240827021235036

訂單確認頁,顯示非同步編排呼叫介面

image-20240827021713237

檢視所有服務依賴

image-20240827022036462

檢視gulimall-gateway服務依賴

image-20240827022056100

20.分散式高階篇總結

01-EleasticSearch、商品上架、首頁、nginx動靜分離、Apache JMeter使用、快取、Redisson、SpringCache
02-檢索服務、CompletableFuture非同步編排、商品詳情
03-認證服務、SpringSession、單點登入、購物車
04-RabbitMQ、訂單確認
05-Seata、RabbitMQ延時佇列、支付寶支付、訂單服務
06-秒殺服務、Sentinel、Sleuth、Zipkin

image-20240827022904684

創作不易,感謝支援。

wxzf

相關文章