主頁 > 軟體設計 > Spring Cloud Alibaba學習總結

Spring Cloud Alibaba學習總結

2021-01-01 13:33:30 軟體設計

Spring Cloud Alibaba

1.入門簡介

1.1 為什么會出現SpringCloud alibaba

spring cloud Netflix進入維護模式

1.2 是什么

2018.10.31, Spring cloud Alibaba正式入駐了Spring Cloud官方范訓器,并在Maven中央庫發布了第一個版本.

1.3 能干嘛

  1. 服務限流降級

    默認支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降級功能的接入,可以在運行時通過控制臺實時修改限流降級規則,還支持查看限流降級Metrics監控

  2. 服務注冊與發現

    適配Spring Cloud服務注冊與發現標準,默認集成了Ribbon的支持

  3. 分布式配置管理

    支持分布式系統中的外部化配置,配置更改時自動重繪

  4. 訊息驅動能力

    基于Spring Cloud Stream為微服務應用構建訊息驅動能力

  5. 阿里云存盤物件

    阿里云提供的海量、安全、低成本、高可靠的云存盤服務.支持在任何應用、任何時間、任何地點存盤和訪問任意型別的資料

  6. 分布式任務調度

    提供秒級、精準、高可靠、高可用的定時(基于Cron運算式)任務調度服務.同時提供分布式的任務執行模型,如網格任務.網格任務支持海量子任務均勻分配到所有Worker(schedulerx-client)上執行

1.4 怎么用

  1. Sentinel

    阿里巴巴開源產品,把流量作為切入點,從流量控制,熔斷降級,系統負載保護等多個維度保護服務的穩定性.

  2. Nacos

    阿里巴巴開源產品,一個更易于構建云原生應用的動態服務發現,配置管理和服務管理平臺.

  3. RocketMQ

    Apache RocketMQ基于Java的高性能,高吞吐量的分布式訊息和流計算平臺.

  4. Dubbo

    Apache Dubbo是一款高性能的Java RPC框架.

  5. Seata

    阿里巴巴開源產品,一個易于使用的高性能微服務分布式事務解決方案.

  6. Alibaba Cloud OSS

    阿里云物件存盤服務器(Object Storage Service,簡稱OSS),是阿里云提供的海量,安全,低成本,高可靠的云存盤服務.

  7. Alibaba Cloud Schedulerx

    阿里中間件團隊開發的一款分布式調度產品,支持周期性的任務與固定時間點觸發任務.

2. SpringCloud Alibaba Nacos服務注冊和配置中心

2.1 Nacos(Naming Configuration Service)簡介

2.1.1 是什么
  1. 一個更易于構建云遠程應用的動態服務發現,配置管理和服務管理平臺
  2. Nacos就是注冊中心 + 配置中心的組合 等價于 Nacos = Eureka + Config + Bus
2.1.2 能干嘛
  1. 替代Eureka做服務注冊中心
  2. 替代Config做服務配置中心
2.1.3 注冊中心比較
服務注冊與發現框架CAP模型控制臺管理社區活躍度
EurekaAP支持低(2.x版本閉源)
ZookeeperCP不支持
ConsulCP支持
NacosAP/CP支持
  1. nacos和CAP

在這里插入圖片描述
在這里插入圖片描述

2.2 Nacos安裝

  1. 本地Java8+Maven環境已經OK
  2. 先從官網下載Nacos
  3. 解壓安裝包,直接運行bin目錄下的startup.cmd
  4. 運行成功后訪問http://localhost:8848/nacos
  5. 默認賬號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兩種模式的切換
  1. C是所有節點在同一時間看到的資料是一致的;而A的定義是所有的請求都會收到回應.

  2. 何時選擇何種模式

    一般來說,

    如果不需要存盤服務級別的資訊且服務實體是通過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

20201223114853032

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三者關系
  1. 是什么

    1. 類似于Java里面的package名和類名
    2. 最外層的namespace是可以用于魚粉部署環境的,Group和DataID邏輯上區分兩個目標物件
  2. 三者情況

images\image-20201223140229213.png)]

  1. 默認情況

    Namespace=public,Group=DEFAULT_GROUP,默認Cluster是DEFAULT

  1. Nacos默認的命名空間是public,Namespace主要是用來實作隔離.
  2. 比方說我們說我們現在有三個開發環境:開發,測驗,生產環境,我們就可以創建三個Namespace,不同的Namespace之間是隔離的.
  3. Group默認是DEFAULT_GROUP,Group可以把不同的微服務劃分到同一個分組里面去
  4. Service就是微服務;一個Service可以包含多個Cluster(集群),Nacos默認Cluster是DEFAULT,Cluster是指對指定微服務的一個虛擬劃分.
  5. 比方說為了容災,將Service微服務分別部署在了杭州機房和廣州機房,
  6. 這時就可以給杭州機房的Service微服務起一個集群名稱(HZ),
  7. 給廣州機房的Service微服務起一個集群名稱(GZ),還可以盡量讓同一個機房的微服務互相呼叫,以提升性能.
  8. 最后是Instance,就是微服務的實體.
2.4.6.1 三種法案加載配置
  1. DataID方案

    1. 指定spring.profile.active和組態檔的DataID來使不同環境下讀取不同的配置

    2. 默認空間+默認分組+新建dev和test兩個DataID

      • 新建dev配置DataID----------nacos-config-client-dev.yaml
      • 新建test配置DataID----------nacos-config-client-test.yaml
    3. 通過spring.profile.active屬性就能進行多環境下檔案的讀取

      spring:
        profiles:
          active: dev # 表示開發環境
          #active: test   #表示測驗環境
      
  2. Group方案

    1. 通過Group實作環境區分

      • 新建Group,在新建組態檔時,將DEFAULT_GROUP改為你要建的分組即可(例如DEV_GROUP)
    2. 在nacos圖形界面控制臺上面新建組態檔DataID

      • 新建dev配置DataID----------nacos-config-client-dev.yaml
    3. 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 # 表示開發環境
        
  3. Namespace方案

    1. 新建dev/test的Namespace

    2. 回到服務管理-服務串列查看

    3. 按照域名配置填寫

    4. 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 說明
  1. 默認Nacos使用嵌入式資料庫實作資料存盤.所以,如果啟動多個默認配置下的Nacos節點,資料存盤是存在一致性問題的.為了解決這個問題,Nacos采用了集中式存盤的方式來支持集群化部署,目前只支持MySQL的存盤.

  2. Nacos支持三種部署模式

    1. 單機模式 - 用于測驗和單機使用.

      • 在0.7版本之前,在單機模式時nacos使用嵌入式資料庫實作資料的存盤,不方便觀察資料存盤的基本情況.

      • 0.7版本之后增加了mysql資料源能力,具體的操作步驟:

        1. 安裝資料庫,版本要求: 5.6.5+

        2. 初始化mysql資料庫,資料庫初始化檔案:nacos-mysql.sql

        3. 修改conf/application.properties檔案,增加支持mysql資料源配置(目前只支持mysql),添加mysql資料源的url,用戶名和密碼.

          images\image-.png)]

     4. 再以單機模式啟動nacos,nacos所有寫嵌入式資料庫的資料都寫到了mysql.
    
    1. 集群模式 - 用于生產環境,確保高可用.

    2. 多集群模式 - 用于多資料中心場景.

2.5.2 Nacos持久化配置解釋
  1. Nacos默認自帶的是嵌入式資料庫derby

  2. derby到mysql切換配置步驟

    1. nacos-server-1.1.4\nacos\conf目錄下找到SQL腳本----nacos-mysql.sql-----并執行腳本

    2. 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
      
      
  3. 啟動Nacos,可以看到是個全新的空記錄界面,以前是記錄進derby

  4. MYSQL密碼:u3w)D9Q&wb%w

3. SpringCloud Alibaba Sentinel

SentinelHystrix
1.單獨一個組件,可以獨立出來1.需要我們程式員自己手工搭建監控平臺
2.直接界面化的細粒度統一配置2.沒有一套web界面可以給我們進行更加細粒度化的配置流控,速率控制,服務熔斷,服務降級…
  1. 約定 > 配置 > 編碼
  2. 都可以寫在代碼里面,但是我們本次還是大規模學習使用配置和注解的方式,盡量少寫代碼

3.1 安裝Sentinel控制臺

3.1.1 組成

Sentinel分為兩個部分:

  1. 核心庫(Java客戶端)不依賴任何框架/庫,能夠運行于所有Java運行時環境,同時對Dubbo/SpringCloud等框架也有較好的支持.
  2. 控制臺(Dashboard)基于SpringBoot開發,打包后可以直接運行,不需要額外的Tomcat等應用容器.
3.1.2 安裝步驟
  1. 下載 https://github.com/alibaba/Sentinel/releases/download/1.7.0/sentinel-dashboard-1.7.0.jar

  2. 運行命令

    1. 前提
      • java 8 環境OK
      • 8080埠不能被占用
    2. 命令 java -jar sentinel-dashboard-1.7.0.jar
  3. 訪問sentinel管理界面 localhost:8080

  4. 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 基本介紹

解釋說明:

  1. 資源名: 唯一名稱,默認請求路徑
  2. 針對來源: Sentinel可以針對呼叫者進行限流,填寫微服務名,默認default(不區分來源)
  3. 閾值型別/單機閾值:
    • QPS(每秒鐘的請求數量): 當呼叫該api的QPS達到閾值的時候,進行限流.
    • 執行緒數: 當呼叫該api的執行緒數達到閾值的時候,進行限流
  4. 是否集群模式: 不需要集群
  5. 流控模式:
    • 直接: api達到限流條件時,直接限流
    • 關聯: 當關聯的資源達到閾值時,就限流自己
    • 鏈路: 只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流) [api級別的針對來源]
  6. 流控效果:
    • 快速失敗: 直接失敗,拋例外
    • Warm Up: 根據codeFactor(冷加載因子,默認3)的值,從閾值/codeFactor,經過預熱時長,才達到設定的QPS閾值
    • 排隊等待: 勻速排隊,讓請求以勻速的速度通過,閾值型別必須設定為QPS,否則無效
3.2.2 流控模式
  1. 直接(默認)

    表示1秒鐘內查詢一次就是OK,若超過一次,就直接-快速失敗,報默認錯誤

    images\image-20201224170933646.png)]

    結果: Blocked by Sentinel (flow limiting)

    QPS和執行緒區別:

    1. QPS把敵人抵御在國門之外,超過閾值,就不讓進來
    2. 執行緒是放進來關門打狗,超過閾值,你進來,但是你在那給我等著.
  2. 關聯

    設定效果

    當關聯資源/testB的QPS閾值超過1時,就限流/testA的Rest訪問地址,當關聯資源達到閾值后限制配置好的資源名

    在這里插入圖片描述

  3. 鏈路

3.2.2 流控效果
  1. 直接–>快速失敗(默認的流控處理)

  2. Warm up(預熱)

    Warm Up(RuleConstant.CONTROL_BEHAVIOR_WAARM_UP)方式,即預熱/冷啟動方式.當系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮.通過"冷啟動",讓通過的流量緩慢增加,在一定的時間內逐漸增加到閾值上線,給冷系統一個預熱的時間,避免冷系統被壓垮.詳細檔案可以參考流量控制 - Warm Up 檔案

    • 說明: 公式: 閾值除以coldFactor(默認值為3),經過預熱時長后才會達到閾值

    • 官網

      1. 默認coldFactor為3,即請求QPS從threshold/3開始,經預熱時長逐漸升至設定的QPS閾值.
      2. 限流冷啟動
    • Warm Up配置

      1. 默認coldFactor為3,即請求QPS從(threshold/3)開始,經多少預熱時長才逐漸升至設定的QPS閾值.

      2. 案例,閾值為10 + 預熱時長設定為5秒

        系統初始化的閾值為10/3約等于3,即閾值剛開始為3;然后過了5秒后閾值才慢慢升高恢復到10

      在這里插入圖片描述

  3. 排隊等待

    勻速排隊,讓請求以均勻的速度通過,閾值型別必須設定成QPS,否則無效.

    設定含義: /testA每秒請求一次,超過的話就排隊等待,等待的超時時間為20000毫秒.

    在這里插入圖片描述

3.3 Sentinel降級

3.3.1 基本介紹

Sentinel熔斷降級會在呼叫鏈路中某個資源出現不穩定狀態時(例如呼叫超時或者例外比例升高),對這個資源的呼叫進行限制,讓請求快速失敗,避免影響到其他的資源而導致級聯錯誤.

擋子源被降級后,在接下來的降級時間視窗之內,對該資源的呼叫都自動熔斷(默認行為是拋出DegradeException).

Sentinel的斷路器是沒有半開狀態的

  1. 半開的狀態系統自動去檢測是否請求有例外,
  2. 沒有例外就關閉斷路器恢復使用,
  3. 有例外則基礎打開斷路器不可用.

在這里插入圖片描述

  1. RT(平均回應時間,秒級)

    平均回應時間 超出閾值在時間視窗期內通過的請求>=5, 兩個條件同時滿足后觸發降級

    視窗期過后關閉斷路器

    RT最大4900(更大的需要通過-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

  2. 例外比例(秒級)

    QPS >= 5 且例外比例(秒級統計)超過閾值時,觸發降級;時間視窗結束后,關閉降級

  3. 例外數(分鐘級)

    例外數(分鐘統計)超過閾值,觸發降級;時間視窗結束后,關閉降級

3.3.2 降級策略實戰
  1. RT

    1. 是什么

      平均回應時間(DEGRADE_GRADE_RT): 當1s內持續進入5個請求,對應時刻的平均回應時間(秒級)均超過閾值(count,以ms為單位),那么在接下來的時間視窗(DegradeRule中的timeWindow,以s為單位)之內,對這個方法的呼叫都會自動的熔斷(拋出DegradeException).注意Sentinel默認統計的RT上線是4900ms,超出此閾值的都會算作4900ms,若需要變更此上限可以通過啟動配置項-Dcsp.sentinel.statistic.max.rt=xxx來配置.

      在這里插入圖片描述

    2. 測驗

      1. 測驗代碼

        @GetMapping("/testD")
            public String getD(){
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("testD   測驗RT");
                return "------testD";
            }
        
      2. 測驗

        在這里插入圖片描述

        按照上述配置:

        • 永遠一秒鐘打進來10個執行緒(大于5個了)呼叫testD,我們希望200毫秒處理完本次的任務,
        • 如果超過200毫秒還沒處理完,在未來一秒鐘的視窗期內,斷路器打開(保險絲跳閘)微服務不可用,保險絲跳閘斷電了
        • 后續訪問量沒有那么大了,保險絲恢復正常,微服務可以訪問
  2. 例外比例

    1. 是什么

      例外比例(DEGRADE_GRADE_EXCEPTION_RATIO): 當資源的每秒請求量 >= 5,并且每秒例外總數占通過量的比值超過閾值(DegradeRule中的count)之后,資源進入降級狀態,即在接下來的時間視窗(DegradeRule中的timeWindow,以s為單位)之內,對這個方法的呼叫都會自動地回傳.例外比率的閾值范圍[0.0,1.0],代表0% - 100%.

      20201225140811335

    2. 配置

    20201225143049015

    @GetMapping("/testE")
        public String getE(){
            log.info("testE   測驗例外比例");
            int age = 10/0;
            return "------testE";
        }
    
    
    1. 結論

      按照上述配置,

      單獨訪問一次,必然來一次報錯一次(int age = 10/0),調一次錯一次;

      開啟jmeter后,直接高并發發送請求,多次呼叫達到我們的配置條件了.

      斷路器開啟(保險絲跳閘),微服務不可用了,不再報錯error而是服務降級.

  3. 例外數

    1. 是什么

      例外數(DEGRADE_GRADE_EXCEPTION_COUNT): 當資源近1分鐘的例外數目超過閾值之后會進行熔斷.注意由于統計時間視窗是分鐘級別的,若timeWindow小于60s,則結束熔斷狀態后仍可能再進入熔斷狀態.

      時間視窗一定要大于等于60秒

      在這里插入圖片描述

    2. 代碼配置

      在這里插入圖片描述

      @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 回傳的值(系統默認和自定義)
  1. 兜底方法分為系統默認用戶自定義兩種
  2. 之前的case,限流出問題之后,都是用sentinel系統默認的提示: Blocked by Sentinel (flow limiting)
  3. 現在我們可以自定義一個 從@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";
    }

在這里插入圖片描述

  1. 第一種

    1. @SentinelResource(value = “getHotKey”, blockHandler = “deal_getHotKey”)
    2. 方法getHotKey里面第一個引數只要超過每秒1次,馬上降級處理
    3. 用了我們自定義的方法.結果:------deal_getHotKey
  2. 第二種

    1. @SentinelResource(value = “getHotKey”)

    2. 例外打到了前臺界面,對用戶不友好,結果:
      20201225150730068

  3. 所以,如果我們用熱點key限流一定要寫兜底的方法

3.4.4 引數例外項

上述案例演示了第一個引數p1,當QPS超過1秒1次點擊后馬上被限流

  1. 特例情況

    1. 普通 超過1秒后,達到閾值1后馬上被限流
    2. 我們期望P1引數當他是某個特殊值時,它的限流值和平時不一樣
    3. 特例 例如當p1的值等于5時,它的閾值可以達到200
  2. 配置

images\image-.png)]

  1. 結論: 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 按資源名稱限流+后續處理
  1. 按照@SentinelResource資源名稱配置流控規則
  2. 因為我們寫了兜底的方法,所以按照我們的方法執行
  3. 產生的問題:關閉8401服務器,系統流控規則消失
3.6.2 按URL地址限流+后續處理
  1. 按照URL地址進行配置流控規則
  2. 我們沒有寫兜底的方法,所以就用系統默認自帶的
  3. 產生的問題:關閉8401服務器,系統流控規則消失
3.6.3 上面兜底方案面臨的問題
  1. 系統默認的,沒有體現我們自己的業務要求.
  2. 依照現有調價,我們自定義處理方法和業務代碼耦合在一起,不直觀.
  3. 每個業務方法都添加一個兜底的方法,代碼膨脹加劇.
  4. 全域統一的處理方法沒有體現.
3.6.4 客戶自定義限流處理邏輯
  1. 創建CustomerBlockHandler類用于自定義限流處理邏輯

  2. 自定義限流處理類CustomerBlockHandler

    package 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");
        }
    }
    
    
  3. 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","我成功了");
        }
    }
    
    
  4. 啟動微服務后先呼叫一次

  5. Sentinel控制臺配置

20201225163826116

  1. 測驗后我們自定義的出來了

3.7 服務熔斷功能

Sentinel整合ribbon+OpenFeign+fallback

3.7.1 服務熔斷框架比較
SentinelHystrixresilience4j
隔離策略信號量隔離(并發執行緒數限流)執行緒池隔離/信號量隔離信號量隔離
熔斷降級策略基于回應時間、一例外比率、例外數基于例外比率基于例外比率、回應時間
實時統計實作滑動視窗(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 步驟
  1. pom檔案

    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    
  2. 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
    
  3. 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 --> 1:N --> N:N

  2. 分布式之后:

    單體應用被拆分成微服務應用,原來的三個模塊被拆分成三個獨立的應用,分別使用三個獨立的資料源,

    業務操作需要三個微服務來完成.此時每個服務內部的資料一致性由本地事務來保證,但是全域的資料一致性問題沒有辦法保證

    images\image-.png)]

    images\image-.png)]

  3. 一次業務操作需要跨多個資料源或需要跨多個系統進行遠程呼叫,就會產生分布式事務問題

4.2 Seata簡介

4.2.1 是什么

Seata是一款開源的分布式事務解決方案,致力于在微服務架構下提供高性能和簡單應用的分布式事務服務

http://seata.io/zh-cn

4.2.2 能干嘛
  1. 一個典型的分布式事務程序

    1. 分布式事務處理程序的一ID+三組件模型

      • Transaction ID XID 全域唯一的事務ID

      • 3組件概念

        • Transaction Coordinator(TC) - 事務協調者

          事務協調器,維護全域事務的運行狀態,負責協調并驅動全域事務的提交或回滾;

        • Transaction Manager? - 事務管理器

          控制全域事務的邊界,負責開啟一個全域事務,并最終發起全域提交或全域回滾的協議;

        • Resource Manager(RM) - 資源管理器

          控制分支事務,負責分支注冊、狀態匯報,并接收事務協調器的指令,驅動分支(本地)事務的提交和回滾.

    2. 處理程序

      1. TM向TC申請開啟一個全域事務,全域事務創建成功并且生成一個全域唯一的XID;

      2. XID在微服務呼叫鏈路的背景關系中傳播;

      3. RM向TC注冊分支事務,將其納入XID對應全域事務的管轄;

      4. TM向TC發起針對XID的全域提交或回滾決議;

      5. TC調度XID下管轄的全部分支事務完成提交或回滾請求.

      images\image-20201228142009465.png)]

4.2.3 怎么用
  1. 本地 @Transactional
  2. 全域 @GlobalTransactional

4.3 Seata-Server安裝

  1. 下載 http://seata.io/zh-cn

  2. 解壓縮

  3. 修改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
      }
    }
    
  4. 創建seata資料庫,將conf/db_store.sql粘腳本建表

  5. 修改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 創建業務資料庫
  1. 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
    
    
  2. 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
    
  3. 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 訂單微服務準備
  1. 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>
    
    
  2. 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  #物體類包
    
    
  3. 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();
        }
    }
    
    
  4. 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 {
    }
    
    
  5. 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 執行流程
  1. TM開啟分布式事務(TM向TC注冊全域事務記錄);
  2. 按業務場景,編排資料庫、服務等事務內資源(RM向TC會報資源準備狀態);
  3. TM結束分布式事務,事務一階段結束(TM通知TC提交/回滾分布式事務);
  4. TC匯總事務資訊,決定分布式事務是提交還是回滾;
  5. TC通知所有RM提交/回滾資源,事務二階段結束.
4.7.2 AT模式如何做到對業務的無侵入

Seata是一款開源的分布式事務解決方案,致力于提供高性能和簡易使用的分布式事務服務.Seata將為用戶提供了AT、TCC、SAGA和XA事務模式,為用戶打造一站式的分布式解決方案.

  1. AT模式

    1. 前提
      • 基于本地ACID事務的關系型資料庫
      • Java應用,通過JDBC訪問資料庫
    2. 整體機制(兩階段提交協議的演變)
      • 一階段: 業務資料和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源.
      • 二階段:
        • 提交異步化,非常快速地完成.
        • 回滾通過一階段的回滾日志進行反向補償.
  2. 一階段加載

    在一階段,Seata會攔截"業務SQL",

    1 . 決議SQL語意,找到"業務SQL"要更新的業務資料,在業務資料被更新前,將其保存成"before image",

    2 . 執行"業務SQL"更新業務資料,在業務資料更新之后,

    3 . 將其保存成"after image",最后生成行鎖.

    以上操作全部在一個資料庫事務內完成,這樣保證了一階段操作的原子性.

    images\image-20201230153317262.png)]

  3. 二階段提交

    二階段如果順利提交的話,

    因為"業務SQL"在一階段已經提交至資料庫,所以Seata只需將一階段保存的快照資料和行鎖刪掉,完成資料清理即可.

    images\image-20201230153605200.png)]

  4. 二階段回滾

    二階段如果是回滾的話,Seata就需要回滾一階段已經執行的"業務SQL",還原業務資料.

    回滾方式便是用"before image" 還原業務資料;但在還原前要首先要校驗臟寫,對比"資料庫當前業務資料"和"after image",

    如果兩份資料完全一致就說明沒有臟寫,可以還原業務資料,如果不一致就說明有臟寫,出現臟寫就需要轉人工處理.

    images\image-20201230154227673.png)]

  5. 補充

mages\image-20201230160139619.png)]

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/243297.html

標籤:其他

上一篇:總結2020:5個月出版兩本書,日更公眾號是一種怎樣的體驗?

下一篇:不容錯過的Java高級面試題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more