微服務(wù):解決復(fù)雜業(yè)務(wù)的妙方
1 微服務(wù)介紹
1)什么是微服務(wù)
? 微服務(wù)(Microservices)是一種軟件架構(gòu)風(fēng)格,它將一個(gè)大型應(yīng)用程序拆分成許多較小的、松散耦合的、獨(dú)立運(yùn)行的服務(wù)。這些服務(wù)通常圍繞特定功能或業(yè)務(wù)領(lǐng)域組織,可以獨(dú)立開發(fā)、部署、擴(kuò)展和更新。微服務(wù)之間通過輕量級(jí)的通信協(xié)議(如HTTP/REST、消息隊(duì)列等)相互協(xié)作。
? 采用微服務(wù)架構(gòu)的優(yōu)點(diǎn)包括:
- 可擴(kuò)展性:每個(gè)服務(wù)都可以根據(jù)需求單獨(dú)進(jìn)行擴(kuò)展。
- 敏捷開發(fā):團(tuán)隊(duì)可以獨(dú)立開發(fā)和部署各自負(fù)責(zé)的服務(wù),加快開發(fā)速度。
- 容錯(cuò)性:當(dāng)某個(gè)服務(wù)出現(xiàn)問題時(shí),不會(huì)影響整個(gè)系統(tǒng)的正常運(yùn)行。
- 技術(shù)棧靈活性:可以針對(duì)具體任務(wù)選擇最佳技術(shù)和編程語言,而不受整體約束。
? 然而,微服務(wù)架構(gòu)也存在一些挑戰(zhàn),例如網(wǎng)絡(luò)延遲、數(shù)據(jù)一致性和服務(wù)間通信復(fù)雜性等問題。在實(shí)踐中,需要根據(jù)項(xiàng)目需求審慎權(quán)衡是否采用微服務(wù)架構(gòu)。
2)什么是分布式
? 分布式(Distributed)是指一個(gè)系統(tǒng)的組件分散在不同的網(wǎng)絡(luò)節(jié)點(diǎn)上,這些組件協(xié)同工作以完成整個(gè)系統(tǒng)的任務(wù)。分布式系統(tǒng)可以提高可擴(kuò)展性、容錯(cuò)能力和資源利用率。由于組件在物理上分隔,分布式系統(tǒng)需要處理諸如數(shù)據(jù)一致性、通信延遲和故障恢復(fù)等問題。
? 微服務(wù)是一種特定的分布式系統(tǒng)架構(gòu)風(fēng)格,它強(qiáng)調(diào)將應(yīng)用程序拆分為許多較小的、松散耦合的、獨(dú)立運(yùn)行的服務(wù)。這些服務(wù)圍繞特定功能或業(yè)務(wù)領(lǐng)域組織,并通過輕量級(jí)通信協(xié)議進(jìn)行交互。
3)分布式和微服務(wù)的區(qū)別:
- 范圍:分布式是更廣泛的概念,涵蓋了各種類型的分布式系統(tǒng);而微服務(wù)是一種具體的分布式架構(gòu)風(fēng)格。
- 設(shè)計(jì)原則:微服務(wù)強(qiáng)調(diào)每個(gè)服務(wù)的獨(dú)立性、職責(zé)單一、松耦合和自治性,這使得微服務(wù)更容易適應(yīng)不斷變化的需求。而其他類型的分布式系統(tǒng)可能沒有這些特性。
- 技術(shù)實(shí)現(xiàn):雖然兩者都涉及跨多個(gè)節(jié)點(diǎn)的組件,但實(shí)現(xiàn)方式可能有所不同。微服務(wù)通常使用HTTP/REST、消息隊(duì)列等輕量級(jí)通信協(xié)議,而其他分布式系統(tǒng)可能使用RPC、一致性算法等技術(shù)。
? 總之,微服務(wù)是分布式系統(tǒng)中的一種特殊類型。雖然它們?cè)诟拍钌嫌兄丿B,但它們?cè)谠O(shè)計(jì)原則和實(shí)現(xiàn)細(xì)節(jié)上有所不同。

2 springcloud介紹
1)springcloud組件
? Spring Cloud是一個(gè)基于Spring Boot的微服務(wù)框架,旨在簡化分布式系統(tǒng)中的開發(fā)、部署和管理工作。它提供了一系列模塊和工具,可以幫助開發(fā)者快速搭建彈性、可擴(kuò)展且易于維護(hù)的微服務(wù)應(yīng)用。以下是一些常見的Spring Cloud組件:
- Spring Cloud Config:分布式配置中心,允許您在集中位置管理所有服務(wù)的配置,并實(shí)時(shí)更新。
- Spring Cloud Netflix Eureka:服務(wù)注冊(cè)與發(fā)現(xiàn)組件,提供了自動(dòng)化的服務(wù)注冊(cè)、注銷和查找功能,支持負(fù)載均衡。
- Spring Cloud Netflix Ribbon:客戶端負(fù)載均衡器,可以在調(diào)用其他服務(wù)時(shí)為請(qǐng)求選擇合適的實(shí)例,提高系統(tǒng)的可用性。
- Spring Cloud Netflix Hystrix:熔斷器和隔離組件,用于防止故障級(jí)聯(lián)和提供降級(jí)處理。
- Spring Cloud Netflix Zuul:API網(wǎng)關(guān), 責(zé)處理客戶端請(qǐng)求并將其轉(zhuǎn)發(fā)到適當(dāng)?shù)膬?nèi)部服務(wù) 。
- Spring Cloud Gateway:API網(wǎng)關(guān),負(fù)責(zé)轉(zhuǎn)發(fā)請(qǐng)求、聚合響應(yīng)、路由、認(rèn)證授權(quán)和限流等功能。
- Spring Cloud Sleuth 和 Zipkin:分布式追蹤組件,用于監(jiān)控和診斷跨服務(wù)的請(qǐng)求鏈路。
- Spring Cloud Stream:基于消息驅(qū)動(dòng)的微服務(wù)通信組件,支持異步處理和解耦服務(wù)間的通信。
- Spring Cloud Security:安全性模塊,提供了一些基本的安全措施,如認(rèn)證、授權(quán)和API保護(hù)等功能。
- Spring Cloud Bus:事件總線,用于實(shí)現(xiàn)服務(wù)間消息傳遞和廣播配置更新等功能。
- Spring Cloud OpenFeign:聲明式HTTP客戶端,簡化了RESTful服務(wù)之間的通信。
? 由于Netflix公司已經(jīng)停止了對(duì)Spring Cloud Netflix組件的維護(hù)和更新工作, 所以Netflix 相關(guān)的組件已經(jīng)目前已經(jīng)有對(duì)應(yīng)的替代組件:
? Eureka --> Nacos; Ribbon --> LoadBalancer; Zuul --> Gateway; Hystrix --> Sentinel
2)springcloud alibaba
? Spring Cloud Alibaba 是一個(gè)基于 Spring Cloud 的微服務(wù)開發(fā)框架,它提供了與 Alibaba 相關(guān)組件(如 Nacos、Sentinel、RocketMQ 等)的集成。Spring Cloud Alibaba 旨在簡化開發(fā)者構(gòu)建分布式應(yīng)用的過程,并幫助實(shí)現(xiàn)高可用、彈性伸縮和快速故障恢復(fù)等目標(biāo)。
主要功能包括:
- 服務(wù)注冊(cè)與發(fā)現(xiàn):Spring Cloud Alibaba 集成了阿里巴巴的 Nacos 作為服務(wù)注冊(cè)中心,支持針對(duì)各種場(chǎng)景的服務(wù)管理。
- 分布式配置管理:通過 Nacos 提供統(tǒng)一的配置管理服務(wù),實(shí)現(xiàn)配置的動(dòng)態(tài)更新和版本控制。
- 服務(wù)熔斷與限流:集成阿里巴巴的 Sentinel 組件,提供流量控制、熔斷降級(jí)和系統(tǒng)負(fù)載保護(hù)等功能,保證服務(wù)的穩(wěn)定性。
- 消息隊(duì)列:集成阿里巴巴的 RocketMQ,提供分布式消息隊(duì)列服務(wù),實(shí)現(xiàn)異步通信、解耦和削峰填谷等功能。
- 分布式事務(wù):通過 Seata 組件提供分布式事務(wù)處理能力,幫助解決微服務(wù)架構(gòu)下的數(shù)據(jù)一致性問題。
- 鏈路追蹤:集成阿里巴巴的 Arthas 和 SkyWalking,實(shí)現(xiàn)鏈路追蹤和性能監(jiān)控等功能。
通過使用 Spring Cloud Alibaba,開發(fā)者可以更輕松地基于阿里巴巴的技術(shù)棧構(gòu)建微服務(wù)應(yīng)用,并充分利用其豐富的功能和穩(wěn)定性。
3)版本對(duì)應(yīng)關(guān)系
spring boot和spring cloud以及 spring cloud alibaba的版本對(duì)應(yīng)關(guān)系;
| Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
|---|---|---|
| 2.2.10-RC1* | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
| 2.2.9.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
| 2.2.8.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
| 2.2.7.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
| 2.2.6.RELEASE | Spring Cloud Hoxton.SR9 | 2.3.2.RELEASE |
| 2.2.1.RELEASE | Spring Cloud Hoxton.SR3 | 2.2.5.RELEASE |
| 2.2.0.RELEASE | Spring Cloud Hoxton.RELEASE | 2.2.X.RELEASE |
| 2.1.4.RELEASE | Spring Cloud Greenwich.SR6 | 2.1.13.RELEASE |
| 2.1.2.RELEASE | Spring Cloud Greenwich | 2.1.X.RELEASE |
| 2.0.4.RELEASE(停止維護(hù),建議升級(jí)) | Spring Cloud Finchley | 2.0.X.RELEASE |
| 1.5.1.RELEASE(停止維護(hù),建議升級(jí)) | Spring Cloud Edgware | 1.5.X.RELEASE |
spring cloud alibaba組件之間的對(duì)應(yīng)關(guān)系
| Spring Cloud Alibaba Version | Sentinel Version | Nacos Version | RocketMQ Version | Dubbo Version | Seata Version |
|---|---|---|---|---|---|
| 2022.0.0.0-RC2 | 1.8.6 | 2.2.1 | 4.9.4 | ~ | 1.7.0-native-rc2 |
| 2021.0.5.0 | 1.8.6 | 2.2.0 | 4.9.4 | ~ | 1.6.1 |
| 2.2.10-RC1 | 1.8.6 | 2.2.0 | 4.9.4 | ~ | 1.6.1 |
| 2022.0.0.0-RC1 | 1.8.6 | 2.2.1-RC | 4.9.4 | ~ | 1.6.1 |
| 2.2.9.RELEASE | 1.8.5 | 2.1.0 | 4.9.4 | ~ | 1.5.2 |
| 2021.0.4.0 | 1.8.5 | 2.0.4 | 4.9.4 | ~ | 1.5.2 |
| 2.2.8.RELEASE | 1.8.4 | 2.1.0 | 4.9.3 | ~ | 1.5.1 |
| 2021.0.1.0 | 1.8.3 | 1.4.2 | 4.9.2 | ~ | 1.4.2 |
| 2.2.7.RELEASE | 1.8.1 | 2.0.3 | 4.6.1 | 2.7.13 | 1.3.0 |
| 2.2.6.RELEASE | 1.8.1 | 1.4.2 | 4.4.0 | 2.7.8 | 1.3.0 |
| 2021.1 or 2.2.5.RELEASE or 2.1.4.RELEASE or 2.0.4.RELEASE | 1.8.0 | 1.4.1 | 4.4.0 | 2.7.8 | 1.3.0 |
| 2.2.3.RELEASE or 2.1.3.RELEASE or 2.0.3.RELEASE | 1.8.0 | 1.3.3 | 4.4.0 | 2.7.8 | 1.3.0 |
| 2.2.1.RELEASE or 2.1.2.RELEASE or 2.0.2.RELEASE | 1.7.1 | 1.2.1 | 4.4.0 | 2.7.6 | 1.2.0 |
| 2.2.0.RELEASE | 1.7.1 | 1.1.4 | 4.4.0 | 2.7.4.1 | 1.0.0 |
| 2.1.1.RELEASE or 2.0.1.RELEASE or 1.5.1.RELEASE | 1.7.0 | 1.1.4 | 4.4.0 | 2.7.3 | 0.9.0 |
| 2.1.0.RELEASE or 2.0.0.RELEASE or 1.5.0.RELEASE | 1.6.3 | 1.1.1 | 4.4.0 | 2.7.3 | 0.7.1 |
3 服務(wù)注冊(cè)和發(fā)現(xiàn)
? 在 Java 微服務(wù)架構(gòu)中,服務(wù)注冊(cè)與發(fā)現(xiàn)是一種關(guān)鍵的設(shè)計(jì)模式,它允許各個(gè)微服務(wù)實(shí)例自動(dòng)注冊(cè)、發(fā)現(xiàn)其他服務(wù)和負(fù)載均衡。這樣做的優(yōu)勢(shì)主要包括:
-
簡化服務(wù)調(diào)用:服務(wù)間可以通過服務(wù)名而非硬編碼的 IP 地址或端口號(hào)進(jìn)行相互調(diào)用。
-
動(dòng)態(tài)伸縮:支持自動(dòng)添加或刪除服務(wù)實(shí)例,無需手動(dòng)維護(hù)服務(wù)列表。
-
負(fù)載均衡:根據(jù)某種策略(如輪詢或最少連接)將請(qǐng)求分發(fā)到多個(gè)服務(wù)實(shí)例,從而提高系統(tǒng)整體容錯(cuò)性和可用性。

在 Java 生態(tài)中,常見的服務(wù)注冊(cè)與發(fā)現(xiàn)解決方案有以下幾種:
- Eureka:Eureka 是 Netflix 開源的一款服務(wù)注冊(cè)與發(fā)現(xiàn)組件,它基于 RESTful API,具有較好的可擴(kuò)展性和容錯(cuò)能力。與 Spring Cloud 配合使用時(shí),只需簡單的配置即可實(shí)現(xiàn)服務(wù)注冊(cè)與發(fā)現(xiàn)功能。
- Consul:Consul 是由 HashiCorp 開發(fā)的一款服務(wù)網(wǎng)格解決方案,提供服務(wù)注冊(cè)與發(fā)現(xiàn)、配置管理和服務(wù)分段等功能。同樣可以與 Spring Cloud 集成,便于構(gòu)建微服務(wù)應(yīng)用。
- Nacos:阿里巴巴開源的 Nacos 提供了服務(wù)注冊(cè)與發(fā)現(xiàn)以及配置管理功能,可以與 Spring Cloud Alibaba 集成使用。Nacos 支持多種服務(wù)注冊(cè)方式(如域名、IP 等),并提供豐富的負(fù)載均衡策略和健康檢查機(jī)制。
- Zookeeper:Apache ZooKeeper 是一個(gè)分布式協(xié)調(diào)服務(wù),雖然它并非專為服務(wù)注冊(cè)與發(fā)現(xiàn)而設(shè)計(jì),但由于其強(qiáng)大的分布式一致性能力,被廣泛用于實(shí)現(xiàn)服務(wù)注冊(cè)與發(fā)現(xiàn)功能。通過 Curator 客戶端庫,可以方便地與 Spring Cloud 集成。
1)nacos介紹
? Nacos(Naming and Configuration Service)是阿里巴巴開源的一款用于服務(wù)注冊(cè)與發(fā)現(xiàn)、配置管理的中間件。它具有易用性、高可用性和強(qiáng)大的功能,旨在幫助開發(fā)者構(gòu)建動(dòng)態(tài)、可擴(kuò)展的分布式系統(tǒng)。以下是 Nacos 的主要特點(diǎn)和功能:
-
服務(wù)注冊(cè)與發(fā)現(xiàn):Nacos 提供了一個(gè)基于 DNS 和 HTTP 的服務(wù)注冊(cè)與發(fā)現(xiàn)框架,支持多種服務(wù)注冊(cè)方式(如域名、IP 地址等),并提供豐富的負(fù)載均衡策略和健康檢查機(jī)制。
-
動(dòng)態(tài)配置管理:Nacos 支持配置信息的實(shí)時(shí)推送和更新,以滿足應(yīng)用程序在運(yùn)行過程中對(duì)配置數(shù)據(jù)的需求。通過其 UI 控制臺(tái),使用者可以輕松管理不同環(huán)境、集群和命名空間下的配置。
-
分組及分區(qū):Nacos 支持根據(jù)服務(wù)實(shí)例元數(shù)據(jù)進(jìn)行分組和分區(qū),從而實(shí)現(xiàn)精細(xì)化的服務(wù)治理。
-
高可用和容錯(cuò):Nacos 具有良好的高可用設(shè)計(jì),支持集群部署,保證在節(jié)點(diǎn)故障的情況下也能正常提供服務(wù)。此外,Nacos 還提供了臨時(shí)存儲(chǔ),以確保用戶在網(wǎng)絡(luò)狀況不佳的情況下仍然能夠訪問配置。
-
多環(huán)境支持:Nacos 支持多環(huán)境、多集群的管理,方便用戶在不同的場(chǎng)景下使用 Nacos。
-
易于集成:Nacos 可以輕松與其他 Java 微服務(wù)框架(如 Spring Cloud、Dubbo 等)集成。在 Spring Cloud Alibaba 體系下,只需簡單的配置就可以實(shí)現(xiàn) Nacos 與應(yīng)用程序的無縫對(duì)接。
-
社區(qū)活躍:作為阿里巴巴開源的項(xiàng)目,Nacos 擁有活躍的社區(qū)和豐富的文檔資源,方便開發(fā)者快速上手和入門。
Nacos = Spring Cloud Eureka + Spring Cloud Config
?
2)nacos基本運(yùn)行
?

Nacos 依賴 Java 環(huán)境來運(yùn)行。所以需要安裝jdk1.8+以及配置環(huán)境變量;
2.1 nacos-server下載和啟動(dòng)
? 根據(jù)spring-cloud-alibaba中的版本說明, nacos我們下載使用2.1.0;
? 如果在github上下載太慢, 可以在gitee上下載源碼, 然后使用maven打包, 安裝; (maven需配置環(huán)境變量)
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
? 運(yùn)行nacos:
? 1) 使用cmd, 跳轉(zhuǎn)到nacos下的bin文件下,
? 2) 單機(jī)運(yùn)行指令: startup.cmd -m standalone

? 3)nacos后臺(tái)管理: http://127.0.0.1:8848/nacos/,
? 輸入賬號(hào): nacos 密碼: nacos

?

2.2 創(chuàng)建聚合項(xiàng)目
創(chuàng)建聚合項(xiàng)目進(jìn)行依賴的版本管理;
pom.xml文件
<packaging>pom</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot-version>2.3.12.RELEASE</spring-boot-version>
<spring-cloud-version>Hoxton.SR12</spring-cloud-version>
<spring-cloud-alibaba-version>2.2.9.RELEASE</spring-cloud-alibaba-version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>${spring-cloud-alibaba-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.3 訂單服務(wù)
POM文件
<parent>
<groupId>com.qs.cloud</groupId>
<artifactId>cloud-117</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>order-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<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-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
啟動(dòng)類:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class);
}
}
配置文件:
spring.application.name=order-server
server.port=9001
spring.cloud.nacos.discovery.server-addr=http://127.0.0.1:8848
feign接口:
@FeignClient("stock-server")
public interface StockFeign {
@GetMapping("index")
String index();
}
controller:
@RestController
public class OrderController {
@Autowired
StockFeign stockFeign;
@GetMapping("/index")
public String index(){
String index = stockFeign.index();
return index;
}
}
4)配置中心
nacos除了作為注冊(cè)中心以外, 同時(shí)也可以作為配置中心來使用;
1)配置中心的基本使用
導(dǎo)入依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
創(chuàng)建項(xiàng)目的bootstrap.properties文件
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=order-server
在nacos配置中心發(fā)布改項(xiàng)目的配置文件:

dataId 的完整格式如下:
服務(wù)名稱-運(yùn)行環(huán)境.后綴名
- 服務(wù)名稱: 默認(rèn)為所屬項(xiàng)目配置
spring.application.name的值(即:order-server) - 運(yùn)行環(huán)境: 即為當(dāng)前環(huán)境對(duì)應(yīng)的 profile,配置文件中spring.profiles.active設(shè)置的值:當(dāng) 這個(gè)值為空時(shí),對(duì)應(yīng)的連接符 - 也將不存
- 后綴名: 為配置內(nèi)容的數(shù)據(jù)格式,可以在項(xiàng)目配置文件中通過配置項(xiàng)
spring.cloud.nacos.config.file-extension來配置。目前只支持properties和yaml類型。
總結(jié):配置所屬工程的spring.application.name的值 + "." + properties/yml
2)使用命名空間
Nacos配置中心的命名空間,它用來隔離不同的配置信息,類似于不同的環(huán)境或應(yīng)用程序之間的劃分。通過使用不同的命名空間,可以在不同的場(chǎng)景下管理和控制配置信息,實(shí)現(xiàn)配置的靈活性和可維護(hù)性。每個(gè)命名空間都有自己獨(dú)立的配置集合,可以設(shè)置不同的權(quán)限和訪問控制,方便團(tuán)隊(duì)協(xié)作和管理配置的版本控制。

nacos配置中心新建配置文件:order-server-dev.yml

myName: 趙六
server:
port: 9001
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
項(xiàng)目bootstrap.properties中指定命名空間和運(yùn)行環(huán)境:
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=order-server
spring.cloud.nacos.config.file-extension=yml
spring.cloud.nacos.config.namespace=b49888df-e80a-4e7b-a4eb-c128a41c16e6
spring.profiles.active=dev
以上是專門為dev運(yùn)行環(huán)境創(chuàng)建命名空間和配置文件, 也可以為其他prod,test等生產(chǎn)環(huán)境創(chuàng)建命名空間.
3)使用組名
分組(Group)用于對(duì)配置進(jìn)行邏輯上的分類和管理。通過將不同項(xiàng)目的配置歸屬到不同的分組中,可以方便地對(duì)配置進(jìn)行組織、查找和管理.
nacos中新建配置文件:

項(xiàng)目的bootstrap.properties文件中加入組的配置:
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=order-server
spring.cloud.nacos.config.file-extension=yml
spring.cloud.nacos.config.namespace=b49888df-e80a-4e7b-a4eb-c128a41c16e6
spring.cloud.nacos.config.group=jingdong
spring.profiles.active=dev
4)配置自動(dòng)刷新

5)加載多配置文件
在不同的組創(chuàng)建兩個(gè)配置文件, 文件的命名也沒有按照之前的命名規(guī)則:

配置文件內(nèi)容分別為: redis: 1234, mysql: 3306;
bootstrap.properties
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=order-server
spring.cloud.nacos.config.file-extension=yml
spring.cloud.nacos.config.namespace=b49888df-e80a-4e7b-a4eb-c128a41c16e6
spring.cloud.nacos.config.extension-configs[0].data-id=redis.yml
spring.cloud.nacos.config.extension-configs[1].data-id=mysql.yml
spring.cloud.nacos.config.extension-configs[1].group=taobao
多配置文件刷新需要加上:
spring.cloud.nacos.config.extension-configs[0].refresh=true
5 服務(wù)之間的遠(yuǎn)程調(diào)用
- HTTP/REST:使用HTTP協(xié)議進(jìn)行通信,通過RESTful API進(jìn)行數(shù)據(jù)交換。每個(gè)微服務(wù)可以作為獨(dú)立的服務(wù)端和客戶端。
- 消息隊(duì)列:使用消息隊(duì)列系統(tǒng)(如RabbitMQ、Kafka)來實(shí)現(xiàn)異步通信。微服務(wù)之間通過發(fā)布和訂閱消息來進(jìn)行通信。
- RPC(遠(yuǎn)程過程調(diào)用):微服務(wù)之間直接調(diào)用對(duì)方提供的方法,就像本地方法調(diào)用一樣??梢允褂每蚣苋鏶RPC、Thrift、Dubbo等來簡化遠(yuǎn)程調(diào)用過程。
- 事件驅(qū)動(dòng)架構(gòu):微服務(wù)之間通過發(fā)布和訂閱事件進(jìn)行通信。一個(gè)微服務(wù)可以產(chǎn)生事件并將其發(fā)布到事件總線,其他微服務(wù)可以訂閱感興趣的事件并做出響應(yīng)。
- Service Mesh:使用Service Mesh框架(如Istio、Linkerd)來管理和控制微服務(wù)之間的通信。它在微服務(wù)之間插入代理,提供了流量管理、安全性、故障恢復(fù)等功能。
1)openfeign的使用
實(shí)現(xiàn): 訂單服務(wù)遠(yuǎn)程調(diào)用庫存服務(wù)
導(dǎo)入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Feign接口:
@FeignClient("stock-server")
public interface StockFeign {
@GetMapping("index")
String index();
}
OrderController:
@RestController
public class OrderController {
@Autowired
StockFeign stockFeign;
@GetMapping("/index")
public String index(){
String index = stockFeign.index();
return index;
}
}
StockController:
@RestController
public class StockController {
@Value("${server.port}")
Integer port;
@GetMapping("/index")
public String index(){
return "這里是stock........."+port;
}
}
2)傳值
3)ribbon負(fù)載均衡的配置
openfeign默認(rèn)使用的負(fù)載均衡是ribbon, 而ribbon默認(rèn)的負(fù)載均衡策略是輪詢機(jī)制:
啟動(dòng)2個(gè)stock-server, 端口號(hào)分別為:9002,9003;

當(dāng)訪問order-server中controller時(shí), 會(huì)發(fā)現(xiàn)9002和9003的stock-server時(shí)輪流訪問的;
ribbon自帶的負(fù)載均衡策略有:
1)輪詢策略: RoundRobinRule,按照一定的順序依次調(diào)用服務(wù)實(shí)例。比如一共有 3 個(gè)服務(wù),第一次調(diào)用服務(wù) 1,第二次調(diào)用服務(wù) 2,第三次調(diào)用服務(wù)3,依次類推.
2)權(quán)重策略: WeightedResponseTimeRule,根據(jù)每個(gè)服務(wù)提供者的響應(yīng)時(shí)間分配一個(gè)權(quán)重,響應(yīng)時(shí)間越長,權(quán)重越小,被選中的可能性也就越低。 它的實(shí)現(xiàn)原理是,剛開始使用輪詢策略并開啟一個(gè)計(jì)時(shí)器,每一段時(shí)間收集一次所有服務(wù)提供者的平均響應(yīng)時(shí)間,然后再給每個(gè)服務(wù)提供者附上一個(gè)權(quán)重,權(quán)重越高被選中的概率也越大.
3)隨機(jī)策略: RandomRule,從服務(wù)提供者的列表中隨機(jī)選擇一個(gè)服務(wù)實(shí)例.
4)最小連接數(shù)策略: BestAvailableRule,也叫最小并發(fā)數(shù)策略,它是遍歷服務(wù)提供者列表,選取連接數(shù)最小的?個(gè)服務(wù)實(shí)例。如果有相同的最小連接數(shù),那么會(huì)調(diào)用輪詢策略進(jìn)行選取
5)重試策略: RetryRule,按照輪詢策略來獲取服務(wù),如果獲取的服務(wù)實(shí)例為 null 或已經(jīng)失效,則在指定的時(shí)間之內(nèi)不斷地進(jìn)行重試來獲取服務(wù),如果超過指定時(shí)間依然沒獲取到服務(wù)實(shí)例則返回 null
6)可用性敏感策略: AvailabilityFilteringRule,先過濾掉非健康的服務(wù)實(shí)例,然后再選擇連接數(shù)較小的服務(wù)實(shí)例.
7)區(qū)域敏感策略: ZoneAvoidanceRule,根據(jù)服務(wù)所在區(qū)域(zone)的性能和服務(wù)的可用性來選擇服務(wù)實(shí)例,在沒有區(qū)域的環(huán)境下,該策略和輪詢策略類似.
配置負(fù)載均衡策略有以下兩種方式:
1)使用配置文件配置負(fù)載均衡策略:
# 格式: 服務(wù)名稱.ribbon.NFLoadBalancerRuleClassName=負(fù)載均衡策略
stock-server.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
2)java類中配置負(fù)載均衡策略:
@Configuration
public class MyConfig {
@Bean
public IRule loadBalancedRule(){
return new RandomRule()
}
}
3)自定義負(fù)載均衡策略:
public class MyRule implements IRule {
private ILoadBalancer lb;
private List<Integer> excludePorts;
public MyRule() {
}
public MyRule(List<Integer> excludePorts) {
this.excludePorts = excludePorts;
}
@Override
public Server choose(Object o) {
List<Server> allServers = lb.getAllServers();
Random random = new Random();
return allServers.get( random.nextInt(allServers.size()) );
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.lb = iLoadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return lb;
}
}
@Configuration
public class MyConfig {
@Bean
public IRule loadBalancedRule(){
return new MyRule();
}
}
?
3)SpringCloudLoadBalancer
由于Netflix公司已經(jīng)停止了對(duì)Spring Cloud Netflix組件的維護(hù)和更新工作, 所以Netflix 相關(guān)的組件已經(jīng)目前已經(jīng)有對(duì)應(yīng)的替代組件:
所以Ribbon逐漸被LoadBalancer給替換;
SpringCloudLoadBalancer官方文檔:Cloud Native Applications (spring.io)
導(dǎo)入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
關(guān)閉對(duì)ribbon的支持:
spring.cloud.loadbalancer.ribbon.enabled=false
SpringCloudLoadBalancer自帶的負(fù)載均衡策略有2中:
? 1)輪詢策略:RoundRobinLoadBalancer (默認(rèn))
? 2)隨機(jī)策略:RandomLoadBalancer
可以使用以下方式設(shè)置改變負(fù)載均衡策略:
@Configuration
public class MyConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@LoadBalancerClients(defaultConfiguration = {MyConfig.class})
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class);
}
}
6 微服務(wù)網(wǎng)關(guān)

1)網(wǎng)關(guān)介紹:
Java微服務(wù)是一種用于構(gòu)建和管理微服務(wù)架構(gòu)的核心組件。它充當(dāng)了系統(tǒng)的入口點(diǎn),負(fù)責(zé)接收來自客戶端的請(qǐng)求,并將其轉(zhuǎn)發(fā)到適當(dāng)?shù)奈⒎?wù)實(shí)例進(jìn)行處理.
? 主要功能和優(yōu)勢(shì):
- 路由和負(fù)載均衡:網(wǎng)關(guān)可以根據(jù)請(qǐng)求的URL、路徑或其他條件,將請(qǐng)求路由到相應(yīng)的微服務(wù)實(shí)例。同時(shí),它還可以通過負(fù)載均衡算法,將請(qǐng)求分發(fā)給多個(gè)實(shí)例,實(shí)現(xiàn)高可用和性能優(yōu)化。
- 安全認(rèn)證和授權(quán):網(wǎng)關(guān)可以集成各種安全機(jī)制,例如基于令牌的身份驗(yàn)證、OAuth、JWT等,對(duì)請(qǐng)求進(jìn)行認(rèn)證和授權(quán)。這樣可以確保只有經(jīng)過驗(yàn)證的用戶才能訪問受保護(hù)的微服務(wù)。
- 請(qǐng)求轉(zhuǎn)換和協(xié)議適配:網(wǎng)關(guān)可以在前后端之間進(jìn)行請(qǐng)求和響應(yīng)的轉(zhuǎn)換,使不同的客戶端能夠使用適合的協(xié)議和數(shù)據(jù)格式進(jìn)行通信。例如,將RESTful請(qǐng)求轉(zhuǎn)換為SOAP請(qǐng)求,或者將JSON請(qǐng)求轉(zhuǎn)換為Protobuf請(qǐng)求等。
- 緩存和性能優(yōu)化:網(wǎng)關(guān)可以對(duì)某些請(qǐng)求進(jìn)行緩存,以減少對(duì)后端微服務(wù)的請(qǐng)求次數(shù),提供更快的響應(yīng)速度。此外,它還可以進(jìn)行請(qǐng)求的壓縮、合并和優(yōu)化,減少網(wǎng)絡(luò)傳輸量,提高性能。
- 監(jiān)控和日志:網(wǎng)關(guān)可以收集和記錄請(qǐng)求的統(tǒng)計(jì)信息、異常情況和日志,方便進(jìn)行系統(tǒng)的監(jiān)控和分析。這對(duì)于故障排除、性能優(yōu)化和安全審計(jì)非常有幫助。
- 灰度發(fā)布和路由策略:網(wǎng)關(guān)可以支持灰度發(fā)布和路由策略,使得可以逐步將流量從舊版本的微服務(wù)遷移到新版本,同時(shí)還能夠根據(jù)一些規(guī)則對(duì)請(qǐng)求進(jìn)行動(dòng)態(tài)路由。
常見的Java微服務(wù)網(wǎng)關(guān)包括Netflix Zuul、Spring Cloud Gateway, 由于Netflix 已經(jīng)停止維護(hù), 所以目前常用的微服務(wù)官網(wǎng)Gateway.
2)基本使用
導(dǎo)入依賴:
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
配置文件:
server.port=9000
spring.application.name=gateway-server
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring:
cloud:
gateway:
routes:
- id: order-server
uri: http://127.0.0.1:9001
predicates:
- Path=/order/**
order-server:
@GetMapping("/order/index")
public String index(){
return "hello"+redis+"+++"+mysql;
}
瀏覽器訪問: 127.0.0.1:9000/order/index, 會(huì)直接訪問到order-server里面;
3)路由規(guī)則
以上的配置中routes就是網(wǎng)關(guān)的路由配置,路由是一個(gè)List
id: 服務(wù)器唯一標(biāo)識(shí);
uri: 服務(wù)器的地址;
predicates: 斷言集合; 用于匹配請(qǐng)求的路徑、方法、頭部等信息,從而確定該請(qǐng)求是否應(yīng)該被路由到某個(gè)目標(biāo)服務(wù)。
filters: 過濾器集合; 用于匹配請(qǐng)求的路徑、方法、頭部等信息,從而確定該請(qǐng)求是否應(yīng)該被路由到某個(gè)目標(biāo)服務(wù)。
路由根據(jù)斷言進(jìn)行匹配,匹配成功就會(huì)轉(zhuǎn)發(fā)請(qǐng)求給服務(wù)器URI,在轉(zhuǎn)發(fā)請(qǐng)求之前或者之后可以添加過濾器。
3.1)斷言工廠
斷言用于匹配請(qǐng)求的條件,以確定是否應(yīng)該將請(qǐng)求路由到某個(gè)目標(biāo)服務(wù)。Spring Cloud Gateway提供了多種類型的斷言,可以根據(jù)請(qǐng)求的路徑、方法、頭部等信息進(jìn)行匹配。
Spring Cloud Gateway包含許多內(nèi)置的斷言工廠:

以下是一些常用且需要掌握的斷言類型及其示例:
-
Path斷言:根據(jù)請(qǐng)求的路徑進(jìn)行匹配。
1.簡單的路徑匹配: Path=/foo:匹配完整的路徑為/foo的請(qǐng)求。 Path=/foo/**:匹配以/foo/開頭的路徑。 2.使用正則表達(dá)式進(jìn)行路徑匹配: Path=^/foo/\\d+$:匹配路徑以/foo/開頭,并且后面跟著一個(gè)或多個(gè)數(shù)字的請(qǐng)求。 3.使用Ant風(fēng)格的路徑匹配: Path=/foo/*:匹配以/foo/開頭的任意一級(jí)路徑。 Path=/foo/**/bar:匹配以/foo/開頭,最后一級(jí)路徑為/bar的請(qǐng)求。 4.使用路徑變量進(jìn)行匹配: Path=/foo/{id}:匹配以/foo/開頭,并且后面跟著任意值的請(qǐng)求,將匹配的部分作為路徑變量id傳遞給下游服務(wù)。 -
Method斷言:根據(jù)請(qǐng)求的HTTP方法進(jìn)行匹配。
predicates: - Method=GET 匹配所有使用GET方法的請(qǐng)求 -
Header斷言:根據(jù)請(qǐng)求的頭部信息進(jìn)行匹配。
1.簡單的Header匹配: Header=X-Custom-Header, custom-value: 匹配請(qǐng)求頭X-Custom-Header的值為custom-value的請(qǐng)求。 2.使用正則表達(dá)式進(jìn)行Header匹配: Header=Content-Type, application/json.*: 匹配請(qǐng)求頭Content-Type的值以application/json開頭的請(qǐng)求。 3.多個(gè)Header條件的匹配: Header=X-Custom-Header, custom-value && Authorization, Bearer .*: 同時(shí)匹配請(qǐng)求頭X-Custom-Header的值為custom-value和Authorization的值以Bearer開頭的請(qǐng)求。 4.忽略大小寫的Header匹配: Header=X-Custom-Header, custom-value, true: 匹配請(qǐng)求頭X-Custom-Header的值為custom-value,忽略大小寫的請(qǐng)求 -
Query斷言:根據(jù)請(qǐng)求的查詢參數(shù)進(jìn)行匹配。
1.簡單的查詢參數(shù)匹配: Query=param,value: 匹配查詢參數(shù)param的值為value的請(qǐng)求。 2.使用正則表達(dá)式進(jìn)行查詢參數(shù)匹配: Query=param,^\d+$: 匹配查詢參數(shù)param的值為一個(gè)或多個(gè)數(shù)字的請(qǐng)求。 3.多個(gè)查詢參數(shù)條件的匹配: Query=param1,value1 Query=param2,value2 同時(shí)匹配查詢參數(shù)param1的值為value1和param2的值為value2的請(qǐng)求。 4.忽略大小寫的查詢參數(shù)匹配: Query=param,value, case-insensitive,true: 匹配查詢參數(shù)param的值為value,忽略大小寫的請(qǐng)求。 5.查詢參數(shù)存在性的匹配: Query=param-exists,true: 匹配包含名為param的查詢參數(shù)的請(qǐng)求 -
Cookie斷言:根據(jù)請(qǐng)求的Cookie信息進(jìn)行匹配
1.簡單的Cookie匹配: Cookie=cookieName,value: 匹配名為cookieName且值為value的Cookie。 2.使用正則表達(dá)式進(jìn)行Cookie匹配: Cookie=cookieName, ^\d+$: 匹配名為cookieName且值為一個(gè)或多個(gè)數(shù)字的Cookie。 3.多個(gè)Cookie條件的匹配: Cookie=cookie1,value1 Cookie=cookie2,value2 同時(shí)匹配名為cookie1且值為value1以及名為cookie2且值為value2的Cookie。 4.忽略大小寫的Cookie匹配: Cookie=cookieName,value, case-insensitive=true: 匹配名為cookieName且值為value,忽略大小寫的Cookie。 5.Cookie存在性的匹配: Cookie=cookieName-exists,true: 匹配包含名為cookieName的Cookie的請(qǐng)求。
### 3.2)過濾器工廠
過濾器用于對(duì)請(qǐng)求和響應(yīng)進(jìn)行處理和修改。它們可以在路由前、路由后或者錯(cuò)誤處理階段執(zhí)行各種操作,例如鑒權(quán)、請(qǐng)求轉(zhuǎn)發(fā)、日志記錄等;
Spring Cloud Gateway提供了兩種類型的過濾器:全局過濾器和路由過濾器。
**全局過濾器(Global Filter)**: 全局過濾器適用于所有的路由規(guī)則,它們能夠?qū)λ羞M(jìn)入網(wǎng)關(guān)的請(qǐng)求進(jìn)行統(tǒng)一處理??梢酝ㄟ^實(shí)現(xiàn)GlobalFilter接口或使用GatewayFilterFactory來創(chuàng)建自定義的全局過濾器。
?```java
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//從請(qǐng)求頭中獲取token
String token = exchange.getRequest().getHeaders().getFirst("token");
//如果請(qǐng)求頭中沒有token就返回權(quán)限不足
if(StringUtils.isEmpty(token)){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}else{
return chain.filter(exchange);
}
}
@Override
public int getOrder() {
return 0; // 過濾器的執(zhí)行順序,值越小優(yōu)先級(jí)越高
}
}
路由過濾器: 是指應(yīng)用于特定路由規(guī)則的過濾器。這些過濾器只會(huì)對(duì)匹配該路由規(guī)則的請(qǐng)求進(jìn)行處理。可以通過在路由配置中添加filters屬性來為特定路由規(guī)則定義過濾器鏈。filters屬性是一個(gè)列表,其中包含要應(yīng)用于該路由規(guī)則的多個(gè)過濾器
Spring Cloud Gateway包含許多內(nèi)置的過濾器工廠:

以下是一些常用且需要掌握的過濾工廠類型及其示例:
1.AddRequestHeader:用于向請(qǐng)求頭部添加指定的頭信息;
filters:
- AddRequestHeader=X-Custom-Header, foo
在請(qǐng)求中添加一個(gè)名為X-Custom-Header的頭部,值為foo。
2.RewritePath:用于修改請(qǐng)求路徑。
filters:
- RewritePath=/oldPath/(?<segment>.*), /newPath/$\{segment}
將匹配/oldPath/開頭的請(qǐng)求路徑,并將其中的/oldPath/替換為/newPath/。
3.SetRequestHeader:用于設(shè)置請(qǐng)求頭部的值
filters:
- SetRequestHeader=X-Forwarded-Host, example.com
將請(qǐng)求頭部中的X-Forwarded-Host設(shè)置為example.com
4.RewriteResponseHeader:用于修改響應(yīng)頭部
filters:
- RewriteResponseHeader=X-Original-Header, X-New-Header
將響應(yīng)頭部中的X-Original-Header替換為X-New-Header
5.RequestRateLimiter:用于實(shí)現(xiàn)請(qǐng)求速率限制
filters:
- RequestRateLimiter=redis-rate-limiter, key-resolver: "#{@userKeyResolver}"
使用Redis作為限流存儲(chǔ),使用自定義的Ke-Resolver來解析限流的鍵
3.3)負(fù)載均衡
spring:
cloud:
gateway:
routes:
- id: order-server
uri: http://127.0.0.1:9001
predicates:
- Path=/order/**
由于上述代碼中, 將order-server的uri地址寫死了, 所以每次訪問order-server的時(shí)候, 都只會(huì)轉(zhuǎn)發(fā)到指定寫死的uri地址, 如果要做到負(fù)載均衡,則必須把網(wǎng)關(guān)工程注冊(cè)到nacos注冊(cè)中心,然后通過服務(wù)名訪問;
將網(wǎng)關(guān)服務(wù)注冊(cè)到nacos中, 使用nacos作為配置中心保存網(wǎng)關(guān)服務(wù)器的配置文件, 參見nacos章節(jié);
修改網(wǎng)關(guān)服務(wù)的配置文件, 使用服務(wù)名稱替換掉寫死的uri地址.
spring:
cloud:
gateway:
routes:
- id: order-server
uri: lb://order-server
predicates:
- Path=/order/**
啟動(dòng)兩個(gè)order-server, 端口號(hào)分別為 9001,和9002.
@RestController
public class OrderController {
@Value("${server.port}")
Integer port;
@GetMapping("/order/index")
public String index(){
return "hello:"+port;
}
}
通過官網(wǎng)訪問order-server, 頁面上輪流顯示9001和9002.
7 Sentinel服務(wù)熔斷和限流
1)Sentinel
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來越重要, 一個(gè)服務(wù)如果不限流控制, 就會(huì)導(dǎo)至這個(gè)服務(wù)越來越擁堵, 甚至宕機(jī), 當(dāng)一個(gè)服務(wù)如果出現(xiàn)問題, 如果不加以處理, 那么就會(huì)逐漸蔓延到其他跟該服務(wù)相關(guān)的其他服務(wù), 而Sentinel是一個(gè)開源的流量控制和熔斷降級(jí)框架, 它就是在幫助開發(fā)人員解決分布式系統(tǒng)中的高并發(fā)、限流、熔斷等問題, 以保證微服務(wù)的穩(wěn)定運(yùn)行.
Sentinel是由阿里巴巴開發(fā)和維護(hù)的開源項(xiàng)目, 它是阿里巴巴技術(shù)團(tuán)隊(duì)在處理大規(guī)模分布式系統(tǒng)中的流量控制和熔斷降級(jí)等問題時(shí)所積累的經(jīng)驗(yàn)的產(chǎn)物.
- 2012 年,Sentinel 誕生,主要功能為入口流量控制。
- 2013-2017 年,Sentinel 在阿里巴巴集團(tuán)內(nèi)部迅速發(fā)展,成為基礎(chǔ)技術(shù)模塊,覆蓋了所有的核心場(chǎng)景。Sentinel 也因此積累了大量的流量歸整場(chǎng)景以及生產(chǎn)實(shí)踐。
- 2018 年,Sentinel 開源,并持續(xù)演進(jìn)。
- 2019 年,Sentinel 朝著多語言擴(kuò)展的方向不斷探索,推出 C++ 原生版本,同時(shí)針對(duì) Service Mesh 場(chǎng)景也推出了 Envoy 集群流量控制支持,以解決 Service Mesh 架構(gòu)下多語言限流的問題。
- 2020 年,推出 Sentinel Go 版本,繼續(xù)朝著云原生方向演進(jìn)。
Sentinel 分為兩個(gè)部分:
- 核心庫(Java 客戶端)不依賴任何框架/庫,能夠運(yùn)行于所有 Java 運(yùn)行時(shí)環(huán)境,同時(shí)對(duì) Dubbo / Spring Cloud 等框架也有較好的支持。
- 控制臺(tái)(Dashboard)基于 Spring Boot 開發(fā),打包后可以直接運(yùn)行,不需要額外的 Tomcat 等應(yīng)用容器。
Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結(jié)合 Dashboard 可以取得最好的效果。
2)基本概念
資源:是 Sentinel 的關(guān)鍵概念。它可以是 Java 應(yīng)用程序中的任何內(nèi)容,例如,由應(yīng)用程序提供的服務(wù),或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù),甚至可以是一段代碼。在接下來的文檔中,我們都會(huì)用資源來描述代碼塊。
只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護(hù)起來。大部分情況下,可以使用方法簽名,URL,甚至服務(wù)名稱作為資源名來標(biāo)示資源。
規(guī)則:圍繞資源的實(shí)時(shí)狀態(tài)設(shè)定的規(guī)則,可以包括流量控制規(guī)則、熔斷降級(jí)規(guī)則以及系統(tǒng)保護(hù)規(guī)則。所有規(guī)則可以動(dòng)態(tài)實(shí)時(shí)調(diào)整。
主要作用:
- 流量控制
- 熔斷降級(jí)
- 系統(tǒng)負(fù)載保護(hù)
使用 Sentinel 來進(jìn)行資源保護(hù),主要分為幾個(gè)步驟:
- 定義資源
- 定義規(guī)則
- 檢驗(yàn)規(guī)則是否生效
先把可能需要保護(hù)的資源定義好,之后再配置規(guī)則。也可以理解為,只要有了資源,我們就可以在任何時(shí)候靈活地定義各種流量控制規(guī)則。在編碼的時(shí)候,只需要考慮這個(gè)代碼是否需要保護(hù),如果需要保護(hù),就將之定義為一個(gè)資源。
3)基本使用
1)下載指定版本的控制臺(tái) jar 包。https://github.com/alibaba/Sentinel/releases
2)cmd跳轉(zhuǎn)到該目錄下: 使用指令運(yùn)行:
java -Dserver.port=8888 -jar sentinel-dashboard-1.8.5.jar
默認(rèn)端口為:8080, -Dserver.port=8888設(shè)置端口.
3)在瀏覽器中訪問sentinel控制臺(tái),進(jìn)入登錄頁面,管理頁面用戶名和密碼:sentinel/sentinel
? 

此時(shí)頁面為空,這是因?yàn)檫€沒有監(jiān)控任何服務(wù)。另外,sentinel是懶加載的,如果服務(wù)沒有被訪問,也看不到該服務(wù)信息。
修改order-server服務(wù)
增加依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置文件:
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.transport.port=8719
# dashboard 地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8888
集群方式啟動(dòng)兩個(gè)order-server, 9001和9002;
然后刷新訪問幾次該服務(wù), 再進(jìn)入到sentinel后臺(tái)查看:

QPS: 每秒請(qǐng)求數(shù),就是說服務(wù)器在一秒的時(shí)間內(nèi)處理了多少個(gè)請(qǐng)求。
3.1)流控規(guī)則
? 
? 資源名: 當(dāng)前服務(wù)需要顯示訪問的接口, 可以是controller中的某個(gè)方法也可以是service中的某個(gè)方法
? 閥值類型: QPS是控制每秒訪問的次數(shù), 并發(fā)線程數(shù)是限制同時(shí)訪問的線程數(shù);
? 單機(jī)閥值: 當(dāng)選擇的是QPS時(shí), 閾值時(shí)1秒鐘內(nèi)訪問的次數(shù), 當(dāng)選擇的是并發(fā)線程數(shù)時(shí), 閾值是同時(shí)訪問的線程數(shù)
? 流控模式: 直接:接口達(dá)到限流條件時(shí),直接限流, 就是單純的對(duì)當(dāng)前資源的流量控制. 關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到閾值時(shí),就限流自己. 只記錄指定鏈路上的流量(指定資源從入口資源進(jìn)來的流量,如果達(dá)到閾值,就可以限流)[api級(jí)別的針對(duì)來源]
? 流控效果: 快速失敗,Sentinel會(huì)直接拒絕后續(xù)的請(qǐng)求,返回錯(cuò)誤信息。這種模式適用于對(duì)系統(tǒng)資源要求較高、不希望超過一定限制的場(chǎng)景; 預(yù)熱模式(Warm Up):預(yù)熱模式在系統(tǒng)剛啟動(dòng)或流量突增時(shí),允許請(qǐng)求的速率緩慢增加,以避免系統(tǒng)被突發(fā)流量壓垮。這種模式適用于系統(tǒng)冷啟動(dòng)或者持續(xù)高流量的場(chǎng)景。排隊(duì)等待模式(QPS/Thread Model):該模式通過隊(duì)列來限制請(qǐng)求處理的并發(fā)數(shù),當(dāng)并發(fā)請(qǐng)求數(shù)達(dá)到設(shè)定的閾值時(shí),多余的請(qǐng)求將會(huì)被放入隊(duì)列中等待處理。這種模式適用于對(duì)系統(tǒng)并發(fā)數(shù)有限制需求的場(chǎng)景,可以平滑處理突發(fā)流量。
3.2)熔斷降級(jí)規(guī)則
? Sentinel除了流量控制以外,對(duì)調(diào)用鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級(jí)也是保障高可用的重要措施之一。由于調(diào)用關(guān)系的復(fù)雜性,如果調(diào)用鏈路中的某個(gè)資源不穩(wěn)定,最終會(huì)導(dǎo)致請(qǐng)求發(fā)生堆積。Sentinel 熔斷降級(jí)會(huì)在調(diào)用鏈路中某個(gè)資源出現(xiàn)不穩(wěn)定狀態(tài)時(shí)(例如調(diào)用超時(shí)或異常比例升高),對(duì)這個(gè)資源的調(diào)用進(jìn)行限制,讓請(qǐng)求快速失敗,避免影響到其它的資源而導(dǎo)致級(jí)聯(lián)錯(cuò)誤, 最后造成微服務(wù)雪崩.
? 當(dāng)資源被降級(jí)后,在接下來的降級(jí)時(shí)間窗口之內(nèi),對(duì)該資源的調(diào)用都自動(dòng)熔斷(默認(rèn)行為是拋出 DegradeException)。
? 
? 熔斷策略:
? 慢調(diào)比: 就是調(diào)用使用該資源, 資源響應(yīng)速度很慢; 最大RT意思是最大響應(yīng)時(shí)間, 比例閾值: 這個(gè)最大響應(yīng)時(shí)間的請(qǐng)求比例的最大值;
? 異常比例: 訪問資源出現(xiàn)異常的比例; 比例閾值: 出現(xiàn)異常的比例最大值;
? 異常數(shù): 當(dāng)資源近 1 分鐘的異常數(shù)目;
? 熔斷時(shí)長: 限制該資源使用的時(shí)長, 超過這個(gè)時(shí)長后, 進(jìn)入探測(cè)恢復(fù)狀態(tài)(HALF-OPEN)階段,即接下來的一個(gè)請(qǐng)求響應(yīng)時(shí)間小于rt,則熔斷結(jié)束,否則會(huì)再次被熔斷。
? 最小請(qǐng)求數(shù): 觸發(fā)熔斷的最小請(qǐng)求數(shù);
? 統(tǒng)計(jì)時(shí)長: 設(shè)置多長時(shí)間作為一個(gè)統(tǒng)計(jì)周期, 判斷是否進(jìn)入熔斷;
4)限流和熔斷處理
@SentinelResource注解
可以靈活用該注解來定義需要控制的資源, 可以在類或者接口上, 也可以是在方法上;
? value: 定義資源名稱;
? blockHandler / fallback: 指定當(dāng)發(fā)生熔斷或者限流時(shí)的處理函數(shù)
? blockHandlerClass: 指定當(dāng)發(fā)生熔斷或者限流時(shí)的處理函數(shù)所在的類, 這樣可以定義全局的限流熔斷的處理方法;
@GetMapping("/order/index")
@SentinelResource(value = "byUrl", blockHandler = "handleException")
public String index(Integer num) throws InterruptedException {
if(num>3){
Thread.sleep(600);
}
return "hello:"+port;
}
public String handleException(Integer num,BlockException e){
return "擁堵了, 心里沒數(shù)嗎?";
}
? fallback和blockHandler 作用相同, 但是blockHandler指定的方法形參和資源方法相同外, 還必須入?yún)lockException異常;
? fallback值需要形參相同即可;
? 優(yōu)先級(jí)上, 一開始幾次是fallback,后面限流開始調(diào)用blockHandler。
5)配置持久化
1)下載sentinel-dashborad源碼;
2)修改pom.xml文件
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!--<scope>test</scope>-->
</dependency>
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8888
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=127.0.0.1:8848
spring.cloud.sentinel.datasource.ds1.nacos.data-id=sentinel-service
spring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow

流控規(guī)則的寫法:
[
{
// 資源名
"resource": "/test",
// 針對(duì)來源,若為 default 則不區(qū)分調(diào)用來源
"limitApp": "default",
// 限流閾值類型(1:QPS;0:并發(fā)線程數(shù))
"grade": 1,
// 閾值
"count": 1,
// 是否是集群模式
"clusterMode": false,
// 流控效果(0:快速失敗;1:Warm Up(預(yù)熱模式);2:排隊(duì)等待)
"controlBehavior": 0,
// 流控模式(0:直接;1:關(guān)聯(lián);2:鏈路)
"strategy": 0,
// 預(yù)熱時(shí)間(秒,預(yù)熱模式需要此參數(shù))
"warmUpPeriodSec": 10,
// 超時(shí)時(shí)間(排隊(duì)等待模式需要此參數(shù))
"maxQueueingTimeMs": 500,
// 關(guān)聯(lián)資源、入口資源(關(guān)聯(lián)、鏈路模式)
"refResource": "rrr"
}
]
spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848
spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel-order2-server
spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade
熔斷降級(jí)的寫法:
[
{
// 資源名
"resource": "/test1",
"limitApp": "default",
// 熔斷策略(0:慢調(diào)用比例,1:異常比率,2:異常計(jì)數(shù))
"grade": 0,
// 最大RT、比例閾值、異常數(shù)
"count": 200,
// 慢調(diào)用比例閾值,僅慢調(diào)用比例模式有效(1.8.0 引入)
"slowRatioThreshold": 0.2,
// 最小請(qǐng)求數(shù)
"minRequestAmount": 5,
// 當(dāng)單位統(tǒng)計(jì)時(shí)長(類中默認(rèn)1000)
"statIntervalMs": 1000,
// 熔斷時(shí)長
"timeWindow": 10
}
]
8 分布式CAP理論
1)CAP的介紹
分布式CAP理論是由計(jì)算機(jī)科學(xué)家Eric Brewer提出的,它描述了在分布式系統(tǒng)中三個(gè)重要屬性之間的沖突。這三個(gè)屬性是一致性(Consistency)、可用性(Availability)和分區(qū)容錯(cuò)性(Partition tolerance)。根據(jù)CAP理論,一個(gè)分布式系統(tǒng)最多只能同時(shí)滿足其中的兩個(gè)屬性,無法同時(shí)滿足全部三個(gè)。
一致性(Consistency)即更新操作成功并返回客戶端完成后,所有節(jié)點(diǎn)在同一時(shí)間的數(shù)據(jù)完全一致。對(duì)于一致性,可以分為從客戶端和服務(wù)端兩個(gè)不同的視角。
從客戶端來看,一致性主要指的是多并發(fā)訪問時(shí)更新過的數(shù)據(jù)如何獲取的問題。從服務(wù)端來看,則是更新如何復(fù)制分布到整個(gè)系統(tǒng),以保證數(shù)據(jù)最終一致。一致性是因?yàn)橛胁l(fā)讀寫才有的問題,因此在理解一致性的問題時(shí),一定要注意結(jié)合考慮并發(fā)讀寫的場(chǎng)景。
從客戶端角度,多進(jìn)程并發(fā)訪問時(shí),更新過的數(shù)據(jù)在不同進(jìn)程如何獲取的不同策略,決定了不同的一致性。對(duì)于關(guān)系型數(shù)據(jù)庫,要求更新過的數(shù)據(jù)能被后續(xù)的訪問都能看到,這是強(qiáng)一致性。如果能容忍后續(xù)的部分或者全部訪問不到,則是弱一致性。如果經(jīng)過一段時(shí)間后要求能訪問到更新后的數(shù)據(jù),則是最終一致性。
可用性(Availability)即服務(wù)一直可用,而且是正常響應(yīng)時(shí)間。對(duì)于一個(gè)可用性的分布式系統(tǒng),每一個(gè)非故障的節(jié)點(diǎn)必須對(duì)每一個(gè)請(qǐng)求作出響應(yīng)。也就是,該系統(tǒng)使用的任何算法必須最終終止。當(dāng)同時(shí)要求分區(qū)容忍性時(shí),這是一個(gè)很強(qiáng)的定義:即使是嚴(yán)重的網(wǎng)絡(luò)錯(cuò)誤,每個(gè)請(qǐng)求必須終止。
好的可用性主要是指系統(tǒng)能夠很好的為用戶服務(wù),不出現(xiàn)用戶操作失敗或者訪問超時(shí)等用戶體驗(yàn)不好的情況。可用性通常情況下可用性和分布式數(shù)據(jù)冗余,負(fù)載均衡等有著很大的關(guān)聯(lián)。
分區(qū)容錯(cuò)性(Partition tolerance)即分布式系統(tǒng)在遇到某節(jié)點(diǎn)或網(wǎng)絡(luò)分區(qū)故障的時(shí)候,仍然能夠?qū)ν馓峁M足一致性和可用性的服務(wù)。分區(qū)容錯(cuò)性和擴(kuò)展性緊密相關(guān)。在分布式應(yīng)用中,可能因?yàn)橐恍┓植际降脑驅(qū)е孪到y(tǒng)無法正常運(yùn)轉(zhuǎn)。好的分區(qū)容錯(cuò)性要求能夠使應(yīng)用雖然是一個(gè)分布式系統(tǒng),而看上去卻好像是在一個(gè)可以運(yùn)轉(zhuǎn)正常的整體。比如現(xiàn)在的分布式系統(tǒng)中有某一個(gè)或者幾個(gè)機(jī)器宕掉了,其他剩下的機(jī)器還能夠正常運(yùn)轉(zhuǎn)滿足系統(tǒng)需求,或者是機(jī)器之間有網(wǎng)絡(luò)異常,將分布式系統(tǒng)分隔未獨(dú)立的幾個(gè)部分,各個(gè)部分還能維持分布式系統(tǒng)的運(yùn)作,這樣就具有好的分區(qū)容錯(cuò)性。
2)CAP的證明

如上圖,是我們證明CAP的基本場(chǎng)景,網(wǎng)絡(luò)中有兩個(gè)節(jié)點(diǎn)N1和N2,可以簡單的理解N1和N2分別是兩臺(tái)計(jì)算機(jī),他們之間網(wǎng)絡(luò)可以連通,N1中有一個(gè)應(yīng)用程序A,和一個(gè)數(shù)據(jù)庫V,N2也有一個(gè)應(yīng)用程序B2和一個(gè)數(shù)據(jù)庫V?,F(xiàn)在,A和B是分布式系統(tǒng)的兩個(gè)部分,V是分布式系統(tǒng)的數(shù)據(jù)存儲(chǔ)的兩個(gè)子數(shù)據(jù)庫。
在滿足一致性的時(shí)候,N1和N2中的數(shù)據(jù)是一樣的,V0=V0。在滿足可用性的時(shí)候,用戶不管是請(qǐng)求N1或者N2,都會(huì)得到立即響應(yīng)。在滿足分區(qū)容錯(cuò)性的情況下,N1和N2有任何一方宕機(jī),或者網(wǎng)絡(luò)不通的時(shí)候,都不會(huì)影響N1和N2彼此之間的正常運(yùn)作。
如上圖,是分布式系統(tǒng)正常運(yùn)轉(zhuǎn)的流程,用戶向N1機(jī)器請(qǐng)求數(shù)據(jù)更新,程序A更新數(shù)據(jù)庫Vo為V1,分布式系統(tǒng)將數(shù)據(jù)進(jìn)行同步操作M,將V1同步的N2中V0,使得N2中的數(shù)據(jù)V0也更新為V1,N2中的數(shù)據(jù)再響應(yīng)N2的請(qǐng)求。
這里,可以定義N1和N2的數(shù)據(jù)庫V之間的數(shù)據(jù)是否一樣為一致性;外部對(duì)N1和N2的請(qǐng)求響應(yīng)為可用行;N1和N2之間的網(wǎng)絡(luò)環(huán)境為分區(qū)容錯(cuò)性。這是正常運(yùn)作的場(chǎng)景,也是理想的場(chǎng)景,然而現(xiàn)實(shí)是殘酷的,當(dāng)錯(cuò)誤發(fā)生的時(shí)候,一致性和可用性還有分區(qū)容錯(cuò)性,是否能同時(shí)滿足,還是說要進(jìn)行取舍呢?
作為一個(gè)分布式系統(tǒng),它和單機(jī)系統(tǒng)的最大區(qū)別,就在于網(wǎng)絡(luò),現(xiàn)在假設(shè)一種極端情況,N1和N2之間的網(wǎng)絡(luò)斷開了,我們要支持這種網(wǎng)絡(luò)異常,相當(dāng)于要滿足分區(qū)容錯(cuò)性,能不能同時(shí)滿足一致性和響應(yīng)性呢?還是說要對(duì)他們進(jìn)行取舍。
假設(shè)在N1和N2之間網(wǎng)絡(luò)斷開的時(shí)候,有用戶向N1發(fā)送數(shù)據(jù)更新請(qǐng)求,那N1中的數(shù)據(jù)V0將被更新為V1,由于網(wǎng)絡(luò)是斷開的,所以分布式系統(tǒng)同步操作M,所以N2中的數(shù)據(jù)依舊是V0;這個(gè)時(shí)候,有用戶向N2發(fā)送數(shù)據(jù)讀取請(qǐng)求,由于數(shù)據(jù)還沒有進(jìn)行同步,應(yīng)用程序沒辦法立即給用戶返回最新的數(shù)據(jù)V1,怎么辦呢?有二種選擇,第一,犧牲數(shù)據(jù)一致性,響應(yīng)舊的數(shù)據(jù)V0給用戶;第二,犧牲可用性,阻塞等待,直到網(wǎng)絡(luò)連接恢復(fù),數(shù)據(jù)更新操作M完成之后,再給用戶響應(yīng)最新的數(shù)據(jù)V1。
這個(gè)過程,證明了要滿足分區(qū)容錯(cuò)性的分布式系統(tǒng),只能在一致性和可用性兩者中,選擇其中一個(gè)。
3)CAP的權(quán)衡
通過CAP理論,我們知道無法同時(shí)滿足一致性、可用性和分區(qū)容錯(cuò)性這三個(gè)特性,那要舍棄哪個(gè)呢?
CA without P:如果不要求P(不允許分區(qū)),則C(強(qiáng)一致性)和A(可用性)是可以保證的。但其實(shí)分區(qū)不是你想不想的問題,而是始終會(huì)存在,因此CA的系統(tǒng)更多的是允許分區(qū)后各子系統(tǒng)依然保持CA。
CP without A:如果不要求A(可用),相當(dāng)于每個(gè)請(qǐng)求都需要在Server之間強(qiáng)一致,而P(分區(qū))會(huì)導(dǎo)致同步時(shí)間無限延長,如此CP也是可以保證的。很多傳統(tǒng)的數(shù)據(jù)庫分布式事務(wù)都屬于這種模式。
AP wihtout C:要高可用并允許分區(qū),則需放棄一致性。一旦分區(qū)發(fā)生,節(jié)點(diǎn)之間可能會(huì)失去聯(lián)系,為了高可用,每個(gè)節(jié)點(diǎn)只能用本地?cái)?shù)據(jù)提供服務(wù),而這樣會(huì)導(dǎo)致全局?jǐn)?shù)據(jù)的不一致性?,F(xiàn)在眾多的NoSQL都屬于此類。
對(duì)于多數(shù)大型互聯(lián)網(wǎng)應(yīng)用的場(chǎng)景,主機(jī)眾多、部署分散,而且現(xiàn)在的集群規(guī)模越來越大,所以節(jié)點(diǎn)故障、網(wǎng)絡(luò)故障是常態(tài),而且要保證服務(wù)可用性達(dá)到N個(gè)9,即保證P和A,舍棄C(退而求其次保證最終一致性)。雖然某些地方會(huì)影響客戶體驗(yàn),但沒達(dá)到造成用戶流程的嚴(yán)重程度。
對(duì)于涉及到錢財(cái)這樣不能有一絲讓步的場(chǎng)景,C必須保證。網(wǎng)絡(luò)發(fā)生故障寧可停止服務(wù),這是保證CA,舍棄P。貌似這幾年國內(nèi)銀行業(yè)發(fā)生了不下10起事故,但影響面不大,報(bào)到也不多,廣大群眾知道的少。還有一種是保證CP,舍棄A。例如網(wǎng)絡(luò)故障事只讀不寫。
孰優(yōu)孰略,沒有定論,只能根據(jù)場(chǎng)景定奪,適合的才是最好的。
9 分布式鎖
1)介紹
隨著技術(shù)快速發(fā)展,數(shù)據(jù)規(guī)模增大,分布式系統(tǒng)越來越普及,一個(gè)應(yīng)用往往會(huì)部署在多臺(tái)機(jī)器上(多節(jié)點(diǎn)),在有些場(chǎng)景中,為了保證數(shù)據(jù)不重復(fù),要求在同一時(shí)刻,同一任務(wù)只在一個(gè)節(jié)點(diǎn)上運(yùn)行,即保證某一方法同一時(shí)刻只能被一個(gè)線程執(zhí)行。在單機(jī)環(huán)境中,應(yīng)用是在同一進(jìn)程下的,只需要保證單進(jìn)程多線程環(huán)境中的線程安全性,通過 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并發(fā)包下一些線程安全的類等就可以做到。而在多機(jī)部署環(huán)境中,不同機(jī)器不同進(jìn)程,就需要在多進(jìn)程下保證線程的安全性了。因此,分布式鎖應(yīng)運(yùn)而生。
分布式鎖需滿足四個(gè)條件
首先,為了確保分布式鎖可用,我們至少要確保鎖的實(shí)現(xiàn)同時(shí)滿足以下四個(gè)條件:
- 互斥性。在任意時(shí)刻,只有一個(gè)客戶端能持有鎖。
- 不會(huì)發(fā)生死鎖。即使有一個(gè)客戶端在持有鎖的期間崩潰而沒有主動(dòng)解鎖,也能保證后續(xù)其他客戶端能加鎖。
- 解鈴還須系鈴人。加鎖和解鎖必須是同一個(gè)客戶端,客戶端自己不能把別人加的鎖給解了,即不能誤解鎖。
- 具有容錯(cuò)性。只要大多數(shù)Redis節(jié)點(diǎn)正常運(yùn)行,客戶端就能夠獲取和釋放鎖。
常見的分布式的實(shí)現(xiàn)方式:
| 分類 | 方案 | 實(shí)現(xiàn)原理 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|---|
| 基于數(shù)據(jù)庫 | 基于mysql 表唯一索引 | 1.表增加唯一索引 2.加鎖:執(zhí)行insert語句,若報(bào)錯(cuò),則表明加鎖失敗 3.解鎖:執(zhí)行delete語句 | 完全利用DB現(xiàn)有能力,實(shí)現(xiàn)簡單 | 1.鎖無超時(shí)自動(dòng)失效機(jī)制,有死鎖風(fēng)險(xiǎn) 2.不支持鎖重入,不支持阻塞等待 3.操作數(shù)據(jù)庫開銷大,性能不高 |
| 基于MongoDB findAndModify原子操作 | 1.加鎖:執(zhí)行findAndModify原子命令查找document,若不存在則新增 2.解鎖:刪除document | 實(shí)現(xiàn)也很容易,較基于MySQL唯一索引的方案,性能要好很多 | 1.大部分公司數(shù)據(jù)庫用MySQL,可能缺乏相應(yīng)的MongoDB運(yùn)維、開發(fā)人員 2.鎖無超時(shí)自動(dòng)失效機(jī)制 | |
| 基于分布式協(xié)調(diào)系統(tǒng) | 基于ZooKeeper | 1.加鎖:在/lock目錄下創(chuàng)建臨時(shí)有序節(jié)點(diǎn),判斷創(chuàng)建的節(jié)點(diǎn)序號(hào)是否最小。若是,則表示獲取到鎖;否,則則watch /lock目錄下序號(hào)比自身小的前一個(gè)節(jié)點(diǎn) 2.解鎖:刪除節(jié)點(diǎn) | 1.由zk保障系統(tǒng)高可用 2.Curator框架已原生支持系列分布式鎖命令,使用簡單 | 需單獨(dú)維護(hù)一套zk集群,維保成本高 |
| 基于緩存 | 基于redis命令 | 1. 加鎖:執(zhí)行setnx,若成功再執(zhí)行expire添加過期時(shí)間 2. 解鎖:執(zhí)行delete命令 | 實(shí)現(xiàn)簡單,相比數(shù)據(jù)庫和分布式系統(tǒng)的實(shí)現(xiàn),該方案最輕,性能最好 | 1.setnx和expire分2步執(zhí)行,非原子操作;若setnx執(zhí)行成功,但expire執(zhí)行失敗,就可能出現(xiàn)死鎖 2.delete命令存在誤刪除非當(dāng)前線程持有的鎖的可能 3.不支持阻塞等待、不可重入 |
| 基于redis Lua腳本能力 | 1. 加鎖:執(zhí)行SET lock_name random_value EX seconds NX 命令 2. 解鎖:執(zhí)行Lua腳本,釋放鎖時(shí)驗(yàn)證random_value -- ARGV[1]為random_value, KEYS[1]為lock_nameif redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0 end | 同上;實(shí)現(xiàn)邏輯上也更嚴(yán)謹(jǐn),除了單點(diǎn)問題,生產(chǎn)環(huán)境采用用這種方案,問題也不大。 | 不支持鎖重入,不支持阻塞等待 |
表格中對(duì)比了幾種常見的方案,redis+lua基本可應(yīng)付工作中分布式鎖的需求。
2)基本使用
模擬:3臺(tái)order-server服務(wù)器同時(shí)賣20個(gè)商品, 商品數(shù)量保存在redis中;
業(yè)務(wù)邏輯: order-server從redis中取出商品數(shù)量, 如果數(shù)量大于10, 那么就賣出一個(gè)商品, 然后講redis中的數(shù)量-1, 如果取出的數(shù)量等于0, 代表商品賣完了, 返回商品搶購結(jié)束!
依賴
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.1</version>
</dependency>
基本配置
spring:
redis:
host: 175.24.205.196
port: 6379
password: boge123123
database: 3
啟動(dòng)網(wǎng)關(guān)服務(wù);
啟動(dòng)3個(gè)訂單服務(wù);
不加鎖:
普通鎖:
分布式鎖:
redisson分布式鎖流程:

3)redisson中的鎖
普通鎖
RLock lock = redisson.getLock("myLock");
lock.lock();
lock.lock(10, TimeUnit.SECONDS);
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
公平鎖
RLock lock = redisson.getFairLock("myLock");
lock.lock();
lock.lock(10, TimeUnit.SECONDS);
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
多重鎖
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);
multiLock.lock();
multiLock.lock(10, TimeUnit.SECONDS);
boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
multiLock.unlock();
}
}
讀寫鎖
RReadWriteLock rwlock = redisson.getReadWriteLock("myLock");
RLock lock = rwlock.readLock();
RLock lock = rwlock.writeLock();
lock.lock();
lock.lock(10, TimeUnit.SECONDS);
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
信號(hào)量
RSemaphore semaphore = redisson.getSemaphore("mySemaphore");
semaphore.acquire();
semaphore.acquire(10);
// or try to acquire permit
boolean res = semaphore.tryAcquire();
// or try to acquire permit or wait up to 15 seconds
boolean res = semaphore.tryAcquire(15, TimeUnit.SECONDS);
// or try to acquire 10 permit
boolean res = semaphore.tryAcquire(10);
// or try to acquire 10 permits or wait up to 15 seconds
boolean res = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
semaphore.release();
}
}
10 分布式事務(wù)
1)事務(wù)的介紹
嚴(yán)格意義上的事務(wù)實(shí)現(xiàn)應(yīng)該是具備原子性、一致性、隔離性和持久性,簡稱 ACID。
- 原子性(Atomicity),可以理解為一個(gè)事務(wù)內(nèi)的所有操作要么都執(zhí)行,要么都不執(zhí)行。
- 一致性(Consistency),可以理解為數(shù)據(jù)是滿足完整性約束的,也就是不會(huì)存在中間狀態(tài)的數(shù)據(jù),比如你賬上有400,我賬上有100,你給我打200塊,此時(shí)你賬上的錢應(yīng)該是200,我賬上的錢應(yīng)該是300,不會(huì)存在我賬上錢加了,你賬上錢沒扣的中間狀態(tài)。
- 隔離性(Isolation),指的是多個(gè)事務(wù)并發(fā)執(zhí)行的時(shí)候不會(huì)互相干擾,即一個(gè)事務(wù)內(nèi)部的數(shù)據(jù)對(duì)于其他事務(wù)來說是隔離的。
- 持久性(Durability),指的是一個(gè)事務(wù)完成了之后數(shù)據(jù)就被永遠(yuǎn)保存下來,之后的其他操作或故障都不會(huì)對(duì)事務(wù)的結(jié)果產(chǎn)生影響。
分布式事務(wù)是指在分布式系統(tǒng)中涉及到多個(gè)獨(dú)立的操作或數(shù)據(jù)更新時(shí),保證這些操作要么全部成功要么全部失敗的機(jī)制。它是為了解決分布式系統(tǒng)中數(shù)據(jù)一致性問題而提出的一種技術(shù)手段。
在傳統(tǒng)的單體應(yīng)用中,數(shù)據(jù)庫事務(wù)能夠確保操作的原子性、一致性、隔離性和持久性(ACID)特性。然而,在分布式系統(tǒng)中,由于存在多個(gè)數(shù)據(jù)庫實(shí)例、服務(wù)節(jié)點(diǎn)或存儲(chǔ)介質(zhì),事務(wù)的隔離性和一致性變得更加復(fù)雜。
為了解決這些挑戰(zhàn),有幾種常見的分布式事務(wù)模型被提出,包括兩階段提交(2PC)、三階段提交(3PC)、補(bǔ)償事務(wù)等。這些模型都試圖通過協(xié)調(diào)各參與節(jié)點(diǎn)的狀態(tài)來達(dá)到全局一致性。
然而,分布式事務(wù)也帶來了一些性能和可用性的問題。由于需要協(xié)調(diào)多個(gè)節(jié)點(diǎn),會(huì)增加通信開銷和延遲;同時(shí),若一個(gè)節(jié)點(diǎn)失效,整個(gè)事務(wù)可能會(huì)被阻塞或回滾。因此,在設(shè)計(jì)分布式系統(tǒng)時(shí),需要權(quán)衡事務(wù)一致性要求和性能可擴(kuò)展性。
2)常用的分布式事務(wù)解決方案
a)XA 模式之兩階段提交
? 兩階段提交又稱2PC(two-phase commit protocol),2pc是一個(gè)非常經(jīng)典的強(qiáng)一致、中心化的原子提交協(xié)議。這里所說的中心化是指協(xié)議中有兩類節(jié)點(diǎn):一個(gè)是中心化協(xié)調(diào)者節(jié)點(diǎn)和N個(gè)參與者節(jié)點(diǎn)。
? 準(zhǔn)備階段: 事務(wù)協(xié)調(diào)者向所有事務(wù)參與者發(fā)送事務(wù)內(nèi)容,詢問是否可以提交事務(wù),并等待參與者回復(fù)。事務(wù)參與者收到事務(wù)內(nèi)容,開始執(zhí)行事務(wù)操作,講 undo 和 redo 信息記入事務(wù)日志中(但此時(shí)并不提交事務(wù))。如果參與者執(zhí)行成功,給協(xié)調(diào)者回復(fù)yes,表示可以進(jìn)行事務(wù)提交。如果執(zhí)行失敗,給協(xié)調(diào)者回復(fù)no,表示不可提交。
? 提交階段: 如果協(xié)調(diào)者收到了參與者的失敗信息或超時(shí)信息,直接給所有參與者發(fā)送回滾(rollback)信息進(jìn)行事務(wù)回滾,否則發(fā)送提交(commit)信息。參與者根據(jù)協(xié)調(diào)者的指令執(zhí)行提交或者回滾操作,釋放所有事務(wù)處理過程中使用的鎖資源。(注意:必須在最后階段釋放鎖資源)
? 會(huì)遇到的一些問題
? 性能問題
? 從流程上我們可以看得出,其最大缺點(diǎn)就在于它的執(zhí)行過程中間,節(jié)點(diǎn)都處于阻塞狀態(tài)。各個(gè)操作數(shù)據(jù)庫的節(jié)點(diǎn)此時(shí)都占用著數(shù)據(jù)庫資源,只有當(dāng)所有節(jié)點(diǎn)準(zhǔn)備完畢,事務(wù)協(xié)調(diào)者才會(huì)通知進(jìn)行全局提交,參與者進(jìn)行本地事務(wù)提交后才會(huì)釋放資源。這樣的過程會(huì)比較漫長,對(duì)性能影響比較大。
? 協(xié)調(diào)者單點(diǎn)故障問題
? 事務(wù)協(xié)調(diào)者是整個(gè)XA模型的核心,一旦事務(wù)協(xié)調(diào)者節(jié)點(diǎn)掛掉,會(huì)導(dǎo)致參與者收不到提交或回滾的通知,從而導(dǎo)致參與者節(jié)點(diǎn)始終處于事務(wù)無法完成的中間狀態(tài)。
? 丟失消息導(dǎo)致的數(shù)據(jù)不一致問題
? 在第二個(gè)階段,如果發(fā)生局部網(wǎng)絡(luò)問題,一部分事務(wù)參與者收到了提交消息,另一部分事務(wù)參與者沒收到提交消息,那么就會(huì)導(dǎo)致節(jié)點(diǎn)間數(shù)據(jù)的不一致問題。
? 2PC 方案實(shí)現(xiàn)起來簡單,基于上面提到的兩階段提交協(xié)議中會(huì)遇到的問題;
b)XA 模式之三階段提交
? 三階段提交又稱3PC, 三階段提交是在二階段提交上的改進(jìn)版本,其在兩階段提交的基礎(chǔ)上增加了 CanCommit階段,并加入了超時(shí)機(jī)制。同時(shí)在協(xié)調(diào)者和參與者中都引入超時(shí)機(jī)制。三階段將二階段的準(zhǔn)備階段拆分為2個(gè)階段,插入了一個(gè)preCommit階段,以此來處理原先二階段,參與者準(zhǔn)備后,參與者發(fā)生崩潰或錯(cuò)誤,導(dǎo)致參與者無法知曉是否提交或回滾的不確定狀態(tài)所引起的延時(shí)問題。
? 階段一(canCommit): 協(xié)調(diào)者向所有參與者發(fā)出包含事務(wù)內(nèi)容的 canCommit 請(qǐng)求,詢問是否可以提交事務(wù),并等待所有參與者答復(fù)。參與者收到 canCommit 請(qǐng)求后,如果認(rèn)為可以執(zhí)行事務(wù)操作,則反饋 yes 并進(jìn)入預(yù)備狀態(tài),否則反饋 no
? 階段二(PreCommit): 階段一中,如果所有的參與者都返回Yes的話,那么就會(huì)進(jìn)入PreCommit階段進(jìn)行事務(wù)預(yù)提交。此時(shí)分布式事務(wù)協(xié)調(diào)者會(huì)向所有的參與者節(jié)點(diǎn)發(fā)送PreCommit請(qǐng)求,參與者收到后開始執(zhí)行事務(wù)操作,并將Undo和Redo信息記錄到事務(wù)日志中。參與者執(zhí)行完事務(wù)操作后(此時(shí)屬于未提交事務(wù)的狀態(tài)),就會(huì)向協(xié)調(diào)者反饋“Ack”表示我已經(jīng)準(zhǔn)備好提交了,并等待協(xié)調(diào)者的下一步指令。如果階段一中有任何一個(gè)參與者節(jié)點(diǎn)返回的結(jié)果是No響應(yīng),或者協(xié)調(diào)者在等待參與者節(jié)點(diǎn)反饋的過程中因掛掉而超時(shí)(2PC中只有協(xié)調(diào)者可以超時(shí),參與者沒有超時(shí)機(jī)制)。整個(gè)分布式事務(wù)就會(huì)中斷,協(xié)調(diào)者就會(huì)向所有的參與者發(fā)送“abort”請(qǐng)求。
? 階段三(do Commit): 該階段進(jìn)行真正的事務(wù)提交,在階段二中如果所有的參與者節(jié)點(diǎn)都可以進(jìn)行PreCommit提交,那么協(xié)調(diào)者就會(huì)從“預(yù)提交狀態(tài)” 轉(zhuǎn)變?yōu)?“提交狀態(tài)”。然后向所有的參與者節(jié)點(diǎn)發(fā)送"doCommit"請(qǐng)求,參與者節(jié)點(diǎn)在收到提交請(qǐng)求后就會(huì)各自執(zhí)行事務(wù)提交操作,并向協(xié)調(diào)者節(jié)點(diǎn)反饋“Ack”消息,協(xié)調(diào)者收到所有參與者的Ack消息后完成事務(wù)。
? 相比較2PC而言,3PC對(duì)于協(xié)調(diào)者(Coordinator)和參與者(Partcipant)都設(shè)置了超時(shí)時(shí)間,而2PC只有協(xié)調(diào)者才擁有超時(shí)機(jī)制。這解決了一個(gè)什么問題呢?這個(gè)優(yōu)化點(diǎn),主要是避免了參與者在長時(shí)間無法與協(xié)調(diào)者節(jié)點(diǎn)通訊(協(xié)調(diào)者掛掉了)的情況下,無法釋放資源的問題,因?yàn)閰⑴c者自身擁有超時(shí)機(jī)制會(huì)在超時(shí)后,自動(dòng)進(jìn)行本地commit從而進(jìn)行釋放資源。而這種機(jī)制也側(cè)面降低了整個(gè)事務(wù)的阻塞時(shí)間和范圍。
? 另外,通過CanCommit、PreCommit、DoCommit三個(gè)階段的設(shè)計(jì),相較于2PC而言,多設(shè)置了一個(gè)緩沖階段保證了在最后提交階段之前各參與節(jié)點(diǎn)的狀態(tài)是一致的。
? 以上就是3PC相對(duì)于2PC的一個(gè)提高(相對(duì)緩解了2PC中的前兩個(gè)問題),但是3PC依然沒有完全解決數(shù)據(jù)不一致的問題。假如在 DoCommit 過程,參與者A無法接收協(xié)調(diào)者的通信,那么參與者A會(huì)自動(dòng)提交,但是提交失敗了,其他參與者成功了,此時(shí)數(shù)據(jù)就會(huì)不一致。
c) AT模式
? AT 模式是一種無侵入的分布式事務(wù)解決方案。在 AT 模式下,用戶只需關(guān)注自己的“業(yè)務(wù) SQL”,用戶的 “業(yè)務(wù) SQL” 作為一階段,Seata 框架會(huì)自動(dòng)生成事務(wù)的二階段提交和回滾操作。
? 在一階段,Seata 會(huì)攔截“業(yè)務(wù) SQL”,首先解析 SQL 語義,找到“業(yè)務(wù) SQL”要更新的業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)被更新前,將其保存成“before image”,然后執(zhí)行“業(yè)務(wù) SQL”更新業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)更新之后,再將其保存成“after image”,最后生成行鎖。以上操作全部在一個(gè)數(shù)據(jù)庫事務(wù)內(nèi)完成,這樣保證了一階段操作的原子性。
? 把業(yè)務(wù)數(shù)據(jù)在更新前后的數(shù)據(jù)鏡像組織成回滾日志,將業(yè)務(wù)數(shù)據(jù)的更新和回滾日志在同一個(gè)本地事務(wù)中提交,分別插入到業(yè)務(wù)表和 UNDO_LOG 表中。
? 二階段如果是提交的話,因?yàn)椤皹I(yè)務(wù) SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可;
? 二階段如果是回滾的話,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務(wù) SQL”,還原業(yè)務(wù)數(shù)據(jù)。回滾方式便是用“before image”還原業(yè)務(wù)數(shù)據(jù);但在還原前要首先要校驗(yàn)臟寫,對(duì)比“數(shù)據(jù)庫當(dāng)前業(yè)務(wù)數(shù)據(jù)”和 “after image”,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務(wù)數(shù)據(jù),如果不一致就說明有臟寫,出現(xiàn)臟寫就需要轉(zhuǎn)人工處理;
? AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動(dòng)生成,用戶只需編寫“業(yè)務(wù) SQL”,便能輕松接入分布式事務(wù),AT 模式是一種對(duì)業(yè)務(wù)無任何侵入的分布式事務(wù)解決方案。但AT模式存在的不足就是當(dāng)操作的數(shù)據(jù)是共享型數(shù)據(jù),會(huì)存在臟寫的問題,所以如果是用戶獨(dú)有數(shù)據(jù)可以使用AT模式。
d)TCC模式
? TCC方案其實(shí)是兩階段提交的一種改進(jìn)。分成了Try、Confirm、Cancel三個(gè)操作。事務(wù)發(fā)起方在一階段執(zhí)行 Try 方式,在二階段提交執(zhí)行 Confirm 方法,二階段回滾執(zhí)行 Cancel 方法。
- Try部分完成業(yè)務(wù)的準(zhǔn)備工作
- confirm部分完成業(yè)務(wù)的提交
- cancel部分完成事務(wù)的回滾
TCC 模式,不依賴于底層數(shù)據(jù)資源的事務(wù)支持:
? 一階段 prepare 行為:調(diào)用 自定義 的 prepare 邏輯。
? 二階段 commit 行為:調(diào)用 自定義 的 commit 邏輯。
? 二階段 rollback 行為:調(diào)用 自定義 的 rollback 邏輯。
用戶接入 TCC 模式,最重要的事情就是考慮如何將業(yè)務(wù)模型拆成 2 階段,實(shí)現(xiàn)成 TCC 的 3 個(gè)方法,并且保證 Try 成功 Confirm 一定能成功。相對(duì)于 AT 模式,TCC 模式對(duì)業(yè)務(wù)代碼有一定的侵入性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會(huì)比 AT 模式高很多。
e)SAGA模式
? Saga模式是SEATA提供的長事務(wù)解決方案,在Saga模式中,業(yè)務(wù)流程中每個(gè)參與者都提交本地事務(wù),當(dāng)出現(xiàn)某一個(gè)參與者失敗則補(bǔ)償前面已經(jīng)成功的參與者,一階段正向服務(wù)和二階段補(bǔ)償服務(wù)都由業(yè)務(wù)開發(fā)實(shí)現(xiàn)。
? 分布式事務(wù)執(zhí)行過程中,依次執(zhí)行各參與者的正向操作,如果所有正向操作均執(zhí)行成功,那么分布式事務(wù)提交。如果任何一個(gè)正向操作執(zhí)行失敗,那么分布式事務(wù)會(huì)退回去執(zhí)行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分布式事務(wù)回到初始狀態(tài)。
? Saga 正向服務(wù)與補(bǔ)償服務(wù)也需要業(yè)務(wù)開發(fā)者實(shí)現(xiàn)。因此是業(yè)務(wù)入侵的。
? Saga 模式下分布式事務(wù)通常是由事件驅(qū)動(dòng)的,各個(gè)參與者之間是異步執(zhí)行的,Saga 模式是一種長事務(wù)解決方案。
使用場(chǎng)景
? Saga 模式適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng),Saga 模式一階段就會(huì)提交本地事務(wù),無鎖、長流程情況下可以保證性能。
? 事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù),無法進(jìn)行改造和提供 TCC 要求的接口,可以使用 Saga 模式。
- 補(bǔ)償事務(wù)(Compensating Transaction):補(bǔ)償事務(wù)模式基于"撤銷"操作來實(shí)現(xiàn)事務(wù)的回滾。當(dāng)某個(gè)步驟發(fā)生錯(cuò)誤時(shí),可以通過執(zhí)行逆向操作來恢復(fù)數(shù)據(jù)的一致性。
- Saga模式:Saga是一種基于微服務(wù)架構(gòu)的分布式事務(wù)處理模式,通過一系列局部事務(wù)來完成分布式事務(wù)。每個(gè)局部事務(wù)都有一個(gè)補(bǔ)償操作,用于回滾之前的操作。
- TCC模式(Try-Confirm-Cancel):TCC是一種基于業(yè)務(wù)邏輯的分布式事務(wù)解決方案。在TCC中,每個(gè)參與者需要實(shí)現(xiàn)Try、Confirm和Cancel三個(gè)操作,分別對(duì)應(yīng)事務(wù)的嘗試階段、確認(rèn)階段和取消階段。
f)總結(jié)
? 四種分布式事務(wù)模式,分別在不同的時(shí)間被提出,每種模式都有它的適用場(chǎng)景:
? AT 模式是無侵入的分布式事務(wù)解決方案,適用于不希望對(duì)業(yè)務(wù)進(jìn)行改造的場(chǎng)景,幾乎0學(xué)習(xí)成本。
? TCC 模式是高性能分布式事務(wù)解決方案,適用于核心系統(tǒng)等對(duì)性能有很高要求的場(chǎng)景。
? Saga 模式是長事務(wù)解決方案,適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng),Saga 模式一階段就會(huì)提交本地事務(wù),無鎖,長流程情況下可以保證性能,多用于渠道層、集成層業(yè)務(wù)系統(tǒng)。事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù),無法進(jìn)行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。
? XA模式是分布式強(qiáng)一致性的解決方案,但性能低而使用較少。
3)seata
a)介紹
? seata是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù)。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務(wù)模式,為用戶打造一站式的分布式解決方案。

TC (Transaction Coordinator) - 事務(wù)協(xié)調(diào)者
維護(hù)全局和分支事務(wù)的狀態(tài),驅(qū)動(dòng)全局事務(wù)提交或回滾。
TM (Transaction Manager) - 事務(wù)管理器
定義全局事務(wù)的范圍:開始全局事務(wù)、提交或回滾全局事務(wù)。
RM (Resource Manager) - 資源管理器
管理分支事務(wù)處理的資源,與TC交談以注冊(cè)分支事務(wù)和報(bào)告分支事務(wù)的狀態(tài),并驅(qū)動(dòng)分支事務(wù)提交或回滾
b)數(shù)據(jù)庫和業(yè)務(wù)層
seata_order.sql
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`count` int NULL DEFAULT 0,
`money` int NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `order_tbl` VALUES (1, '1', 'product-1', 1, 5);
INSERT INTO `order_tbl` VALUES (9, '1', 'product-1', 1, 5);
INSERT INTO `order_tbl` VALUES (10, '1', 'product-1', 1, 5);
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
seata_stock.sql
DROP TABLE IF EXISTS `stock_tbl`;
CREATE TABLE `stock_tbl` (
`id` int NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`count` int NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `commodity_code`(`commodity_code`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `stock_tbl` VALUES (1, 'product-1', 100000);
INSERT INTO `stock_tbl` VALUES (2, 'product-2', 0);
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
? 分別創(chuàng)建order-server, stock-server兩個(gè)服務(wù);
? order-server: get請(qǐng)求 , /order/insert , 新增一條訂單, 往數(shù)據(jù)庫中新增一條數(shù)據(jù), 同時(shí)遠(yuǎn)程調(diào)用stock-server中的/stock減庫存,;
? stock-server: put請(qǐng)求, /stock, 接收傳入過來的對(duì)象信息, 減去庫存;
業(yè)務(wù)邏輯代碼省略
c)seata-server配置
? 腳本和配置準(zhǔn)備:
? 
? nacos中創(chuàng)建seata的命名空間
? 
執(zhí)行腳本
使用git命令行打開,并且執(zhí)行腳本:
sh nacos-config.sh -h 127.0.0.1 -p 8848 -g seata -t 命名空間的id -u nacos -w nacos
腳本執(zhí)行完后, 配置的信息都導(dǎo)入到了nacos中:

修改seata的配置文件

啟動(dòng)seata-server
d)業(yè)務(wù)服務(wù)的配置
導(dǎo)入依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
seata配置(***)
#配置當(dāng)前事務(wù)的組名 必須和nacos中的service.vgroupMapping后面的值一直(下圖)
seata.tx-service-group=my_test_tx_group
#配置分組服務(wù)的mapping的值, 必須和nacos中service.vgroupMapping.****的值一直
seata.service.vgroup-mapping.my_test_tx_group=default
#設(shè)置事務(wù)分組對(duì)應(yīng)的seata服務(wù)地址 seata.service.grouplist后面的default和上面配置的值要相同
seata.service.grouplist.default=127.0.0.1:8091


e)測(cè)試
在業(yè)務(wù)方法中手動(dòng)添加異常, 觀察事務(wù)的回滾過程;
11 微服務(wù)鏈路追蹤
在微服務(wù)架構(gòu)下,系統(tǒng)的功能是由大量的微服務(wù)協(xié)調(diào)組成的,例如:電商創(chuàng)建訂單業(yè)務(wù)就需要訂單服務(wù)、庫存服務(wù)、支付服務(wù)、短信通知服務(wù)逐級(jí)調(diào)用才能完成。而每個(gè)服務(wù)可能是由不同的團(tuán)隊(duì)進(jìn)行開發(fā),部署在成百上千臺(tái)服務(wù)器上。

如此復(fù)雜的消息傳遞過程,當(dāng)系統(tǒng)發(fā)生故障的時(shí)候,就需要一種機(jī)制對(duì)故障點(diǎn)進(jìn)行快速定位,確認(rèn)是哪個(gè)服務(wù)出了問題,鏈路追蹤技術(shù)由此而生。所謂的鏈路追蹤,就是運(yùn)行時(shí)通過某種方式記錄下服務(wù)之間的調(diào)用過程,在通過可視化的 UI 界面幫研發(fā)運(yùn)維人員快速定位到出錯(cuò)點(diǎn)。引入鏈路追蹤,是微服務(wù)架構(gòu)運(yùn)維的底層基礎(chǔ),沒有它,運(yùn)維人員就像盲人摸象一樣,根本無法了解服務(wù)間通信過程。
1)Sleuth+Zipkin
在 Spring Cloud 標(biāo)準(zhǔn)生態(tài)下內(nèi)置了 Sleuth 這個(gè)組件,它通過擴(kuò)展 Logging 日志的方式實(shí)現(xiàn)微服務(wù)的鏈路追蹤。說起來比較晦澀,咱們看一個(gè)實(shí)例就明白了,在標(biāo)準(zhǔn)的微服務(wù)下日志產(chǎn)生的格式是:
2021-01-12 17:00:33.441 INFO [nio-7000-exec-2] c.netflix.config.ChainedDynamicProperty : Flipping property: b-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
但是當(dāng)引入 Spring Cloud Sleuth 鏈路追蹤組件后就會(huì)變成下面的格式:
2021-01-12 17:00:33.441 INFO [a-service,5f70945e0eefa832,5f70945e0eefa832,true] 18404 --- [nio-7000-exec-2] c.netflix.config.ChainedDynamicProperty : Flipping property: b-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
//比較后會(huì)發(fā)現(xiàn),在原有日志中額外附加了下面的文本
[a-service,5f70945e0eefa832,5f70945e0eefa832,true]
這段文本就是 Sleuth 在微服務(wù)日志中附加的鏈路調(diào)用數(shù)據(jù),它的格式是固定的,包含以下四部分:
[微服務(wù) Id,TraceId,SpanId,isExport]

鏈路追蹤數(shù)據(jù)的組成
微服務(wù) Id: 說明日志是由哪個(gè)微服務(wù)產(chǎn)生的。
TraceId: 軌跡編號(hào)。一次完整的業(yè)務(wù)處理過程被稱為軌跡,例如:實(shí)現(xiàn)登錄功能需要從服務(wù) A 調(diào)用服務(wù) B,服務(wù)B再調(diào)用服務(wù) C,那這一次登錄處理的過程就是一個(gè)軌跡,從前端應(yīng)用發(fā)來請(qǐng)求到接收到響應(yīng),每一次完整的業(yè)務(wù)功能處理過程都對(duì)應(yīng)唯一的 TraceId。
SpanId,步驟編號(hào)。剛才要實(shí)現(xiàn)登錄功能需要從服務(wù) A 到服務(wù) C 涉及 3 個(gè)微服務(wù)處理,按處理前后順序,每一個(gè)微服務(wù)處理時(shí)日志都被賦予不同的 SpanId。一個(gè) TraceId 擁有多個(gè) SpanId,而 SpanId 只能隸屬于某一個(gè) TraceId。
導(dǎo)出標(biāo)識(shí),當(dāng)前這個(gè)日志是否被導(dǎo)出,該值為 true 的時(shí)候說明當(dāng)前軌跡數(shù)據(jù)允許被其他鏈路追蹤可視化服務(wù)收集展現(xiàn)。
下面我們看一個(gè)完整的追蹤數(shù)據(jù)實(shí)例:
模擬了服務(wù) A -> 服務(wù) B -> 服務(wù) C的調(diào)用鏈路,下面是分別產(chǎn)生的日志
#服務(wù) A 應(yīng)用控制臺(tái)日志
2021-01-12 22:16:54.394 DEBUG [a-service,e8ca7047a782568b,e8ca7047a782568b,true] 21320 --- [nio-7000-exec-1] org.apache.tomcat.util.http.Parameters : ...
#服務(wù) B 應(yīng)用控制臺(tái)日志
2021-01-12 22:16:54.402 DEBUG [b-service,e8ca7047a782568b,b6aa80fb33e71de6,true] 21968 --- [nio-8000-exec-2] org.apache.tomcat.util.http.Parameters : ...
#服務(wù) C 應(yīng)用控制臺(tái)日志
2021-01-12 22:16:54.405 DEBUG [c-service,e8ca7047a782568b,537098c59827a242,true] 17184 --- [nio-9000-exec-2] org.apache.tomcat.util.http.Parameters : ...
可以發(fā)現(xiàn),在 DEBUG 級(jí)別下鏈路追蹤數(shù)據(jù)被打印出來,按調(diào)用時(shí)間先后順序分別是 A 到 C 依次出現(xiàn)。因?yàn)槭且淮瓮暾麡I(yè)務(wù)處理,TraceId 都是相同的,SpanId 卻各不相同,這些日志都已經(jīng)被 Sleuth 導(dǎo)出,可以被 ZipKin 收集展示。
Zipkin 是 推特的一個(gè)開源項(xiàng)目,它能收集各個(gè)服務(wù)實(shí)例上的鏈路追蹤數(shù)據(jù)并可視化展現(xiàn)。剛才 ABC 服務(wù)控制臺(tái)產(chǎn)生的日志在 ZipKin 的 UI 界面中會(huì)以鏈路追蹤圖表的形式展現(xiàn)。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
spring:
sleuth:
sampler:
probability: 1.0
rate: 100
zipkin:
base-url: http://localhost:9411

浙公網(wǎng)安備 33010602011771號(hào)