第一節 項目簡介
Spring Cloud Alibaba 致力於提供微服務開發的一站式解決方案。此項目包含開發分布式應用微服務的必需組件,方便開發者通過 Spring Cloud 編程模型輕松使用這些組件來開發分布式應用服務。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以將 Spring Cloud 應用接入阿里微服務解決方案,通過阿里中間件來迅速搭建分布式應用系統。
第二章 依賴管理
一、版本管理規范

二、依賴管理
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
三、父項目的創建
3.1 spring-cloud-alibaba-examples 項目的創建
3.1.1 使用 IDEA 創建一個 Maven 項目


3.1.2 Spring Boot 版本的控制
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
3.1.3 Spring Cloud 版本的控制
<properties> <spring-cloud.version>Hoxton.SR3</spring-cloud.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
3.1.4 Spring Cloud Alibaba 版本的控制
<properties> ... <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version> ... </properties>
<dependencyManagement> <dependencies> ... <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${com-alibaba-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> .... </dependencies> </dependencyManagement>
3.1.4 設置為 pom 的版本方式
<packaging>pom</packaging>
3.1.5 完整的 pom.xml
<?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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-alibaba-examples</artifactId> <version>1.0</version> <properties> <spring-cloud.version>Hoxton.SR3</spring-cloud.version> <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${com-alibaba-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
3.2 項目的打包
3.2.1 刪除項目里面多余的文件夾

3.2.2 執行打包

3.2.3 觀察打包后的效果


第三章 Nacos


二、Nacos 的關鍵特性
三、Nacos 的核心概念
第三章 Nacos 注冊中心
一、Nacos Discovery 簡介
二、Nacos Server 安裝
2.1 Nacos Server 的下載



2.2 Nacos Server 目錄的說明

2.3 配置 Nacos Server
- 上面的 url 地址是我的服務器地址,如果你的 mysql 安裝在虛擬機或雲服務器上面,就
- db.user 用戶名
- db.password 密碼
2.4 Mysql 表的導入



2.5 Nacos Server 的啟動

三、框架的搭建

3.1 創建 nacos-examples 的父項目


- spring-boot-starter-web (spring-boot-web 開啟的最基礎的依賴)
- spring-cloud-alibaba-nacos-discovery(nacos 做服務發現的最基礎的依賴)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
<packaging>pom</packaging>
<?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>spring-cloud-alibaba-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>provider</module> <module>consumer</module> <module>config-client</module> </modules> <artifactId>nacos-examples</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
3.2 創建服務的提供者 provider



3.3 創建服務的消費者




四、使用 Nacos 做注冊中心
4.1 provider 項目的完善
4.1.1 添加一個 application.yml 配置文件



server:
port: 8080
spring:
application:
name: provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
- server.port provider 服務端口為 8001 ;
- spring.application.name 服務名稱為 provider-service;
- spring.cloud.nacos.server-addr ,指定 Nacos 注冊中心的地址;
4.1.2 添加一個啟動類


@SpringBootApplication // 標記為 SpringBoot 的應用
@EnableDiscoveryClient // 啟用服務發現的功能
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args) ;
}
}
4.1.3 添加一個 API 接口


@RestController public class EchoController { /** * echo message * @param message * @return reply content */ @GetMapping("/echo/{message}") public ResponseEntity<String> echo(@PathVariable("message") String message){ return ResponseEntity.ok(String.format("hello,%s",message)) ; } }
4.1.4 啟動 provider 測試


4.2 consumer 項目的完成
4.2.1 添加一個 application.yml 配置文件
我們給 consumer 添加一個配置文件


server:
port: 8090
spring:
application:
name: consumer-service
cloud:
nacos:
discovery:
server-addr: localhost:8848


@SpringBootApplication // 標記為 SpringBoot 的應用
@EnableDiscoveryClient // 開啟服務的發現功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args) ;
}
}
@Autowired private DiscoveryClient discoveryClient ;// 快速服務的發現 @GetMapping("/discovery/{serviceName}") public ResponseEntity<List<String>> discovery(@PathVariable("serviceName") String serviceName){ /** * 通過服務的 ID / 名稱得到服務的實例列表 */ List<ServiceInstance> instances = discoveryClient.getInstances(serviceName); if(instances==null || instances.isEmpty()){ return ResponseEntity.notFound().build() ; } List<String> services = new ArrayList<>(instances.size()); instances.forEach(instance->{ services.add(instance.getHost()+":"+instance.getPort()); }); return ResponseEntity.ok(services) ; }

http://localhost:8090/discovery/provider-service


/** * 在容器里面注入一個 RestTempalte 對象 * @return */ @Bean public RestTemplate restTemplate(){ return new RestTemplate() ; }
@Autowired 27private RestTemplate restTemplate ; @GetMapping("/rpcv1/{message}") public ResponseEntity<String> rpcV1(@PathVariable("message") String message){ ResponseEntity<String> responseEntity = restTemplate.getForEntity( "http://localhost:8081/echo/{message}", String.class, message ); if(responseEntity.getStatusCode()==HttpStatus.OK){ return ResponseEntity.ok(String.format("遠程調用成功,結果 為%s",responseEntity.getBody())) ; } return ResponseEntity.badRequest().body("遠程調用失敗") ; } @GetMapping("/rpcv2/{message}") public ResponseEntity<String> rpcV2(@PathVariable("message") String message){ List<ServiceInstance> instances = discoveryClient.getInstances("nacos-provider"); if(instances==null || instances.isEmpty()){ return ResponseEntity.badRequest().body("當前服務沒有服務的提供者") ; } ServiceInstance serviceInstance = instances.get(0); String instance = serviceInstance.getHost()+":"+serviceInstance.getPort() ; ResponseEntity<String> responseEntity = restTemplate.getForEntity( String.format("http://%s/echo/{message}",instance), String.class, message ); if(responseEntity.getStatusCode()==HttpStatus.OK){ return ResponseEntity.ok(String.format("遠程調用成功,結果 為%s",responseEntity.getBody())) ; } return ResponseEntity.badRequest().body("遠程調用失敗") ; } @GetMapping("/rpcv3/{message}") public ResponseEntity<String> rpcV3(@PathVariable("message") String message){ List<ServiceInstance> instances = 28discoveryClient.getInstances("nacos-provider"); if(instances==null || instances.isEmpty()){ return ResponseEntity.badRequest().body("當前服務沒有服務的提供者") ; } ServiceInstance serviceInstance = loadbalance(instances); System.out.println(serviceInstance); String instance = serviceInstance.getHost()+":"+serviceInstance.getPort() ; ResponseEntity<String> responseEntity = restTemplate.getForEntity( String.format("http://%s/echo/{message}",instance), String.class, message ); if(responseEntity.getStatusCode()==HttpStatus.OK){ return ResponseEntity.ok(String.format("遠程調用成功,結果 為%s",responseEntity.getBody())) ; } return ResponseEntity.badRequest().body("遠程調用失敗") ; }
/** * 從一個服務的實例列表里面選擇一個服務的實例 * @param instances * 實例列表 * @return * 具體的實例 */ private ServiceInstance loadbalance(List<ServiceInstance> instances) { Random random = new Random(System.currentTimeMillis()); return instances.get(random.nextInt(instances.size())) ; }

http://localhost:8090/rpc/world


@RestController public class EchoController { @Value("${server.port}") private Integer port ; /** * echo message * @param message * @return reply content */ @GetMapping("/echo/{message}") public ResponseEntity<String> echo(@PathVariable("message") String message){ return ResponseEntity.ok(String.format("hello,%s,我是服務提供 者:%s",message,port)) ; } }





java -jar xxx.jar --server.port=8081



5.3 測試負載均衡
@Autowired private LoadBalancerClient loadBalancerClient ; @GetMapping("/choose/{serviceName}") public ResponseEntity<String> choose(@PathVariable("serviceName") String serviceName){ ServiceInstance instance = loadBalancerClient.choose(serviceName); System.out.println(String.format("本次選擇的實例 為:%s:%s",instance.getHost(),instance.getPort())); return ResponseEntity.ok(instance.getHost()+":"+instance.getPort()) ; }
http://localhost:8090/choose/provider-service

刷新頁面:

5.4 更簡單的遠程調用測試


@GetMapping("/rpc/ribbon/{message}") public ResponseEntity<String> rpcByRibbon(@PathVariable("message") String message){ ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://provider-service/echo/" + message, String.class); if(responseEntity.getStatusCode()==HttpStatus.OK){ return ResponseEntity.ok("調用成功,provider-service 相應給我們的數 據為:"+responseEntity.getBody()) ; } return ResponseEntity.badRequest().body("調用失敗,provider-service 的 相應碼為:"+responseEntity.getStatusCode()) ; }


六、Nacos Discovery 對外暴露的 Endpoint
6.1 給項目添加依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
6.2 修改配置文件
management: 38endpoints: web: exposure: include: "*"
6.3 查詢效果

七、Nacos Discovery Starter 更多的配置項

第三章 Nacos 配置中心

2.1 創建 config-client 項目




2.2 添加依賴
spring-cloud-alibaba-nacos-config

2.3 完整的 pom.xml 文件
<?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>nacos-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>config-client</artifactId> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-config</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
三、在 nacos-server 里面添加配置




四、獲取配置信息
config-client項目的pom.xml
<?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>nacos-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>config-client</artifactId> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-config</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
4.1 添加一個配置文件


server: port: 8070 spring: cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: properties application: name: config-client
4.2 新建一個啟動類

@SpringBootApplication @EnableDiscoveryClient @RefreshScope @RestController public class ConfigClientApplication { @Value("${user.name}") private String userName ; @Value("${user.age}") private Integer userAge ; public static void main(String[] args) { SpringApplication.run(ConfigClientApplication.class,args) ; } /** * 獲取配置文件里面用戶的信息 * @return */ @GetMapping("/user/info") public ResponseEntity<String> getUser(){ return ResponseEntity.ok( String.format("從配置中心獲取的信息為:user:%s, age:%s",userName,userAge)) ; } }
4.3 啟動測試

五、獲取配置規則
六、配置划分實戰


8defab18-df88-49e5-b13e-526f89da87ad
6.2 在 Nacos 新建配置



6.3 獲取配置文件
server:
port: 8070
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
config: # 指定配置中心的地址和配置中心使用的數據格式
server-addr: localhost:8848
file-extension: yml #properties
group: GROUP_A # 獲取 GROUP_A 里面的配置
namespace: 8defab18-df88-49e5-b13e-526f89da87ad # 命名空間,寫 id的值
# prefix: ${spring.application.name} # 前綴,默認為應用的名稱,不需要
修改
application:
name: config-client
profiles:
active: dev # 使用的 dev 環境的配置

6.4 重啟 config-client 測試

七、配置回滾
7.1 動態刷新
7.2 修改配置文件



7.3 歷史版本的查詢
7.4 回滾




http://localhost:8070/user/info

八、獲取多個配置
8.1 修改 config-client 里面的配置文件
server: port: 8070 spring: cloud: nacos: discovery: server-addr: localhost:8848 config: extension-configs: # 多個配置 - dataId: test-a.yml group: DEFAULT_GROUP refresh: true - dataId: test-b.yml group: DEFAULT_GROUP refresh: false # 指定配置中心的地址和配置中心使用的數據格式 #server-addr: localhost:8848 #file-extension: yml #properties #group: GROUP_A # 獲取 GROUP_A 里面的配置 #namespace: 8defab18-df88-49e5-b13e-526f89da87ad # 命名空間,寫 id 的值 ##prefix: ${spring.application.name} # 前綴,默認為應用的名稱,不需要修改 application: name: config-client profiles: active: dev # 使用的 dev 環境的配置
- spring.cloud.nacos.config.extension-configs[n].dataId,指定多個配置的 dataId,必須包含
- spring.cloud.nacos.config.extension-configs[n].group,指定分組;
- spring.cloud.nacos.config.extension-configs[n].refresh,是否支持刷新。
8.2 在 Nacos 里面完成這 2 個配置文件的創建



8.3 獲取配置信息
九、Spring Cloud Alibaba Nacos Config 常用的配置.
第四章 Spring Cloud Alibaba Sentinel
Sentinel 具有以下特征:
- 豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,
- 完備的實時監控: Sentinel 同時提供實時的監控功能。您可以在控制台中看到接入應
- 廣泛的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與
- 完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展點。您可以通過實現擴
二、Sentinel 控制台安裝
2.1 下載 Sentinel


2.2 啟動 sentinel-dashboard



- -Dsentinel.dashboard.auth.username=sentinel 用於指定控制台的登錄用戶名為 sentinel;
- -Dsentinel.dashboard.auth.password=123456 用於指定控制台的登錄密碼為 123456,如果省略這兩個參數,默認用戶和密碼均為 sentinel;
- -Dserver.servlet.session.timeout=7200 用於指定 Spring Boot 服務端 session 的過期時間,如 7200 表示 7200 秒;60m 表示 60 分鍾,默認為 30 分鍾;
- -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口
三、搭建客戶端


3.1 使用 IDEA 創建子模塊



3.2 添加依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies>
- spring-cloud-starter-alibaba-sentinel 這是 spring cloud 和 sentinel 集成的項目
- spring-boot-starter-web 開啟 web 最基礎的依賴
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
3.3 完整的 pom.xml
<?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>spring-cloud-alibaba-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sentinel-example-client</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.4 添加一個配置文件

server:
port: 8085
spring:
application:
name: sentinel-client
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719
- spring.cloud.sentinel.transport.dashboard 指定了 sentinel 控制台的 ip 和端口地址;
- spring.cloud.sentinel.transport.port 代表 sentinel 客戶端和控制台通信的端口,默認為
3.5 添加一個啟動類

@SpringBootApplication public class SentinelClientApplication { public static void main(String[] args) { SpringApplication.run(SentinelClientApplication.class ,args) ; } }
3.6 添加一個 Controller


@RestController public class TestController { @GetMapping("/hello") public ResponseEntity<String> hello(){ return ResponseEntity.ok("hello,sentinel") ; } }
3.7 啟動項目





四、流控規則


- 資 源 名 : 標 識 資 源 的 唯 一 名 稱 , 默 認 為 請 求 路 徑 , 也 可 以 在 客 戶 端 中 使 用
- 針 對 來 源 : Sentinel 可 以 針 對 服 務 調 用 者 進 行 限 流 , 填 寫 微 服 務 名 稱 即
- 閾值類型、單機閾值:
-
- QPS(Queries-per-second,每秒鍾的請求數量):當調用該 api 的 QPS 達到閾值的時候,進行限流;
-
- 線程數:當調用該 api 的線程數達到閾值的時候,進行限流。
-
- 是否集群:默認不集群;
- 流控模式:
-
- 直接:當 api 調用達到限流條件的時,直接限流;
-
- 關聯:當關聯的資源請求達到閾值的時候,限流自己;
-
- 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,則進行限流)。
- 流控效果:
-
- 快速失敗:直接失敗;
-
- Warm Up:根據 codeFactor(冷加載因子,默認值為 3)的值,從閾值/codeFactor,經過預熱時長,才達到設置的 QPS 閾值;
-
- 排隊等待:勻速排隊,讓請求勻速通過,閾值類型必須設置為 QPS,否則無效。
4.1 QPS 直接失敗



4.2 線程數直接失敗
4.2.1 添加接口
/** * 線程直接失敗 * @return * @throws InterruptedException */ @GetMapping("/thread") public ResponseEntity<String> threadMode() throws InterruptedException { TimeUnit.SECONDS.sleep(1); return ResponseEntity.ok("hello,sentinel!") ; }
TimeUnit.SECONDS.sleep(1);
http://localhost:8085/thread

4.2.2 新增流控規則

4.2.3 測試該規則
http://localhost:8085/thread

4.3 關聯
4.3.1 在 TestController 里面添加 api 接口
@GetMapping("/test1") public ResponseEntity<String> test1(){ return ResponseEntity.ok("hello,test1") ; } @GetMapping("/test2") public ResponseEntity<String> test2(){ return ResponseEntity.ok("hello,test2") ; }

4.3.2 添加規則

4.3.3 測試規則

4.4 鏈路


4.4.2 添加接口
@Autowired private TestService testService ; @GetMapping("/link1") public ResponseEntity<String> link1(){ return ResponseEntity.ok( String.format("link1,調用 test,結果 為%s",testService.hello())) ; } @GetMapping("/link2") public ResponseEntity<String> lin2(){ return ResponseEntity.ok( String.format("link2,調用 test,結果為%s",testService.hello())) ; }
4.4.3 聲明資源


4.4.4 添加鏈路規則
點擊簇點鏈路:



4.4.5 測試該規則


4.5 預熱 Warm Up

根據codeFactor
(冷加載因子,默認為3)的值,即請求 QPS 從 threshold / 3
開始,經預熱時長逐漸升至設定的 QPS 閾值


4.6 排隊等待
private static Logger logger = LoggerFactory.getLogger(TestController.class) ; @GetMapping("/queue") public ResponseEntity<String> queue(){ logger.info("開始處理請求"); return ResponseEntity.ok("ok") ; }


五、降級規則

- RT,平均響應時間 (DEGRADE_GRADE_RT):當 1s 內持續進入 5 個請求,對應時刻的
- 異常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):當資源的每秒請求量 >= 5,並且每秒
- 異常數 (DEGRADE_GRADE_EXCEPTION_COUNT):當資源近 1 分鍾的異常數目超過閾值
5.1 RT
5.1.1 添加測試接口
@GetMapping("/rt") public ResponseEntity<String> rt() throws InterruptedException { TimeUnit.SECONDS.sleep(1); return ResponseEntity.ok("ok") ; }
5.1.2 添加降級的規則
5.1.3 測試


5.2 異常比例
5.2.1 添加接口
@GetMapping("/exception") public ResponseEntity<String> exception() throws InterruptedException { throw new RuntimeException("就是不想成功!") ; }
5.2.2 添加降級規則
5.2.3 測試

開始測試


5.3 異常數
5.3.1 添加規則
5.3.2 測試該規則
六、熱點規則
6.1 添加一個接口
@GetMapping("/buy") @SentinelResource("buy") public ResponseEntity<String> buy(String prodName,Integer prodCount){ return ResponseEntity.ok("買" + prodCount + "份" + prodName ); }
6.2 添加熱點的規則

6.3 測試效果


七、系統規則

閾值類型包含以下五種:
八、授權規則

第四章 Spring Cloud Alibaba Sentinel
一、@SentinelResource 簡介

2.1 搭建 sentinel-example
<?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>spring-cloud-alibaba-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <artifactId>sentinel-examples</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
2.1.3 修改項目的打包方式
<packaging>pom</packaging>
2.2 搭建 sentinel-provider
2.2.1 使用 IDEA 創建一個 Maven 項目

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
2.2.3 完整的 pom.xml 文件
<?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>sentinel-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sentinel-provider</artifactId> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>


2.3.2 修改 pom.xml 文件
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
2.3.3 完整的 pom.xml 文件
<?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>sentinel-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sentinel-consumer</artifactId> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
三、完善 sentinel-provider

@RestController public class GoodsController { @GetMapping("/goods/buy/{name}/{count}") public ResponseEntity<String> buy( @PathVariable("name") String name, @PathVariable("count") Integer count) { return ResponseEntity.ok(String.format("購買%d 份%s", count, name)); } }
3.2 添加配置文件
server:
port: 8081
spring:
application:
name: sentinel-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
3.3 添加一個啟動類
@SpringBootApplication @EnableDiscoveryClient public class SentinelProviderApplication { public static void main(String[] args) { SpringApplication.run(SentinelProviderApplication.class ,args) ; } }
3.4 啟動測試


四、完善 sentinel-consumer

@RestController public class BuyController { @Autowired private RestTemplate restTemplate; @GetMapping("buy/{name}/{count}") @SentinelResource(value = "buy", fallback = "buyFallback", blockHandler = "buyBlock") public ResponseEntity<String> buy(@PathVariable String name, @PathVariable Integer count) { if (count >= 20) { throw new IllegalArgumentException("購買數量過多"); } if ("miband".equalsIgnoreCase(name)) { throw new NullPointerException("已售罄"); } Map<String, Object> params = new HashMap<>(2); params.put("name", name); params.put("count", count); return ResponseEntity.ok( this.restTemplate.getForEntity("http://sentinel-provider/goods/buy/{name}/{ count}", String.class, params).getBody()); } // 異常回退 public ResponseEntity<String> buyFallback(@PathVariable String name, @PathVariable Integer count, Throwable throwable) { return ResponseEntity.ok( String.format("【進入 fallback 方法】購買%d 份%s 失敗,%s", count, name, throwable.getMessage())); } // sentinel 回退 public ResponseEntity<String> buyBlock(@PathVariable String name, @PathVariable Integer count, BlockException e) { return ResponseEntity.ok(String.format("【進入 blockHandler 方法】購買%d 份%s 失敗,當前購買人數過多,請稍后再試", count, name)); } }


4.4.2 添加流控的規則
當訪問該資源,QPS 超過 2 時,拋出異常。


代碼如下:

代碼如下:
第五章 Spring Cloud Alibaba Seata
一、Seata 簡介
二、Seata-Server 的安裝
2.1 下載 Seata

2.2 Seata-Server 目錄分析

2.3 Seata 啟動

三、框架的搭建

3.1 搭建 seata-examples 項目
3.1.1 使用 IDEA 創建 Maven 項目



3.1.2 添加依賴
<dependencies> <!-- 服務注冊--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <!--web 項目的基礎依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency> </dependencies>
3.1.3 完整的 pom.xml
<?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>spring-cloud-alibaba-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-examples</artifactId> <dependencies> <!-- 服務注冊--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> <!-- seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <!-- web 項目的基礎依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>

3.2 搭建 account-service 項目



3.2.2 添加依賴
<dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<?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>seata-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>account-service</artifactId> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.3 搭建 business-service 項目
3.3.1 使用 IDEA 創建一個 Maven 項目
3.3.2 添加依賴
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<?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>seata-examples</artifactId><groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>account-service</artifactId> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

3.3 搭建 business-service 項目
3.3.1 使用 IDEA 創建一個 Maven 項目



3.3.2 添加依賴
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<?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>seata-examples</artifactId><groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>account-service</artifactId> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

3.4 搭建 order-service 項目
3.4.1 使用 IDEA 創建一個 Maven 項目


3.4.2 添加依賴
<dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<?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>seata-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>order-service</artifactId> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

3.5 搭建 storage-service 項目
3.5.1 使用 IDEA 創建一個 Maven 項目


3.5.2 添加依賴
<dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency></dependencies> 為了以后我們打包發布我們的項目,在此我們添加 boot-maven 的打包插件: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
3.5.3 完整的 pom.xml 文件如下
<?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>seata-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>order-service</artifactId> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>2.3</version> </dependency> <!--MySQL 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.6 完整的項目的

4.1 數據庫表導入


成功后,如圖所示:



4.3.2 實現該接口

@Service public class StorageServiceImpl implements StorageService { private static Logger logger = LoggerFactory.getLogger(StorageServiceImpl.class); @Autowired private StorageTblMapper storageTblMapper; @Override public void deduct(String commodityCode, int count) { logger.info("開始扣減庫存,商品編碼:{},數量:{}", commodityCode, count); StorageTbl storageTbl = storageTblMapper.selectOne( new LambdaQueryWrapper<StorageTbl>() .eq(StorageTbl::getCommodityCode, commodityCode)); int idleCount = storageTbl.getCount() - count; if (idleCount < 0) { throw new RuntimeException("庫存不足"); } storageTbl.setCount(idleCount); storageTblMapper.updateById(storageTbl); logger.info("庫存扣減成功,商品編碼:{},剩余數量:{}", commodityCode, idleCount); } }

@RestController public class StorageController { private static Logger logger = LoggerFactory.getLogger(StorageController.class) ; @Autowired private StorageService storageService ; /** * 扣減商品的庫存 * @param commodityCode 商品的編碼 * @param count 商品的數量 * @return */ @GetMapping("/deduct/{commodityCode}/{count}") public ResponseEntity<Void> deduct( @PathVariable("commodityCode") String commodityCode, @PathVariable("count") Integer count){ logger.info("Account Service ... xid: " + RootContext.getXID()); // 開始扣減庫存 storageService.deduct(commodityCode , count); return ResponseEntity.ok().build() ; } }
4.3.4 添加配置文件

server: port: 18084 spring: application: name: storage-service cloud: alibaba: seata: tx-service-group: storage-service nacos: discovery: server-addr: localhost:8848 datasource: name: storageDataSource type: com.alibaba.druid.pool.DruidDataSource username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver url:jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC druid: max-active: 20 min-idle: 2 initial-size: 2 seata: service: vgroup-mapping: account-service: default grouplist: default: 127.0.0.1:8091 disable-global-transaction: false enabled: true mybatis-plus: mapper-locations: classpath:/mapper/*.xml
4.3.5 添加啟動類

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.bjsxt.mapper")
public class StorageServiceApplication {
public static void main(String[] args) {
SpringApplication.run(StorageServiceApplication.class ,args) ;
}
}
4.4.2 實現該接口
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountTblMapper accountTblMapper; private static Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); @Override public void debit(String userId, int money) { logger.info("准備扣減用戶:{} 余額,扣減的數目為:{}", userId, money); AccountTbl accountTbl = accountTblMapper.selectOne( new LambdaQueryWrapper<AccountTbl>() .eq(AccountTbl::getUserId, userId)); int idleMoney = accountTbl.getMoney() - money; if (idleMoney < 0) { throw new RuntimeException("用戶余額不足"); } accountTbl.setMoney(idleMoney); accountTblMapper.updateById(accountTbl); logger.info("扣減用戶{}金額成功,剩余金額為{}", userId, money); } }
4.4.3 使用 Restful 暴露此接口
@RestController public class AccountController { @Autowired private AccountService accountService ; private static Logger logger = LoggerFactory.getLogger(AccountController.class) ; @GetMapping("/debit/{userId}/{money}") public ResponseEntity<Void> debit( @PathVariable("userId") String userId, @PathVariable("money") Integer money){ logger.info("Account Service ... xid: " + RootContext.getXID()); // 開始扣減余額 accountService.debit(userId , money); return ResponseEntity.ok().build() ; } }
4.4.4 添加配置文件
server: port: 18085 spring: application: name: account-service cloud: alibaba: seata: tx-service-group: account-service nacos: discovery: server-addr: localhost:8848 datasource: type: com.alibaba.druid.pool.DruidDataSource username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC druid: max-active: 20 min-idle: 2 initial-size: 2 seata: service: vgroup-mapping: account-service: default grouplist: default: 127.0.0.1:8091 disable-global-transaction: false enabled: true mybatis-plus: mapper-locations: classpath:/mapper/*.xml
4.4.5 添加啟動類
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.bjsxt.mapper")
public class AccoutServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AccoutServiceApplication.class ,args) ;
}
}
4.4.6 啟動項目測試
4.5.1 接口設計
public interface OrderService {
/**
* 創建一個訂單
* @param userId 用戶 id
* @param commodityCode 商品的編號
* @param orderCount 商品的數量
* @return OrderTbl
*/OrderTbl create(String userId, String commodityCode, int orderCount) ;
}
4.5.4 Ribbon 集成

@Configuration
public class HttpUtilConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate() ;
}
}
4.5.2 實現該接口

@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderTblMapper orderTblMapper; @Autowired private AccountService accountService; private static Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); @Override public OrderTbl create(String userId, String commodityCode, int orderCount) { logger.info("准備為{}創建一個訂單,商品編號為{},數量為{}", userId, commodityCode, orderCount); // 1 計算總金額 int orderMoney = calculate(commodityCode, orderCount); accountService.debit(userId, orderMoney); OrderTbl order = new OrderTbl(); order.setUserId(userId); order.setCommodityCode(commodityCode); order.setCount(orderCount);order.setMoney(orderMoney); orderTblMapper.insert(order); // INSERT INTO orders ... return order; } private int calculate(String commodityCode, int orderCount) { // 我們現在沒有商品的表,在此我們把商品的價格定死 int prodPrice = 0 ; if("HUAWEI_0001".equals(commodityCode)){ // 華為時 100 prodPrice = 100; }else if ("XIAOMI_002".equals(commodityCode)){ // 小米時 200 prodPrice = 200 ; }else { prodPrice = 1000 ; // 其他為 1000 } return orderCount * prodPrice ; } }
4.5.3 遠程調用 account-service 的實現


/** * 實現對賬號服務的遠程調用 */ @Service public class AccountService { private static Logger logger = LoggerFactory.getLogger(AccountService.class) ; /** * 1 ribbon 的方式 */ @Autowired private RestTemplate restTemplate ; /** * 2 feign 的方式 */ public void debit(String userId, int orderMoney) { ResponseEntity<Void> entity = restTemplate. getForEntity( "http://accout-service/debit/{userId}/{orderMoney}", Void.class, userId, orderMoney ); if(entity.getStatusCode()== HttpStatus.OK){ logger.info("扣減用戶{}金額成功,本次扣減的數目為{}",userId,orderMoney); return ; } logger.info("扣減用戶{}金額失敗",userId); throw new RuntimeException("扣減金額失敗") ; } }
4.5.5 使用 Restful 暴露此接口

@RestController public class OrderController { private static Logger logger = LoggerFactory.getLogger(OrderController.class) ; @Autowired private OrderService orderService ; /** * 創建訂單 * @param userId * 用戶 Id * @param commodityCode * 商品的編號* @param orderCount * 商品的數量 * @return */ @GetMapping("/create/{userId}/{commodityCode}/{orderCount}") public ResponseEntity<Void> create( @PathVariable("userId") String userId, @PathVariable("commodityCode") String commodityCode, @PathVariable("orderCount") int orderCount){ logger.info("Order Service ... xid: " + RootContext.getXID()); orderService.create(userId, commodityCode, orderCount) ; return ResponseEntity.ok().build() ; } }
4.5.6 添加配置文件

server: port: 18086 spring: application: name: order-service cloud: alibaba: seata: tx-service-group: order-service nacos: discovery: server-addr: localhost:8848 datasource: name: orderDataSource type: com.alibaba.druid.pool.DruidDataSource username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC druid: max-active: 20 min-idle: 2 initial-size: 2 seata: service: vgroup-mapping: order-service: default grouplist: default: 127.0.0.1:8091 disable-global-transaction: false enabled: true mybatis-plus: mapper-locations: classpath:/mapper/*.xml

@SpringBootApplication@EnableDiscoveryClient @MapperScan("com.bjsxt.mapper") public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class ,args) ; } }

4.6 business-service 代碼的完善
4.6.1 接口設計

public interface BusinessService { /** * 采購/下單的過程 * @param userId * 用戶的 Id * @param commodityCode * 商品的編碼 * @param orderCount * 商品的數量 */ void purchase(String userId, String commodityCode, int orderCount) ; }
4.6.2 實現該接口
@Service public class BusinessServiceImpl implements BusinessService { private static Logger logger = LoggerFactory.getLogger(BusinessServiceImpl.class) ; @Autowired private StorageService storageService; @Autowired private OrderService orderService; @Override public void purchase(String userId, String commodityCode, int orderCount) { logger.info("准備下單,用戶:{},商品:{},數量: {}",userId,commodityCode,orderCount); storageService.deduct(commodityCode, orderCount); orderService.create(userId, commodityCode, orderCount) ; logger.info("下單完成"); } }
4.6.3 遠程調用 storage-service 的實現

@Service public class StorageService { private static Logger logger = LoggerFactory.getLogger(StorageService.class) ; /** * 1 采用 Ribbon 的形式 */ @Autowired private RestTemplate restTemplate ; /** * 2 采用 Feign 的形式 */ public void deduct(String commodityCode, int orderCount) { ResponseEntity<Void> entity = restTemplate. getForEntity( "http://storage-service/debut/{commodityCode}/{orderCount}", Void.class, commodityCode, orderCount ); if (entity.getStatusCode()== HttpStatus.OK){ logger.info("扣減庫存成功,商品編號為{},本次扣減的數量為{}",commodityCode,orderCount); return; } throw new RuntimeException("扣減庫存失敗") ;} }
4.6.4 遠程調用 order-service 的實現

@Service public class OrderService { private static Logger logger = LoggerFactory.getLogger(StorageService.class) ; /** * 1 采用 Ribbon 的形式 */ @Autowired private RestTemplate restTemplate ; /** * 2 采用 Feign 的形式 */ public void create(String userId, String commodityCode, int orderCount) { ResponseEntity<Void> entity = restTemplate. getForEntity( "http://order-service/create/{userId}/{commodityCode}/{orderCount}", Void.class, userId , commodityCode, orderCount ); if (entity.getStatusCode()== HttpStatus.OK){ logger.info("訂單創建成功,用戶為{} ,商品編號為{},本次扣減的數量為{}",userId , commodityCode,orderCount); return;} throw new RuntimeException("訂單創建失敗") ; } }
4.6.5 集成 Ribbon

@Configuration public class HttpUtilConfig { @LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate() ; } }
4.6.6 添加配置文件

server: port: 18087 spring: application: name: business-service cloud: alibaba: seata: tx-service-group: business-service nacos: discovery: server-addr: localhost:8848 seata: service: vgroup-mapping: business-service: default grouplist: default: 127.0.0.1:8091 disable-global-transaction: false enabled: true
4.6.7 添加啟動類
@SpringBootApplication @EnableDiscoveryClient public class BusinessServiceApplication { public static void main(String[] args) { SpringApplication.run(BusinessServiceApplication.class ,args) ; } }
@SpringBootApplication @EnableDiscoveryClient @RestController public class BusinessServiceApplication { @Autowired private BusinessService businessService ; public static void main(String[] args) { SpringApplication.run(BusinessServiceApplication.class ,args) ; } /*** 開始下單 * @param userId * 用戶的 Id * @param commodityCode * 商品的編號 * @param orderCount * 商品的數量 * @return */ @GetMapping("/purchase/{userId}/{commodityCode}/{orderCount}") public ResponseEntity<Void> purchase( @PathVariable("userId") String userId, @PathVariable("commodityCode")String commodityCode, @PathVariable("orderCount")Integer orderCount){ businessService.purchase(userId,commodityCode,orderCount); return ResponseEntity.ok().build() ; } }
4.6.9 啟動項目測試
4.7 總體的調用流程如下


4.8 正常下單測試
- Accout_tbl 里面,SXT_USER_1 用戶的金額減少 100; Storage_tbl 里面,HUAWEI_0001 的庫存減少了 1;
- Order_Tbl 里面,創建了一條訂單記錄;

4.9 分布式事務的演示

4.9.1 在 accout-service 服務扣減余額觸發異常

4.9.2 重啟 accout-service
4.9.3 還原數據庫里面的數據




4.10 使用 Seata 解決分布式問題
4.10.1 改造 accout-service 里面的 AccountServiceImpl

4.10.2 改造 BusinessServiceImpl


發現發生異常后,
五、集成 Feign 測試 Seata
5.1 改造 business-service
5.1.1 添加依賴
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency> </dependencies>

@FeignClient("order-service") public interface OrderServiceFeign { @GetMapping("/create/{userId}/{commodityCode}/{orderCount}") ResponseEntity<Void> create( @PathVariable("userId") String userId, @PathVariable("commodityCode") String commodityCode, @PathVariable("orderCount") Integer orderCount); }

@FeignClient("storage-service") public interface StorageServiceFeign { @GetMapping("/deduct/{commodityCode}/{orderCount}") ResponseEntity<Void> deduct( @PathVariable("commodityCode") String commodityCode, @PathVariable("orderCount") Integer orderCount ) ; }

@Service public class OrderService { private static Logger logger = LoggerFactory.getLogger(StorageService.class) ; /** * 1 采用 Ribbon 的形式 */ @Autowired private RestTemplate restTemplate ; @Autowired private OrderServiceFeign orderServiceFeign ; /** * 2 采用 Feign 的形式 */ public void create(String userId, String commodityCode, int orderCount){
// Ribbon // ResponseEntity<Void> entity = restTemplate. // getForEntity( // "http://order-service/create/{userId}/{commodityCode}/{orderCount}", // Void.class, // userId , // commodityCode, // orderCount // ); //Feign ResponseEntity<Void> entity = orderServiceFeign.create(userId, commodityCode, orderCount); if (entity.getStatusCode()== HttpStatus.OK){ logger.info("訂單創建成功,用戶為{} ,商品編號為{},本次扣減的數量為{}",userId , commodityCode,orderCount); return; } throw new RuntimeException("訂單創建失敗") ; } }
5.1.7 改造 StorageService

@Service public class StorageService { private static Logger logger = LoggerFactory.getLogger(StorageService.class); /*** 1 采用 Ribbon 的形式 */ @Autowired private RestTemplate restTemplate; @Autowired private StorageServiceFeign storageServiceFeign; /** * 2 采用 Feign 的形式 */ public void deduct(String commodityCode, int orderCount) { // Ribbon // ResponseEntity<Void> entity = restTemplate. // getForEntity( // "http://storage-service/deduct/{commodityCode}/{orderCount}", // Void.class, // commodityCode, // orderCount // ); //Feign ResponseEntity<Void> entity = storageServiceFeign.deduct(commodityCode, orderCount); if (entity.getStatusCode() == HttpStatus.OK) { logger.info("扣減庫存成功,商品編號為{},本次扣減的數量為{}", commodityCode, orderCount); return; } throw new RuntimeException("扣減庫存失敗"); } }
5.1.6 在啟動類里面開啟對 Feign 的支持

5.2 改造 order-service
5.2.1 添加依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

@FeignClient("account-service") public interface AccountServiceFeign { @GetMapping("/debit/{userId}/{orderMoney}") ResponseEntity<Void> debit( @PathVariable("userId") String userId, @PathVariable("orderMoney") Integer orderMoney ) ; }
5.2.3 修改 AccoutService

/** * 實現對賬號服務的遠程調用 */ @Service public class AccountService { private static Logger logger = LoggerFactory.getLogger(AccountService.class) ; /** * 1 ribbon 的方式 */ @Autowired private RestTemplate restTemplate ; @Autowired private AccountServiceFeign accountServiceFeign ; /** * 2 feign 的方式*/ public void debit(String userId, int orderMoney) { //Ribbon // ResponseEntity<Void> entity = restTemplate. // getForEntity( // "http://accout-service/debit/{userId}/{orderMoney}", // Void.class, // userId, // orderMoney // ); ResponseEntity<Void> entity = accountServiceFeign.debit(userId, orderMoney); if(entity.getStatusCode()== HttpStatus.OK){ logger.info("扣減用戶{}金額成功,本次扣減的數目為{}",userId,orderMoney); return ; } logger.info("扣減用戶{}金額失敗",userId); throw new RuntimeException("扣減金額失敗") ; } }
5.2.4 在啟動類里面添加對 Feign 的支持

5.3 重啟測試


第六章 Spring Cloud Alibaba Dubbo
一、項目簡介
二、功能的完成度

三、框架的搭建

3.1 搭建 spring-cloud-dubbo-examples
3.1.1 創建項目




3.1.2 添加依賴
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
3.1.3 修改項目的打包方式
3.1.4 完整的 pom.xml 文件如下
<?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>spring-cloud-alibaba-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <artifactId>spring-cloud-dubbo-examples</artifactId> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
3.2 搭建 dubbo-api




3.2.2 完整的 pom.xml 文件如下
<?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>spring-cloud-dubbo-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>dubbo-api</artifactId> </project>


<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
3.3.3 完整的 pom.xml 文件如下
<?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>spring-cloud-dubbo-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>dubbo-provider</artifactId> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.4 搭建 dubbo-consumer
3.4.1 創建 dubbo-provider-consumer 項目



3.4.2 修改 Maven 的打包方式
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
3.4.3 完整的 pom.xml 文件如下
<?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>spring-cloud-dubbo-examples</artifactId> <groupId>com.bjsxt</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>dubbo-consumer</artifactId> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

四、代碼的完善
4.1 dubbo-api 代碼的完善.
4.1.1 定義 Dubbo 服務接口

4.1.2 項目的打包


4.2.2 實現 dubbo-api 里面定義的接口

4.2.3 添加配置文件

dubbo:
scan:
# dubbo 服務掃描基准包
base-packages: com.bjsxt.service.impl
cloud:
subscribed-services: dubbo-provider
protocol:
# dubbo 協議
name: dubbo
# dubbo 協議端口( -1 表示自增端口,從 20880 開始)
port: -1
registry:
# 掛載到 Spring Cloud 注冊中心
address: spring-cloud://localhost
spring:
application:
# Dubbo 應用名稱
name: dubbo-provider
main:
# Spring Boot 2.1 需要設定
allow-bean-definition-overriding: true
cloud:
nacos:
# Nacos 服務發現與注冊配置
discovery:
server-addr: localhost:8848
4.2.4 啟動類

@SpringBootApplication @EnableDiscoveryClient public class ProviderServiceApplication { public static void main(String[] args) { SpringApplication.run(ProviderServiceApplication.class, args) ; } }
4.3 dubbo-consumer 代碼的完善
4.3.1 添加依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.bjsxt</groupId> <artifactId>dubbo-api</artifactId> <version>1.0</version> </dependency> <!-- Dubbo Spring Cloud Starter --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
4.3.2 添加配置文件

dubbo: registry: # 掛載到 Spring Cloud 注冊中心 address: nacos://127.0.0.1:8848 cloud: subscribed-services: dubbo-provider server: port: 8080 spring: application: # Dubbo 應用名稱 name: dubbo-consumer main: # Spring Boot 2.1 需要設定 allow-bean-definition-overriding: true cloud: nacos: # Nacos 服務發現與注冊配置 discovery: server-addr: 127.0.0.1:8848
4.3.3 啟動類
@EnableDiscoveryClient @SpringBootApplication @RestController public class ConsumerServiceApplication { @Reference private EchoService echoService ; public static void main(String[] args) { SpringApplication.run(ConsumerServiceApplication.class,args) ; } @GetMapping("/rpc") public ResponseEntity<String> rpc(){ return ResponseEntity.ok(String.format("調用結果 為%s",echoService.echo("info"))); } }
4.4 遠程調用測試


五、負載均衡調用測試
5.1 啟動多個服務的提供者

5.2 使用消費者負載均衡調用測試
