01 前言
"微服務(wù)”一詞源于 Martin Fowler的名為 Microservices的,博文,可以在他的官方博客上找到http:/ /martinfowler . com/articles/microservices.html簡(jiǎn)單地說(shuō),微服務(wù)是系統(tǒng)架構(gòu)上的一種設(shè)計(jì)風(fēng)格,它的主旨是將一個(gè)原本獨(dú)立的系統(tǒng)拆分成多個(gè)小型服務(wù),這些小型服務(wù)都在各自獨(dú)立的進(jìn)程中運(yùn)行,服務(wù)之間通過(guò)基于HTTP的 RESTfuL AP進(jìn)行通信協(xié)作。常見(jiàn)微服務(wù)框架:Spring的spring cloud、阿里dubbo、華為ServiceComb、騰訊Tars、Facebook thrift、新浪微博Motan。本章節(jié)我們先從了解組成完整系統(tǒng)的各個(gè)組件開(kāi)始,下章節(jié)將利用這些組件,搭建出一個(gè)完善的分布式系統(tǒng)。
1.1 Spring Cloud
這就不用多說(shuō)了,官網(wǎng)有詳細(xì)的介紹。
1.2 Spring Cloud Alibaba
Spring Cloud Alibaba 致力于提供微服務(wù)開(kāi)發(fā)的一站式解決方案。此項(xiàng)目包含開(kāi)發(fā)分布式應(yīng)用微服務(wù)的必需組件,方便開(kāi)發(fā)者通過(guò) Spring Cloud 編程模型輕松使用這些組件來(lái)開(kāi)發(fā)分布式應(yīng)用服務(wù)
主要組件
Sentinel:把流量作為切入點(diǎn),從流量控制、熔斷降級(jí)、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度保護(hù)服務(wù)的穩(wěn)定性。
Nacos:一個(gè)更易于構(gòu)建云原生應(yīng)用的動(dòng)態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺(tái)。
RocketMQ:一款開(kāi)源的分布式消息系統(tǒng),基于高可用分布式集群技術(shù),提供低延時(shí)的、高可靠的消息發(fā)布與訂閱服務(wù)。
Dubbo:Apache Dubbo? 是一款高性能 Java RPC 框架。
Seata:阿里巴巴開(kāi)源產(chǎn)品,一個(gè)易于使用的高性能微服務(wù)分布式事務(wù)解決方案。
02 服務(wù)注冊(cè)與發(fā)現(xiàn)
Eureka:官方宣布2.x不再開(kāi)源(閉源),之前的版本已經(jīng)停止更新;也就說(shuō)Eureka將來(lái)更多的技術(shù)提升已經(jīng)沒(méi)有了。所以,如果希望注冊(cè)中心有更多強(qiáng)大功能的話(huà),還需要另辟蹊徑 。
Zookeeper:在企業(yè)級(jí)Zookeeper注冊(cè)中心與 Dubbo組合比較多一些,kafka使用的也是,隨著Eureka的停更,我們可以通過(guò)spring-cloud-starter-zookeeper-discovery這個(gè)啟動(dòng)器,將Zookeeper做為springcloud的注冊(cè)中心。
Consul:go語(yǔ)言開(kāi)發(fā)的,也是一個(gè)優(yōu)秀的服務(wù)注冊(cè)框架,使用量也比較多。
Nacos:來(lái)自于SpringCloud??ibaba,在企業(yè)中經(jīng)過(guò)了百萬(wàn)級(jí)注冊(cè)考驗(yàn)的,不但可以完美替換Eureka,還能做其他組件的替換,所以,Naocs也強(qiáng)烈建議使用。
2.1 介紹下Nacos用作注冊(cè)中心
(1)Nacos簡(jiǎn)介
Nacos(Dynamic Naming and Configur ation Service) 是阿里巴巴2018年7月開(kāi)源的項(xiàng)目,致力于發(fā)現(xiàn)、配置和管理微服務(wù)。

(2)Nacos安裝
單節(jié)點(diǎn)
--下載鏡像
docker pull nacos/nacos-server:1.3.1
--啟動(dòng)容器
docker run --name nacos --env MODE=standalone --privileged=true -p 8848:8848 --restart=always -d dc833dc45d8f
訪(fǎng)問(wèn):
http://127.0.0.1:8848/nacos 賬號(hào)密碼都是nacos
集群
安裝前提
64 bit OS Linux/Unix/Mac,推薦使用Linux系統(tǒng)。
集群需要依賴(lài)mysql,單機(jī)可不必
3個(gè)或3個(gè)以上Nacos節(jié)點(diǎn)才能構(gòu)成集群
(3)搭建Nacos高可用集群步驟:
1、需要去Nacos官網(wǎng)clone Nacos集群項(xiàng)目nacos-docker
2、nacos-docker是使用的Docker Compose對(duì)容器進(jìn)行編排,所以首先需要安裝Docker Compose詳細(xì)信息可參照Nacos官網(wǎng):https:/ /nacos.io/zh-cn/docs/quick-start-docker.html
1)安裝Docker Compose
什么是Docker Compose
Compose項(xiàng)目是Docker官方的開(kāi)源項(xiàng)目,負(fù)責(zé)實(shí)現(xiàn)對(duì)Docker容器集群的快速編排。
#在Linux下下載(下載到/usr/local/bin)
curl -L https://github.com/docker/compose/releases/download/1.25.0/run.sh > /usr/local/bin/docker-compose
# 設(shè)置文件可執(zhí)行權(quán)限
chmod +x /usr/local/bin/docker-compose
# 查看版本信息
docker-compose --version
2)克隆Nacos-docker項(xiàng)目
#切換到自定義目錄
cd /usr/local/nacos
#開(kāi)始clone
git clone https://github.com/nacos-group/nacos-docker.git
3)運(yùn)行nacos-docker腳本
#執(zhí)行編排命令
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml up
上面的編排命令主要下載mysql鏡像和nacos鏡像,自動(dòng)完成鏡像下載/容器啟動(dòng)
Pulling from nacos/nacos-mysql,版本是5. 7(執(zhí)行初始化腳本)
Pulling nacos3 (nacos/nacos-server:latest)最新版本
4)停止、啟動(dòng)
#啟動(dòng)
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml start
#停止
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml stop
5)訪(fǎng)問(wèn)Nacos
http://192.168.1.1:8848/nacos
http://192.168.1.1:8849/nacos
http://192.168.1.1:8850/nacos
(4)Nacos快速入門(mén)
配置服務(wù)提供者
服務(wù)提供者可以通過(guò) Nacos 的服務(wù)注冊(cè)發(fā)現(xiàn)功能將其服務(wù)注冊(cè)到 Nacos server 上。
添加nacos依賴(lài)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${latest.version}</version>
</dependency>
添加配置
server.port=8070
spring.application.name=nacos-demo
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
啟動(dòng)類(lèi)
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderApplication.class, args);
}
@RestController
class EchoController {
@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
public String echo(@PathVariable String string) {
return "Hello Nacos Discovery " + string;
}
}
}
啟動(dòng)后,控制臺(tái):
說(shuō)明注冊(cè)成功,后臺(tái)查看該服務(wù):
下面我們用spring cloud 整合naocs實(shí)現(xiàn)服務(wù)調(diào)用
配置服務(wù)消費(fèi)者
添加配置
server.port=8080
spring.application.name=service-consumer
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
添加啟動(dòng)類(lèi):服務(wù)消費(fèi)者使用 @LoadBalanced RestTemplate 實(shí)現(xiàn)服務(wù)調(diào)用
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApplication.class, args);
}
@RestController
public class TestController {
private final RestTemplate restTemplate;
@Autowired
public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
public String echo(@PathVariable String str) {
return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
}
}
}
(5)測(cè)試
啟動(dòng) ProviderApplication 和 ConsumerApplication ,調(diào)用 http://localhost:8080/echo/2018,返回內(nèi)容為 Hello Nacos Discovery 2018。
03 分布式配置中心解決方案與應(yīng)用
目前市面上用的比較多的配置中心有(時(shí)間順序)
Disconf:2014年7月百度開(kāi)源的配置管理中心,同樣具備配置的管理能力,不過(guò)目前已經(jīng)不維護(hù)了,最近的一次提交是4-5年前了。
Spring Cloud Config:2014年9月開(kāi)源,Spring Cloud 生態(tài)組件,可以和Spring Cloud體系無(wú)縫整合。
Apollo:2016年5月,攜程開(kāi)源的配置管理中心,具備規(guī)范的權(quán)限、流程治理等特性。
Nacos:2018年6月,阿里開(kāi)源的配置中心,也可以做DNS和RPC的服務(wù)發(fā)現(xiàn)
3.1 介紹下Nacos用作分布式配置中心
啟動(dòng)了 Nacos server 后,您就可以參考以下示例代碼,為您的 Spring Cloud 應(yīng)用啟動(dòng) Nacos 配置管理服務(wù)了。完整示例代碼請(qǐng)參考:nacos-spring-cloud-config-example
- 添加依賴(lài):
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${latest.version}</version>
</dependency>
注意:版本 2.1.x.RELEASE 對(duì)應(yīng)的是 Spring Boot 2.1.x 版本。版本 2.0.x.RELEASE 對(duì)應(yīng)的是 Spring Boot 2.0.x 版本,版本 1.5.x.RELEASE 對(duì)應(yīng)的是 Spring Boot 1.5.x 版本。
更多版本對(duì)應(yīng)關(guān)系參考:版本說(shuō)明 Wiki
- 在 bootstrap.properties 中配置 Nacos server 的地址和應(yīng)用名
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=example
說(shuō)明:之所以需要配置 spring.application.name ,是因?yàn)樗菢?gòu)成 Nacos 配置管理 dataId字段的一部分。
在 Nacos Spring Cloud 中,dataId 的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
- prefix 默認(rèn)為 spring.application.name 的值,也可以通過(guò)配置項(xiàng) spring.cloud.nacos.config.prefix來(lái)配置。
- spring.profiles.active 即為當(dāng)前環(huán)境對(duì)應(yīng)的 profile,詳情可以參考 Spring Boot文檔。 注意:當(dāng) spring.profiles.active 為空時(shí),對(duì)應(yīng)的連接符 - 也將不存在,dataId 的拼接格式變成
{file-extension}
- file-exetension 為配置內(nèi)容的數(shù)據(jù)格式,可以通過(guò)配置項(xiàng) spring.cloud.nacos.config.file-extension 來(lái)配置。目前只支持 properties 和 yaml 類(lèi)型。
- 通過(guò) Spring Cloud 原生注解 @RefreshScope 實(shí)現(xiàn)配置自動(dòng)更新:
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${useLocalCache:false}")
private boolean useLocalCache;
@RequestMapping("/get")
public boolean get() {
return useLocalCache;
}
}
- 首先通過(guò)調(diào)用 Nacos Open API 向 Nacos Server 發(fā)布配置:dataId 為example.properties,內(nèi)容為useLocalCache=true
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=true"
- 運(yùn)行 NacosConfigApplication,調(diào)用 curl http://localhost:8080/config/get,返回內(nèi)容是 true。
- 再次調(diào)用 Nacos Open API 向 Nacos server 發(fā)布配置:dataId 為example.properties,內(nèi)容為useLocalCache=false
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=false"
- 再次訪(fǎng)問(wèn) http://localhost:8080/config/get,此時(shí)返回內(nèi)容為false,說(shuō)明程序中的useLocalCache值已經(jīng)被動(dòng)態(tài)更新了。
04 分布式服務(wù)調(diào)用
4.1 RPC概述
RPC 的主要功能目標(biāo)是讓構(gòu)建分布式計(jì)算(應(yīng)用)更容易,在提供強(qiáng)大的遠(yuǎn)程調(diào)用能力時(shí)不損失本地調(diào)用的語(yǔ)義簡(jiǎn)潔性。為實(shí)現(xiàn)該目標(biāo),RPC 框架需提供一種透明調(diào)用機(jī)制,讓使用者不必顯式的區(qū)分本地調(diào)用和遠(yuǎn)程調(diào)用。
RPC的優(yōu)點(diǎn):分布式設(shè)計(jì)、部署靈活、解耦服務(wù)、擴(kuò)展性強(qiáng)。
4.2 RPC框架
Dubbo:國(guó)內(nèi)最早開(kāi)源的 RPC 框架,由阿里巴巴公司開(kāi)發(fā)并于 2011 年末對(duì)外開(kāi)源,僅支持 Java 語(yǔ)言。
Motan:微博內(nèi)部使用的 RPC 框架,于 2016 年對(duì)外開(kāi)源,僅支持 Java 語(yǔ)言。
Tars:騰訊內(nèi)部使用的 RPC 框架,于 2017 年對(duì)外開(kāi)源,僅支持 C++ 語(yǔ)言。
Spring Cloud:國(guó)外 Pivotal 公司 2014 年對(duì)外開(kāi)源的 RPC 框架,提供了豐富的生態(tài)組件。
gRPC:Google 于 2015 年對(duì)外開(kāi)源的跨語(yǔ)言 RPC 框架,支持多種語(yǔ)言。
Thrift:最初是由 Facebook 開(kāi)發(fā)的內(nèi)部系統(tǒng)跨語(yǔ)言的 RPC 框架,2007 年貢獻(xiàn)給了 Apache 基金,成為
Apache:開(kāi)源項(xiàng)目之一,支持多種語(yǔ)言。
4.3 RPC框架優(yōu)點(diǎn)
RPC框架一般使用長(zhǎng)鏈接,不必每次通信都要3次握手,減少網(wǎng)絡(luò)開(kāi)銷(xiāo)。
RPC框架一般都有注冊(cè)中心,有豐富的監(jiān)控管理發(fā)布、下線(xiàn)接口、動(dòng)態(tài)擴(kuò)展等,對(duì)調(diào)用方來(lái)說(shuō)是無(wú)感知、統(tǒng)一化的操作協(xié)議私密,安全性較高
RPC 協(xié)議更簡(jiǎn)單內(nèi)容更小,效率更高,服務(wù)化架構(gòu)、服務(wù)化治理,RPC框架是一個(gè)強(qiáng)力的支撐。
4.4 RPC框架應(yīng)用:使用Spring Cloud Alibaba 整合Dubbo實(shí)現(xiàn)
由于 Dubbo Spring Cloud 構(gòu)建在原生的 Spring Cloud 之上, 其服務(wù)治理方面的能力可認(rèn)為是 Spring Cloud Plus,不僅完全覆蓋 Spring Cloud 原生特性,而且提供更為穩(wěn)定和成熟的實(shí)現(xiàn),特性比對(duì)如下表所示:

4.5 Dubbo 作為 Spring Cloud 服務(wù)調(diào)用
默認(rèn)情況,Spring Cloud Open Feign 以及@LoadBalanced`RestTemplate 作為 Spring Cloud 的兩種服務(wù)調(diào)用方式。 Dubbo Spring Cloud 為其提供了第三種選擇,即 Dubbo 服務(wù)將作為 Spring Cloud 服務(wù)調(diào)用的同等公民出現(xiàn),應(yīng)用可通過(guò) Apache Dubbo 注解@Service 和@Reference 暴露和引用 Dubbo 服務(wù),實(shí)現(xiàn)服務(wù)間多種協(xié)議的通訊。同時(shí),也可以利用 Dubbo 泛化接口輕松實(shí)現(xiàn)服務(wù)網(wǎng)關(guān)。
4.6 快速上手
按照傳統(tǒng)的 Dubbo 開(kāi)發(fā)模式,在構(gòu)建服務(wù)提供者之前,第一個(gè)步驟是為服務(wù)提供者和服務(wù)消費(fèi)者定義 Dubbo 服務(wù)接口。
為了確保契約的一致性,推薦的做法是將 Dubbo 服務(wù)接口打包在第二方或者第三方的 artifact(jar)中,該 artifact 甚至無(wú)需添加任何依賴(lài)。
對(duì)于服務(wù)提供方而言,不僅通過(guò)依賴(lài) artifact 的形式引入 Dubbo 服務(wù)接口,而且需要將其實(shí)現(xiàn)。對(duì)應(yīng)的服務(wù)消費(fèi)端,同樣地需要依賴(lài)該 artifact,并以接口調(diào)用的方式執(zhí)行遠(yuǎn)程方法。接下來(lái)的步驟則是創(chuàng)建 artifact。
4.7 創(chuàng)建服務(wù)API
創(chuàng)建一個(gè)api模塊,專(zhuān)門(mén)寫(xiě)各種接口的:
/**
* @author 原
* @date 2020/12/8
* @since 1.0
**/
public interface TestService {
String getMsg();
}
4.8 創(chuàng)建服務(wù)提供者
導(dǎo)入依賴(lài)
<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>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- api接口的依賴(lài)包-->
<dependency>
<groupId>com.dubbo.demo</groupId>
<artifactId>dubbo-demo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
編寫(xiě)配置
dubbo:
scan:
# dubbo 服務(wù)掃描基準(zhǔn)包
base-packages: org.springframework.cloud.alibaba.dubbo.bootstrap
protocol:
# dubbo 協(xié)議
name: dubbo
# dubbo 協(xié)議端口( -1 表示自增端口,從 20880 開(kāi)始)
port: -1
spring:
cloud:
nacos:
# Nacos 服務(wù)發(fā)現(xiàn)與注冊(cè)配置
discovery:
server-addr: 127.0.0.1:8848實(shí)現(xiàn)
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@Service//dubbo的service注解
public class TestServiceImpl implements TestService {
@Override
public String getMsg() {
return "123123";
}
}
啟動(dòng)類(lèi)
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@SpringBootApplication
@EnableDiscoveryClient
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class,args);
}
}
4.9 創(chuàng)建服務(wù)消費(fèi)者
除了api的實(shí)現(xiàn)類(lèi) 其他復(fù)用提供者的代碼
編寫(xiě)測(cè)試類(lèi)
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@RestController
public class TestController {
@Reference
TestService testService;
@GetMapping("/dubbo/test")
public String getMsg(){
return testService.getMsg();
}
}
訪(fǎng)問(wèn):
返回111
05 服務(wù)流量管理
5.1 為什么要流控降級(jí)?
流量是非常隨機(jī)性的、不可預(yù)測(cè)的。前一秒可能還風(fēng)平浪靜,后一秒可能就出現(xiàn)流量洪峰了(例如雙十一零點(diǎn)的場(chǎng)景)。然而我們系統(tǒng)的容量總是有限的,如果突然而來(lái)的流量超過(guò)了系統(tǒng)的承受能力,就可能會(huì)導(dǎo)致請(qǐng)求處理不過(guò)來(lái),堆積的請(qǐng)求處理緩慢,CPU/Load飆高,最后導(dǎo)致系統(tǒng)崩潰。因此,我們需要針對(duì)這種突發(fā)的流量來(lái)進(jìn)行限制,在盡可能處理請(qǐng)求的同時(shí)來(lái)保障服務(wù)不被打垮,這就是流量控制。
一個(gè)服務(wù)常常會(huì)調(diào)用別的模塊,可能是另外的一個(gè)遠(yuǎn)程服務(wù)、數(shù)據(jù)庫(kù),或者第三方API 等。例如,支付的時(shí)候,可能需要遠(yuǎn)程調(diào)用銀聯(lián)提供的 API;查詢(xún)某個(gè)商品的價(jià)格,可能需要進(jìn)行數(shù)據(jù)庫(kù)查詢(xún)。然而,這個(gè)被依賴(lài)服務(wù)的穩(wěn)定性是不能保證的。如果依賴(lài)的服務(wù)出現(xiàn)了不穩(wěn)定的情況,請(qǐng)求的響應(yīng)時(shí)間變長(zhǎng),那么調(diào)用服務(wù)的方法的響應(yīng)時(shí)間也會(huì)變長(zhǎng),線(xiàn)程會(huì)產(chǎn)生堆積,最終可能耗盡業(yè)務(wù)自身的線(xiàn)程池,服務(wù)本身也變得不可用。
現(xiàn)代微服務(wù)架構(gòu)都是分布式的,由非常多的服務(wù)組成。不同服務(wù)之間相互調(diào)用,組成復(fù)雜的調(diào)用鏈路。以上的問(wèn)題在鏈路調(diào)用中會(huì)產(chǎn)生放大的效果。復(fù)雜鏈路上的某一環(huán)不穩(wěn)定,就可能會(huì)層層級(jí)聯(lián), 最終導(dǎo)致整個(gè)鏈路都不可用。 因此我們需要對(duì)不穩(wěn)定的弱依賴(lài)服務(wù)進(jìn)行熔斷降級(jí),暫時(shí)切斷不穩(wěn)定調(diào)用,避免局部不穩(wěn)定因素導(dǎo)致整體的雪崩。
關(guān)于容錯(cuò)組件的停更/升級(jí)/替換
服務(wù)降級(jí):
Hystrix:官網(wǎng)不極力推薦,但是中國(guó)企業(yè)中還在大規(guī)模使用,對(duì)于限流和熔斷降級(jí)雖然在1 .5版本官方還支持(版本穩(wěn)定),
但現(xiàn)在官方已經(jīng)開(kāi)始推薦大家使用Resilience4j
Resilience4J:官網(wǎng)推薦使用,但是國(guó)內(nèi)很少用這個(gè)。
Sentienl:來(lái)自于Spring Cloud Alibaba,在中國(guó)企業(yè)替換Hystrix的組件,國(guó)內(nèi)強(qiáng)烈建議使用
這里就主要介紹下Sentinel。
5.2 Sentinel介紹
Sentinel是阿里開(kāi)源的項(xiàng)目,提供了流量控制、熔斷降級(jí)、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度來(lái)保障服務(wù)之間的穩(wěn)定性。
Sentinel的流控操作起來(lái)非常簡(jiǎn)單,在控制臺(tái)進(jìn)行配置即可看見(jiàn)效,所見(jiàn)即所得
Sentinel 具有以下特征:
- 豐富的應(yīng)用場(chǎng)景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場(chǎng)景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、集群流量控制、實(shí)時(shí)熔斷下游不可用應(yīng)用等。
- 完備的實(shí)時(shí)監(jiān)控:Sentinel 同時(shí)提供實(shí)時(shí)的監(jiān)控功能。您可以在控制臺(tái)中看到接入應(yīng)用的單臺(tái)機(jī)器秒級(jí)數(shù)據(jù),甚至 500 臺(tái)以下規(guī)模的集群的匯總運(yùn)行情況。
- 廣泛的開(kāi)源生態(tài):Sentinel 提供開(kāi)箱即用的與其它開(kāi)源框架/庫(kù)的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應(yīng)的依賴(lài)并進(jìn)行簡(jiǎn)單的配置即可快速地接入 Sentinel。
- 完善的 SPI 擴(kuò)展點(diǎn):Sentinel 提供簡(jiǎn)單易用、完善的 SPI 擴(kuò)展接口。您可以通過(guò)實(shí)現(xiàn)擴(kuò)展接口來(lái)快速地定制邏輯。例如定制規(guī)則管理、適配動(dòng)態(tài)數(shù)據(jù)源等。
官網(wǎng)
https://github.com/alibaba/Sentinel
中文
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
https://sentinelguard.io/zh-cn/docs/introduction.html
Sentinel 的使用可以分為兩個(gè)部分:
核心庫(kù)(Java 客戶(hù)端):不依賴(lài)任何框架/庫(kù),能夠運(yùn)行于 Java 7 及以上的版本的運(yùn)行時(shí)環(huán)境,同時(shí)對(duì)Dubbo / Spring Cloud 等框架也有較好的支持。
控制臺(tái)(Dashboard):控制臺(tái)主要負(fù)責(zé)管理推送規(guī)則、監(jiān)控、集群限流分配管理、機(jī)器發(fā)現(xiàn)等
使用場(chǎng)景
? 在服務(wù)提供方(Service Provider)的場(chǎng)景下,我們需要保護(hù)服務(wù)提供方自身不被流量洪峰打垮。 這時(shí)候通常根據(jù)服務(wù)提供方的服務(wù)能力進(jìn)行流量控制, 或針對(duì)特定的服務(wù)調(diào)用方進(jìn)行限制。我們可以結(jié)合前期壓測(cè)評(píng)估核心口的承受能力,配置 QPS 模式的限流,當(dāng)每秒的請(qǐng)求量超過(guò)設(shè)定的閾值時(shí),會(huì)自動(dòng)拒絕多余的請(qǐng)求。
? 為了避免調(diào)用其他服務(wù)時(shí)被不穩(wěn)定的服務(wù)拖垮自身,我們需要在服務(wù)調(diào)用端(Service Consumer)對(duì)不穩(wěn)定服務(wù)依賴(lài)進(jìn)行隔離和熔斷。手段包括信號(hào)量隔離、異常比例降級(jí)、RT 降級(jí)等多種手段。
? 當(dāng)系統(tǒng)長(zhǎng)期處于低水位的情況下, 流量突然增加時(shí), 直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮。這時(shí)候我們可以借助 Sentinel 的 WarmUp 流控模式控制通過(guò)的流量緩慢增加,在一定時(shí)間內(nèi)逐漸增加到閾值上限,而不是在一瞬間全部放行。這樣可以給冷系統(tǒng)一個(gè)預(yù)熱的時(shí)間,避免冷系統(tǒng)被壓垮。
? 利用 Sentinel 的勻速排隊(duì)模式進(jìn)行“削峰填谷”, 把請(qǐng)求突刺均攤到一段時(shí)間內(nèi), 讓系統(tǒng)負(fù)載保持在請(qǐng)求處理水位之內(nèi),同時(shí)盡可能地處理更多請(qǐng)求。
? 利用 Sentinel 的網(wǎng)關(guān)流控特性,在網(wǎng)關(guān)入口處進(jìn)行流量防護(hù),或限制 API 的調(diào)用頻率。
5.2 Sentinel安裝
1、下載jar包https://github.com/alibaba/Sentinel/releases
2、啟動(dòng)
java -Dserver.port=8787 -Dcsp.sentinel.dashboard.server=127.0.0.1:8787 -Dproject.name=sentinel-dashboard -jar /home/sentinel/sentinel-dashboard-1.8.0.jar
3、訪(fǎng)問(wèn)
http://127.0.0.1:8787/#/login
初始賬號(hào)密碼sentinel/sentinel
可以看到sentinel是自己本身的監(jiān)控
5.3 sentinel快速入門(mén)
1、導(dǎo)入依賴(lài)
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
2、測(cè)試類(lèi)
public class TestService {
public static void main(String[] args) {
initFlowRules();
while (true) {
Entry entry = null;
try {
entry = SphU.entry("HelloWorld");
/*您的業(yè)務(wù)邏輯 - 開(kāi)始*/
System.out.println("hello world");
/*您的業(yè)務(wù)邏輯 - 結(jié)束*/
} catch (BlockException e1) {
/*流控邏輯處理 - 開(kāi)始*/
System.out.println("block!");
/*流控邏輯處理 - 結(jié)束*/
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
//設(shè)置流量控制規(guī)則 設(shè)置當(dāng)QPS達(dá)到20時(shí) 會(huì)限制流量(拋出異常,可以執(zhí)行處理)
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
執(zhí)行結(jié)果:
可以看到,這個(gè)程序每秒穩(wěn)定輸出 "hello world" 20 次,和規(guī)則中預(yù)先設(shè)定的閾值是一樣的。 block表示被阻止的請(qǐng)求。
官方使用文檔:https://github.com/alibaba/Sentinel/wiki/如何使用
5.4 Sentinel整合SpringCloud實(shí)現(xiàn)服務(wù)限流/熔斷
導(dǎo)入依賴(lài)
<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>
配置
server.port=8082
spring.application.name=sentinel-demo
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8787
//在需要流控的方法加上@SentinelResource
@RestController
public class TestController {
@GetMapping("/sentinel")
@SentinelResource
public String getMsg(){
return "11";
}
}
啟動(dòng)應(yīng)用,訪(fǎng)問(wèn)http://127.0.0.1:8082/sentinel后去sentinel后臺(tái)
下面我們來(lái)配一條最簡(jiǎn)單的流控規(guī)則。針對(duì) sentinel_spring_web_context /sentinel 這個(gè)服務(wù)調(diào)用配置限流規(guī)則(需要有過(guò)訪(fǎng)問(wèn)量才能看到)。我們配一條 QPS 為 1的流控規(guī)則,這代表針對(duì)該服務(wù)方法的調(diào)用每秒鐘不能超過(guò) 1 次,超出會(huì)直接拒絕。
現(xiàn)在快速訪(fǎng)問(wèn):http://localhost:8082/sentinel
查看實(shí)時(shí)監(jiān)控頁(yè)面:
其他功能的使用,大家可以參考官方文檔自行摸索。
5.5 如何選擇流控降級(jí)組件?
以下是 Sent inel 與其它fault-tolerance 組件的對(duì)比:
06 分布式事務(wù)
6.1 為什么需要分布式事務(wù)?
分布式事務(wù)是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點(diǎn)之上。簡(jiǎn)單來(lái)說(shuō)就是組成事務(wù)的各個(gè)單元處于不同數(shù)據(jù)庫(kù)服務(wù)器上。 如何保證服務(wù)間的一致性?當(dāng)在一條較長(zhǎng)的微服務(wù)調(diào)用鏈中, 位于中間位置的微服務(wù)節(jié)點(diǎn)出現(xiàn)異常,如何保證整個(gè)服務(wù)的數(shù)據(jù)一致性?分布式一致性的引入, 一定不可避免帶來(lái)性能問(wèn)題, 如何更高效的解決分布式一致性問(wèn)題,一直是我們致力于解決此問(wèn)題的關(guān)鍵出發(fā)點(diǎn)。
6.2 分布式事務(wù)解決方案分類(lèi)
剛性事務(wù)
剛性事務(wù)指的就是遵循本地事務(wù)四大特性(ACID)的強(qiáng)一致性事務(wù)。它的特點(diǎn)就是強(qiáng)一致性,要求組成事務(wù)的各個(gè)單元馬上提交或者馬上回滾,沒(méi)有時(shí)間彈性,要求以同步的方式執(zhí)行。通常在單體架構(gòu)項(xiàng)目中應(yīng)用較多,一般都是企業(yè)級(jí)應(yīng)用(或者局域網(wǎng)應(yīng)用)。例如:生成合同時(shí)記錄日志,付款成功后生成憑據(jù)等等。但是,在時(shí)下流行的互聯(lián)網(wǎng)項(xiàng)目中,這種剛性事務(wù)來(lái)解決分布式事務(wù)就會(huì)有很多的弊端。其中最明顯的或者說(shuō)最致命的就是性能問(wèn)題。
因?yàn)槟硞€(gè)參與者不能自己提交事務(wù),必須等待所有參與者執(zhí)行OK了之后,一起提交事務(wù),那么事務(wù)鎖住的時(shí)間就變得非常長(zhǎng),從而導(dǎo)致性能非常低下?;诖宋覀円簿偷贸隽艘粋€(gè)結(jié)論,階段越多,性能越差。
柔性事務(wù)
柔性事務(wù)是針對(duì)剛性事務(wù)而說(shuō)的,我們剛才分析了剛性事務(wù),它有兩個(gè)特點(diǎn),第一個(gè)強(qiáng)一致性,第二個(gè)近實(shí)時(shí)性(NRT)。而柔性事務(wù)的特點(diǎn)是不需要立刻馬上執(zhí)行(同步性),且不需要強(qiáng)一致性。它只要滿(mǎn)足基本可用和最終一致就可以了。要想真正的明白,需要從BASE理論和CAP理論說(shuō)起。
6.3 CAP理論和BASE理論
1)CAP理論
CAP理論,又叫做CAP原則,網(wǎng)上對(duì)他的概念描述非常的清晰,且一致。換句話(huà)說(shuō),我們?cè)诰W(wǎng)上搜到的CAP理論的描述,基本都是一樣的。它的描述是這樣的:
CAP指的是在一個(gè)分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)、分區(qū)容錯(cuò)性(Partitiontolerance)。其中,C,A,P的說(shuō)明如下:
一致性(C):在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時(shí)刻是否同樣的值。(等同于所有節(jié)點(diǎn)訪(fǎng)問(wèn)同一份最新的數(shù)據(jù)副本)
可用性(A):在集群中一部分節(jié)點(diǎn)故障后,集群整體是否還能響應(yīng)客戶(hù)端]的讀寫(xiě)請(qǐng)求。(對(duì)數(shù)據(jù)更新具備高可用性)
分區(qū)容錯(cuò)性(P):以實(shí)際效果而言,分區(qū)相當(dāng)于對(duì)通信的時(shí)限要求。系統(tǒng)如果不能在時(shí)限內(nèi)達(dá)成數(shù)據(jù)一致性,就意味著發(fā)生了分區(qū)的情況,必須就當(dāng)前操作在C和A之間做出選擇。
CAP原則指的是,這三個(gè)要素最多只能同時(shí)實(shí)現(xiàn)兩點(diǎn),不可能三者兼顧。因此在進(jìn)行分布式架構(gòu)設(shè)計(jì)時(shí),必須做出取舍。而對(duì)于分布式數(shù)據(jù)系統(tǒng),分區(qū)容錯(cuò)性是基本要求,否則就失去了價(jià)值。因此設(shè)計(jì)分布式數(shù)據(jù)系統(tǒng),就是在一致性和可用性之間取一個(gè)平衡。對(duì)于大多數(shù)web應(yīng)用,其實(shí)并不需要強(qiáng)一致性,因此犧牲一致性而換取高可用性,是目前多數(shù)分布式數(shù)據(jù)庫(kù)產(chǎn)品的方向。
2)BASE理論
BASE理論是指,Basically Available(基本可用)、Soft-state( 軟狀態(tài)/柔性事務(wù))、Eventual Consistency(最
終一致性)。
1、基本可用 BA:(Basically Available ):
指分布式系統(tǒng)在出現(xiàn)故障的時(shí)候,允許損失部分可用性,保證核心可用。但不等價(jià)于不可用。比如:搜索引擎0.5秒返回查詢(xún)結(jié)果,但由于故障,2秒響應(yīng)查詢(xún)結(jié)果;網(wǎng)頁(yè)訪(fǎng)問(wèn)過(guò)大時(shí),部分用戶(hù)提供降級(jí)服務(wù)等。簡(jiǎn)單來(lái)說(shuō)就是基本可用。
2、軟狀態(tài) S:( Soft State):
軟狀態(tài)是指允許系統(tǒng)存在中間狀態(tài),并且該中間狀態(tài)不會(huì)影響系統(tǒng)整體可用性。即允許系統(tǒng)在不同節(jié)點(diǎn)間副本同步的時(shí)候存在延時(shí)。簡(jiǎn)單來(lái)說(shuō)就是狀態(tài)可以在一段時(shí)間內(nèi)不同步。
3、最終一致性 E:(Eventually Consistent ):
系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過(guò)一定時(shí)間后,最終能夠達(dá)到一致的狀態(tài),不需要實(shí)時(shí)保證系統(tǒng)數(shù)據(jù)的強(qiáng)一致性。最終一致性是弱一致性的一種特殊情況。BASE理論面向的是大型高可用可擴(kuò)展的分布式系統(tǒng),通過(guò)犧牲強(qiáng)一致性來(lái)獲得可用性。ACID是傳統(tǒng)數(shù)據(jù)庫(kù)常用的概念設(shè)計(jì),追求強(qiáng)一致性模型。簡(jiǎn)單來(lái)說(shuō)就是在一定的時(shí)間窗口內(nèi), 最終數(shù)據(jù)達(dá)成一致即可。
BASE理論是基于CAP原則演化而來(lái),是對(duì)CAP中一致性和可用性權(quán)衡的結(jié)果。
核心思想:即使無(wú)法做到強(qiáng)一致性,但每個(gè)業(yè)務(wù)根據(jù)自身的特點(diǎn),采用適當(dāng)?shù)姆绞絹?lái)使系統(tǒng)達(dá)到最終一致性。
6.4 分布式事務(wù)解決方案
(1)二階段提交2PC(3PC)
關(guān)于更多2pc與3pc的介紹,可以參考博文:2PC和3PC原理,這不是我們今天討論的重點(diǎn)。
(2)XA
XA 標(biāo)準(zhǔn)提出后的 20 多年間未能得到持續(xù)的演進(jìn),在學(xué)術(shù)界有協(xié)議優(yōu)化和日志協(xié)同處理等相關(guān)的研究,在工業(yè)界使用 XA 落地方案的相對(duì)較少,主要集中在應(yīng)用服務(wù)器的場(chǎng)景。XA 方案要求相關(guān)的廠(chǎng)商提供其具體協(xié)議的實(shí)現(xiàn),目前大部分關(guān)系數(shù)據(jù)庫(kù)支持了 XA 協(xié)議,但是支持程度不盡相同,例如,MySQL 在 5.7 才對(duì) xa_prepare 語(yǔ)義做了完整支持。XA 方案被人詬病的是其性能, 其實(shí)更為嚴(yán)重的是對(duì)于連接資源的占用, 導(dǎo)致在高并發(fā)未有足夠的連接資源來(lái)響應(yīng)請(qǐng)求成為系統(tǒng)的瓶頸。在微服務(wù)架構(gòu)下 XA 事務(wù)方案隨著微服務(wù)鏈路的擴(kuò)展成為一種反伸縮模式,進(jìn)一步加劇了資源的占用。另外 XA 事務(wù)方案要求事務(wù)鏈路中的 resource 全部實(shí)現(xiàn) XA 協(xié)議方可使用,若其中某一資源不滿(mǎn)足,那么就無(wú)法保證整個(gè)鏈路的數(shù)據(jù)一致性。
(3)TCC補(bǔ)償方案
TCC分別指的是Try,Confirm,Cancel。它是補(bǔ)償型分布式事務(wù)解決方案。何為補(bǔ)償呢?其實(shí)我們把TCC這3個(gè)部分分別做什么捋清楚,就很容理解了。首先,我們先來(lái)看下它們的主要作用:
Try 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做檢測(cè)及資源預(yù)留。
Confirm 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做確認(rèn)提交,Try階段執(zhí)行成功并開(kāi)始執(zhí)行 Confirm階段時(shí),默認(rèn) Confirm階段是
不會(huì)出錯(cuò)的。即:只要Try成功,Confirm一定成功。
Cancel 階段主要是在業(yè)務(wù)執(zhí)行錯(cuò)誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消,預(yù)留資源釋放。
由此,我們可以得出結(jié)論,就是在Try階段進(jìn)行嘗試提交事務(wù),當(dāng)Try執(zhí)行OK了,Confirm執(zhí)行,且默認(rèn)認(rèn)為它一定成功。但是當(dāng)Try提交失敗了,則由Cancel處理回滾和資源釋放。
從概念上 TCC 框架可以認(rèn)為是一種萬(wàn)能框架,但是其難點(diǎn)是業(yè)務(wù)對(duì)于這三個(gè)接口的實(shí)現(xiàn),開(kāi)發(fā)成本相對(duì)較高,有較多業(yè)務(wù)難以做資源預(yù)留相關(guān)的邏輯處理, 以及是否需要在預(yù)留資源的同時(shí)從業(yè)務(wù)層面來(lái)保證隔離性。因此,這種模式比較適應(yīng)于金融場(chǎng)景中易于做資源預(yù)留的扣減模型。
(4) Saga
Saga其實(shí)是30年前的一篇數(shù)據(jù)庫(kù)論文里提到的一個(gè)概念。在論文中一個(gè)Saga事務(wù)就是一個(gè)長(zhǎng)期運(yùn)行的事務(wù),這個(gè)事務(wù)是由多個(gè)本地事務(wù)所組成, 每個(gè)本地事務(wù)有相應(yīng)的執(zhí)行模塊和補(bǔ)償模塊,當(dāng)saga事務(wù)中的任意一個(gè)本地事務(wù)出錯(cuò)了, 可以通過(guò)調(diào)用相關(guān)事務(wù)對(duì)應(yīng)的補(bǔ)償方法恢復(fù),達(dá)到事務(wù)的最終一致性。
有了 TCC 解決方案為什么還需要 Saga 事務(wù)解決方案?上文提到了 TCC 方案中對(duì)業(yè)務(wù)的改造成本較大, 對(duì)于內(nèi)部系統(tǒng)可以自上而下大刀闊斧的推進(jìn)系統(tǒng)的改造, 但對(duì)于第三方的接口的調(diào)用往往很難推動(dòng)第三方進(jìn)行 TCC 的改造,讓對(duì)方為了你這一個(gè)用戶(hù)去改造 TCC 方案而其他用戶(hù)并不需要,需求上明顯也是不合理的。要求第三方業(yè)務(wù)接口提供正反接口比如扣款和退款,在異常場(chǎng)景下必要的數(shù)據(jù)沖正是合理的。另外,Saga 方案更加適應(yīng)于工作流式的長(zhǎng)事務(wù)方案并且可異步化。
(5)最終一致性方案
本地消息表
這種實(shí)現(xiàn)方式應(yīng)該是業(yè)界使用最多的,其核心思想是將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理,這種思路是來(lái)源于
ebay。它和MQ事務(wù)消息的實(shí)現(xiàn)思路都是一樣的,都是利用MQ通知不同的服務(wù)實(shí)現(xiàn)事務(wù)的操作。不同的是,針對(duì)
消息隊(duì)列的信任情況,分成了兩種不同的實(shí)現(xiàn)。本地消息表它是對(duì)消息隊(duì)列的穩(wěn)定性處于不信任的態(tài)度,認(rèn)為消息
可能會(huì)出現(xiàn)丟失,或者消息隊(duì)列的運(yùn)行網(wǎng)絡(luò)會(huì)出現(xiàn)阻塞,于是在數(shù)據(jù)庫(kù)中建立一張獨(dú)立的表,用于存放事務(wù)執(zhí)行的
狀態(tài),配合消息隊(duì)列實(shí)現(xiàn)事務(wù)的控制。
MQ事務(wù)消息
有一些第三方的MQ是支持事務(wù)消息的,比如RocketMQ,ActiveMQ,他們支持事務(wù)消息的方式也是類(lèi)似于采用的
二階段提交。但是有一些常用的MQ也不支持事務(wù)消息,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中間件為例,其思路大致為:
第一階段Prepared消息,會(huì)拿到消息的地址。
第二階段執(zhí)行本地事務(wù)。
第三階段通過(guò)第一階段拿到的地址去訪(fǎng)問(wèn)消息,并修改狀態(tài)。
也就是說(shuō)在業(yè)務(wù)方法內(nèi)要想消息隊(duì)列提交兩次請(qǐng)求,一次發(fā)送消息和一次確認(rèn)消息。如果確認(rèn)消息發(fā)送失敗了
RocketMQ會(huì)定期掃描消息集群中的事務(wù)消息,這時(shí)候發(fā)現(xiàn)了Prepared消息,它會(huì)向消息發(fā)送者確認(rèn),所以生產(chǎn)方需要實(shí)現(xiàn)一個(gè)check接口,RocketMQ會(huì)根據(jù)發(fā)送端設(shè)置的策略來(lái)決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息。這樣就保證了消息發(fā)送與本地事務(wù)同時(shí)成功或同時(shí)失敗。
用一個(gè)下單業(yè)務(wù)流程圖來(lái)表示下:
兩者對(duì)比
6.5 分布式事務(wù)框架Seata
FESCAR是阿里巴巴 開(kāi)源的分布式事務(wù)中間件,以高效并且對(duì)業(yè)務(wù)0侵入的方式,解決微服務(wù)場(chǎng)景下面臨的分布式
事務(wù)問(wèn)題。Seata是fescar的升級(jí)版本,從2019年4月起,更名為seata。
(1)seata的典型案例分析
在seata的官方網(wǎng)站中提供了一套詳細(xì)的業(yè)務(wù)流程介紹,我們就以此為例,展開(kāi)講解。
首先是單體架構(gòu)的事務(wù)場(chǎng)景介紹:電商購(gòu)物中的3個(gè)業(yè)務(wù)模塊(庫(kù)存、訂單和賬戶(hù))。他們操作本地?cái)?shù)據(jù)庫(kù),由于
同庫(kù)同源,所以本地事務(wù)可以保證其一致性,如下圖所示:
但是在微服務(wù)架構(gòu)中,由于每個(gè)業(yè)務(wù)模塊變成了獨(dú)立的服務(wù),且每個(gè)服務(wù)連接自己的數(shù)據(jù)庫(kù),此時(shí)由原來(lái)同庫(kù)同源
變成了不同數(shù)據(jù)庫(kù)不同數(shù)據(jù)源的情況,雖然每個(gè)服務(wù)自己的數(shù)據(jù)庫(kù)操作仍然可以使用本地事務(wù)控制,但是要讓服務(wù)
間的事務(wù)保持一致性就需要用到分布式事務(wù)了。
在此時(shí),引入了seata,它提供了一個(gè)“完美”的解決方案。如下圖所示:

在圖中的三個(gè)和seata相關(guān)的組件,前面在介紹fescar的時(shí)候已經(jīng)介紹了,它也是seata的基本組件,他們分別是:
1)事務(wù)協(xié)調(diào)器(TC):維護(hù)全局和分支事務(wù)的狀態(tài),驅(qū)動(dòng)全局提交或回滾。
2)事務(wù)管理器(TM):定義全局事務(wù)的范圍:開(kāi)始全局事務(wù),提交或回滾全局事務(wù)。
3)資源管理器(RM):管理分支事務(wù)的資源,與TC通信以注冊(cè)分支事務(wù)和報(bào)告分支事務(wù)的狀態(tài),并驅(qū)動(dòng)分支事務(wù)提交或回滾。
seata管理分布式事務(wù)的生命周期
- TM要求TC開(kāi)始新的全局事務(wù)。 TC生成表示全局事務(wù)的XID。
- XID通過(guò)微服務(wù)的調(diào)用鏈傳播。
- RM將本地事務(wù)注冊(cè)為XID到TC的相應(yīng)全局事務(wù)的分支。
- TM要求TC提交或回滾XID的相應(yīng)全局事務(wù)。
- TC在XID的相應(yīng)全局事務(wù)下驅(qū)動(dòng)所有分支事務(wù),以完成分支提交或回滾。
庫(kù)存服務(wù)
public interface StorageService {
/**
* deduct storage count
*/
void deduct(String commodityCode, int count);
}
訂單服務(wù)
public interface OrderService {
/**
* create order
*/
Order create(String userId, String commodityCode, int orderCount);
}
賬戶(hù)服務(wù)
public interface AccountService {
/**
* debit balance of user's account
*/
void debit(String userId, int money);
}
主業(yè)務(wù)邏輯
public class BusinessServiceImpl implements BusinessService {
private StorageService storageService;
private OrderService orderService;
/**
* purchase
*/
public void purchase(String userId, String commodityCode, int orderCount) {
storageService.deduct(commodityCode, orderCount);
orderService.create(userId, commodityCode, orderCount);
}
}
public class OrderServiceImpl implements OrderService {
private OrderDAO orderDAO;
private AccountService accountService;
public Order create(String userId, String commodityCode, int orderCount) {
int orderMoney = calculate(commodityCode, orderCount);
accountService.debit(userId, orderMoney);
Order order = new Order();
order.userId = userId;
order.commodityCode = commodityCode;
order.count = orderCount;
order.money = orderMoney;
// INSERT INTO orders ...
return orderDAO.insert(order);
}
}
我們只需要一個(gè)@GlobalTransactional關(guān)于業(yè)務(wù)方法的注釋?zhuān)?/p>
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
......
}
6.6 由Dubbo + SEATA提供支持的示例
(1)步驟1:建立資料庫(kù)
- 要求:具有InnoDB引擎的MySQL。
注意:實(shí)際上,在示例用例中,這3個(gè)服務(wù)應(yīng)該有3個(gè)數(shù)據(jù)庫(kù)。但是,為了簡(jiǎn)單起見(jiàn),我們只能創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)并配置3個(gè)數(shù)據(jù)源。
使用您剛創(chuàng)建的數(shù)據(jù)庫(kù)URL /用戶(hù)名/密碼修改Spring XML。
dubbo-account-service.xml dubbo-order-service.xml dubbo-storage-service.xml
<property name="url" value="jdbc:mysql://x.x.x.x:3306/xxx" />
<property name="username" value="xxx" />
<property name="password" value="xxx" />
(2)步驟2:創(chuàng)建UNDO_LOG表
UNDO_LOG SEATA AT模式需要此表。
-- 注意此處0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
(3)步驟3:建立表格,例如業(yè)務(wù)
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(4)步驟4:?jiǎn)?dòng)服務(wù)器
- 從https://github.com/seata/seata/releases下載服務(wù)器軟件包,將其解壓縮。
Usage: sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows) [options]
Options:
--host, -h
The host to bind.
Default: 0.0.0.0
--port, -p
The port to listen.
Default: 8091
--storeMode, -m
log store mode : file、db
Default: file
--help
e.g.
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
(5)步驟5:運(yùn)行示例
前往樣品倉(cāng)庫(kù):seata-samples
- 啟動(dòng)DubboAccountServiceStarter
- 啟動(dòng)DubboStorageServiceStarter
- 啟動(dòng)DubboOrderServiceStarter
- 運(yùn)行DubboBusinessTester進(jìn)行演示測(cè)試
TBD:用于運(yùn)行演示應(yīng)用程序的腳本