詳解“微服務(wù)”架構(gòu)體系——SpringCloud Alibaba!

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ù)。

image.png

(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

image

集群

安裝前提

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)最新版本

image

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):

image

說(shuō)明注冊(cè)成功,后臺(tái)查看該服務(wù):

image

下面我們用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

  1. 添加依賴(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

  1. 在 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 的拼接格式變成 {prefix}.{file-extension}
  • file-exetension 為配置內(nèi)容的數(shù)據(jù)格式,可以通過(guò)配置項(xiàng) spring.cloud.nacos.config.file-extension 來(lái)配置。目前只支持 properties 和 yaml 類(lèi)型。
  1. 通過(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;
    }
}
  1. 首先通過(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"

  1. 運(yùn)行 NacosConfigApplication,調(diào)用 curl http://localhost:8080/config/get,返回內(nèi)容是 true。
  2. 再次調(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"
  1. 再次訪(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ì)如下表所示:

image.png

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ù)本身也變得不可用。

image

現(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ù)源等。
image

官網(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

image

可以看到sentinel是自己本身的監(jiān)控

image

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é)果:

image

可以看到,這個(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)

image

下面我們來(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ì)直接拒絕。

image

現(xiàn)在快速訪(fǎng)問(wèn):http://localhost:8082/sentinel

image

查看實(shí)時(shí)監(jiān)控頁(yè)面:

image

其他功能的使用,大家可以參考官方文檔自行摸索。

5.5 如何選擇流控降級(jí)組件?

以下是 Sent inel 與其它fault-tolerance 組件的對(duì)比:

image

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)

image

關(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ù)留的扣減模型。

image

(4) Saga

image

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)表示下:

image

微服務(wù)架構(gòu):RPC+Dubbo+SpirngBoot+Alibaba+Docker+K8s

兩者對(duì)比

image

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ù)可以保證其一致性,如下圖所示:

image

但是在微服務(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è)“完美”的解決方案。如下圖所示:

image.png

在圖中的三個(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ù)的生命周期

  1. TM要求TC開(kāi)始新的全局事務(wù)。 TC生成表示全局事務(wù)的XID。
  2. XID通過(guò)微服務(wù)的調(diào)用鏈傳播。
  3. RM將本地事務(wù)注冊(cè)為XID到TC的相應(yīng)全局事務(wù)的分支。
  4. TM要求TC提交或回滾XID的相應(yīng)全局事務(wù)。
  5. 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ù)器

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)用程序的腳本

原文鏈接:https://www.cnblogs.com/whgk/p/14356994.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容