欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springcloud H版+alibaba cloud4

程序员文章站 2022-06-19 10:21:48
springcloud H版+alibaba cloud419,sleuth链路追踪20,springcloud alibaba21,nacos22,sentinel23,seata19,sleuth链路追踪sleuth提供了一套完整的服务追踪解决方案在分布式系统中提供追踪解决方案并且兼容支持zipkin#springcloud F版本起就不需要自己maven构建zipkin了。只需要调用jar包即可把jar包下载下来放到一个盘运行java -jar jar包名修改服务提供者cloud-p...

19,sleuth链路追踪

sleuth提供了一套完整的服务追踪解决方案
在分布式系统中提供追踪解决方案并且兼容支持zipkin

#springcloud F版本起就不需要自己maven构建zipkin了。只需要调用jar包即可

把jar包下载下来放到一个盘
运行java -jar jar包名

修改服务提供者cloud-provider-payment8001
修改服务消费者cloud-consumer-order80

8001:
pom新增

<!--包含了sleuth+zipkin-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

yml

zipkin:
  base-url: http://localhost:9411
sleuth:
  sampler:
    probability: 1

controller

@GetMapping("/payment/zipkin")
public String paymentZipkin(){
    return "hi, i'am paymentzipkin server fall back,welcome to atguigu, O(∩_∩)O哈哈~";
}

80
pom和yml一样

controller

//============> zipkin + sleuth
@GetMapping("/consumer/payment/zipkin")
public String paymentZipkin(){
    String result = restTemplate.getForObject("http://localhost:8001" + "/payment/zipkin", String.class);
    return result;
}

启动7001,8001,80,调用80的controller接口形成链路。。。。然后刷新9411的web界面。。。可以看到链路追踪详细信息

springcloud H版+alibaba cloud4

20,springcloud alibaba

springcloud alibaba做了一件包饺子的事情

dubbo被springcloud包饺子
cloud又被alibaba cloud包饺子

springcloud alibaba官网
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

springalibaba 支持的功能
服务限流降级
服务注册与发现
分布式配置管理
消息驱动能力:rocketMq
阿里云对象存储
分布式任务调度

如果需要使用springcloud alibaba,则在父工程的root的pom里引入

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  <version>2.1.0.RELEASE</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

springcloud alibaba的各大组件:

sentinel ,nacos,Rocket,Dubbo,seata,oss,schedulerX

21,nacos

nacos:Naming configuration service

服务注册发现与配置中心服务组件

下载地址:
https://github.com/alibaba/Nacos

官方学习文档也从这进
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

nacos和eureka都是保证AP,高可用和分区容错性。Nacos还保证了cp

1,nacos下载后直接找到bin下的startup.cmd,启动即可

2,访问http://localhost:8848/nacos
用户名,密码都是nacos

                    nacos服务注册与发现

#编码
1,新建项目cloudalibaba-provider-payment9001
2,pom新增

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3,yml

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'  #监控

4,主启动类

package com.atguigu.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * @author wsk
 * @date 2020/3/23 10:21
 */
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9001.class,args);
    }
}

5,主业务类

package com.atguigu.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author wsk
 * @date 2020/3/23 10:27
 */
@RestController
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id){
        return "nacos registry,serverPort: "+ serverPort+"\t id"+id;
    }
}

测试启动9001,访问localhost:9001/payment/nacos/1
springcloud H版+alibaba cloud4

                                          nacos负载均衡

1,参照9001新建9002
2,新建微服务消费者83
cloudalibaba-consumer-nacos-order83
pom跟提供者一样

yml

server:
  port: 83

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

#消费者将要去访问的微服务名称(成功注册进nacos的微服务提供者),在这配置了访问的服务,业务类就不用在定义常量了

service-url:
  nacos-user-service: http://nacos-payment-provider

主启动类一样
配置类

package com.atguigu.springcloud.alibaba.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
 * @author wsk
 * @date 2020/3/23 11:07
 */
@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced   //RestTemplate结合Ribbon做负载均衡一定要加@LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

业务类

package com.atguigu.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
 * @author wsk
 * @date 2020/3/23 11:09
 */
@RestController
public class OrderNacosController {
    /*
    因为在yml中配置了service-url.nacos-user-service,
    这里不需要再定义要访问微服务名常量,而是通过boot直接读出来
     */
    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id){
        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
    }
}

启动9002
启动83
然后看nacos
springcloud H版+alibaba cloud4

再访问80的接口localhost:83/consumer/
springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

nacos是cp+ap的支持HTTP/DNS/UDP协议的各种支持的强大的组件

#nacos ap和cp的切换
AP模式支持是springcloud,保证了高可用和分区容错性。只支持注册临时实例
CP模式是k8s,保证强一致性和分区容错性

AP和cp可以互相切换
切换命令

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
                   nacos配置中心

#编码
1,新建cloudalibaba-config-nacos-client3377
2,pom新增

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3,yml 两个
bootstrap.yml

server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yml

application.yml

spring:
  profiles:
    active: dev #

4,主启动类

package com.atguigu.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * @author wsk
 * @date 2020/3/23 12:28
 */
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfigClientMain3377 {
    public static void main(String[] args) {
        SpringApplication.run(NacosConfigClientMain3377.class,args);
    }
}

5,业务类

package com.atguigu.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author wsk
 * @date 2020/3/23 12:30
 */
@RestController
@RefreshScope   //SpringCloud原生注解 支持Nacos的动态刷新功能
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo(){
        return configInfo;
    }
}

6,在nacos里添加规则
规则是

${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}

所以根据以上配置

Data Id   :nacos-config-client-dev.yml

springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

启动3377,访问localhost:3377/config/info
springcloud H版+alibaba cloud4

                                        动态刷新

直接修改nacos配置中心的yaml文件,version改成20,在访问接口
springcloud H版+alibaba cloud4

                                        nacos高级篇

nacos对于多环境多项目的管理十分强大,不仅对生产,开发,测试环境都能管理,而且对其他的公司的外包项目的环境也能管理

由nacos的namespace+Group+DataID三者构成
namespace:用于区分部署环境的
Group和DataID用于逻辑上区分两个目标对象

默认情况下:namespace:public
Group:DEFAULT_GROUP
默认的集群cluster也是DEFAULT

Group可以把不同的微服务划分到同一个分组里面去

#######三种配置方式
1,DataId级别的配置
在原基础上再新建一个nacos-config-client-test.yaml
配置内容

config:
    info: from nacos config center,nacos-config-client-test.yaml ,version=2

application.yml里改active改成test就可以访问后面这个新加的配置了
springcloud H版+alibaba cloud4

2,group级别的配置
(1)新建配置

DataId: nacos-config-client-info.yaml

Group:DEV_GROUP
配置内容:

config:
    info: nacos-config-client-info.yaml,DEV_GROUP

(2)新建配置

DataId: nacos-config-client-info.yaml
Group:TEST_GROUP

配置内容:

config:
    info: nacos-config-client-info.yaml,TEST_GROUP

(3)修改bootstrap.yml和application.yml
1,bootstrap
config下增加

group: TEST_GROUP

2,application

active: info
springcloud H版+alibaba cloud4

重启后访问controller
springcloud H版+alibaba cloud4

3,namespace命名空间方案
(1)新建dev/test的namespace
点击命名空间,新建命名空间
dev和test

(2)选择dev 点+号

Dataid :nacos-config-client-dev.yaml

配置内容:

 config:
    info: xxxxxxxxx  DEFAULT_GROUP

回到bootstrap
group下面配置一行

namespace:  命名空间id

(3)dev下再新建配置

nacos-config-client-dev.yaml
Group: DEV_GROUP

配置内容:

 config:
     info: xxxxxxxxx,DEV-GROUP

(4)dev下再新建配置

nacos-config-client-dev.yaml
Group:TEST_GROUP

配置内容:

config:
       info: xxxxxxxxx,TEST_GROUP

到此为止dev下有三个配置文件。相同命名空间不同组配置
重启服务,访问成功。test命名空间下道理一样不在赘述

                          nocas集群和持久化

8848挂掉了,所有的玩完?
至少得有3台nacos

nocas为什么关闭服务以后。数据还存在。关闭电脑再启动还存在。因为它本身有一个嵌入式数据库,那么有了数据库为什么还要持久化集群mysql?因为多个nacos嵌入式数据库造成数据一致性问题得不到解决

nacos底层源码:
https://github.com/alibaba/nacos/blob/develop/config/pom.xml

springcloud H版+alibaba cloud4

默认自带derby嵌入式数据库坐标

derby----mysql的配置
1,nacos-server-1.1.4\nacos\conf目录下找到nacos-mysql.sql
2,到sqlyog执行sql脚本
3,nacos-server-1.1.4\nacos\conf目录下找到application.properties
4,mysql版本在5.6.5以上
application下粘贴

spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?
characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true

db.user=root
db.password=ljs

5,重启nacos会发现有一个空的页面

6,新建配置
springcloud H版+alibaba cloud4

7,发现数据库

springcloud H版+alibaba cloud4

                               linux下搭建生产环境下的nacos集群+mysql+nginx

1,下载linux版本的nacos
https://github.com/alibaba/nacos/releases/tag/1.1.4
跟之前windows的一样也是找到sql文件。然后复制到linux的数据库下。然后修改配置文件。。

直接参考这篇文章。。。https://www.cnblogs.com/larscheng/p/11427733.html

springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

代码实例

修改9002 nacos的服务提供者yml文件

springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

注册成功。nacos

22,sentinel

alibaba sentinel 实现熔断和限流

#官方文档
github.com/alibaba/sentinel

之前的hystrix
1,需要自己手工搭建监控平台
2,没有web界面可给我们进行更加细粒化的配置
以及流控,速率控制。服务熔断,服务降级

sentinel:
1,单独一个组件,可独立出来
2,直接姐棉花细粒度统一配置

下载地址:https://github.com/alibaba/sentinel/releases
下载1.7的sentinel-dashboard.jar

sentinal分为两个部分:
核心库:(java客户端)不依赖任何框架/库。能够运行于所有java运行时环境。对dubbo/springcloud框架也有很好的支持

控制台:基于springboot开发。打包后可直接运行不需要额外的tomcat等应用容器

运行jar包以后访问localhost:8080

用户名密码都是sentinel
springcloud H版+alibaba cloud4

                     sentinel的初始化监控

#编码
1,启动nacos8848

2,新建cloud-alibaba-sentinel-service 8401

3,pom

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--springcloud alibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--springcloud alibaba sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

4,yml

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # sentinel dashboard 地址
        dashboard: localhost:8080
        # 默认为8719,如果被占用会自动+1,直到找到为止
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: "*"      

5,主启动类

package com.atguigu.springcloud.alibaba;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * @author wsk
 * @date 2020/3/24 13:59
 */
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}

6,controller

package com.atguigu.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author wsk
 * @date 2020/3/24 14:00
 */
@RestController
@Slf4j
public class FlowLimitController {

    @GetMapping("/testA")
    public String testA(){
        return "--------testA";
    }

    @GetMapping("/testB")
    public String testB(){
        return "--------testB";
    }
}

7,启动8401
访问接口http://localhost:8401/testA
springcloud H版+alibaba cloud4

                                    sentinel 流控规则

流量控制:
资源名就是我们的application -name
唯一的。或者默认请求路径

针对来源就是:sentinel可以针对调用者进行限流。填写微服务名。默认default

阈值类型/单机阈值
qps:当调用该api的qps达到阈值的时候,进行限流
线程数:当调用该api的线程数达到阈值的时候进行限流
是否集群:sentinel不需要

流控模式
直接:api达到限流条件时,直接限流
关联:当关联资源达到阈值,限流自己
链路:只记录指定链路上的流量

流控效果:
快速失败:直接失败,抛异常
warm up:根据codeFactor(冷加载因子。默认3)的值,从阈值codeFactor,经过预热时长,才达到设置的qps值
排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须为qps,否则无效

                      流控模式

1,qps:
设置单机阈值 1
流控模式:直接
流控效果:快速失败
springcloud H版+alibaba cloud4

快速点击

springcloud H版+alibaba cloud4

2,线程数
在sentinel设置
springcloud H版+alibaba cloud4

然后回到testA修改testA方法

@GetMapping("/testA")
public String testA(){
    try{
        TimeUnit.MILLISECONDS.sleep(800);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "--------testA";
}

然后开两个浏览器模拟多线程

springcloud H版+alibaba cloud4

流控模式:1,关联
相当于我生病,你吃药?这有什么用?
#通常用于支付模块达到阈值,限流订单模块。

配置规则
testA
阈值类型qps 单机阈值1
流控模式:关联
关联资源:/testB
流控效果:快速失败
打开postman 模拟多线程访问testB
springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

找到collections
springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

模拟20个线程,每延迟300毫秒访问testB

这期间访问testA
springcloud H版+alibaba cloud4

这之后访问

springcloud H版+alibaba cloud4
流控效果:default
直接失败:抛出异常

Blocked by sentinel(flow limiting)

                     流控效果:预热:warm up

设置 资源名:/testB
针对来源:default
阈值类型:qps 单机阈值10
预热时长 5 流程模式:直接

在5秒钟阈值慢慢从3-10
访问浏览器很快刷新页面。刚开始不行。慢慢过了5秒的样子能抗住了

如秒杀系统在开启的瞬间,会有很多流量上来。很有可能把系统打死。预热方式就是为了保护系统,慢慢将流量放进来。慢慢把阈值增长到设置的阈值

冷加载因子=单机阈值/3
根据warm up 设置时间慢慢qps由单机阈值/3-----单机阈值

                      流程效果:排队等待

让请求以均匀的速度通过,阈值类型必须设置成qps否则无效
设置:资源名:/testA
阈值类型:qps 单机阈值:1
流控模式:直接
流控效果:排队等待
超时时间:20000

意思是:/testA每秒1次请求。超过就排队等待,等待的超时时间为20000毫秒

相当于在某一秒有大量请求。接下来空闲。一会又有大量请求。我们可以在空闲时间处理剩下的请求。而不是在第一秒就直接拒绝了

回到idea在testB方法里加

log.info(Thread.currentThread().getName()+"\t"+"...testB");

springcloud H版+alibaba cloud4

                                   sentinel降级

点击降级规则,右上角–+新增降级规则
有三种策略:RT,异常比例,异常数

RT:平均响应时间:秒级
平均响应时间超出阈值,且时间窗口内通过的请求>=5两个条件同时满足触发降级。窗口期过后关闭断路器。RT最大为4900

异常比例:秒级
qps>=5且异常比例(秒级)超过阈值。触发降级。时间窗口结束后。关闭降级

异常数(分钟级)
异常数(分钟统计)超过阈值时。触发降级,时间窗口结束后,关闭降级

sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时候(例如调用超时或异常比例升高),对这个资源的调用进行限制。让请求快速失败。避免影响到其他资源导致级联错误

当资源被降级后,在接下来降级时间窗口内。对该资源的调用都自动熔断。默认抛出DegradeException

Hystrix有open closed halfopen状态
sentinel没有半开状态

1,RT
平均响应时间:1秒持续5个请求。且平均响应时间大于阈值,触发降级。时间窗口期结束。关闭降级

回到controller

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

回到sentinel dashboard —簇点链路–选择降级–选RT–RT200–时间窗口1

使用jmeter压测
springcloud H版+alibaba cloud4

线程数设置10
springcloud H版+alibaba cloud4
Ramp-up perviod(in seconds):1
循环次数:永远

启动项目,再启动jmeter

再访问接口localhost:8401/testD
会出现降级
springcloud H版+alibaba cloud4

#为什么会这样呢?
根据jmeter的设置。永远一秒打进10个线程。大于5个。调用testD。我们希望200ms处理完本次任务。如果超过200ms没有处理完。在未来一秒钟内的时间窗口内。断路器打开。微服务不可用

2,异常比例
异常比例:当资源的每次请求量>=5,并且每秒异常总数占通过量比值超过阈值之后。资源进入降级状态,即在接下的时间窗口内,对这个方法调用都会自动地返回。异常比例的阈值范围在[0.0,1.0]。0%-100%

qps>=5&&异常比例超过阈值,触发降级,时间窗口期结束,关闭降级

将/testD代码改为。。

@GetMapping("/testD")
public String testD(){
    log.info("testD 测试 RT");
    int age=10/0;
    return "--------testD";
}

sentinel的设置
异常比例-0.2–时间窗口1

jmeter依旧和刚才一样设置跑起来
springcloud H版+alibaba cloud4

停掉jmeter
springcloud H版+alibaba cloud4
3,异常数
异常数:当资源近一分钟的异常数目超过阈值之后会进行熔断:时间窗口一定大于60秒。因为它是分钟级的

异常数超过阈值—降级—时间窗口结束----关闭降级

controller

@GetMapping("/testE")
public String testE(){
    log.info("testE测试异常数");
    int age = 10/0;
    return "--------testE 测试异常数";
}

sentinel设置—异常数–5--时间窗口61

访问localhost:8401/testE
访问5次以后才触发降级

springcloud H版+alibaba cloud4

                  sentinel热点key限流

官网:github.com/alibaba/sentinel/wiki/热点参数限流

像hystrix某个方法出问题了,去找对应兜底方法降级。这是我们自定义的

像sentinel不配置的话。默认提示Blocked by sentinel(flow limiting)

可以配置Sentinel的@SentinelResource

热点限流key的原理
官网源码:com.alibaba.csp.sentinel.slots.block.BlockException

#编码
controller

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
    return "---------------deal_testHotKey,o(╥﹏╥)o";
}

sentinel设置热点限流规则
资源名:testHotKey
参数索引:0
单机阈值:1
窗口时长:1

正常访问
springcloud H版+alibaba cloud4
点击速度加快
springcloud H版+alibaba cloud4

#如果用了@SentinelResource一定要指定value的同时,指定blockhandler否则会有异常页面。用户看到不太好

                   热点key参数例外项

在编辑热点规则下面有个高级选项

当我们设定了p1这个参数为0的索引。一旦qps>1则熔断限流。可不可以p1的值如果等于一个特定的值。则让他不熔断限流呢

设置参数例外项。记得不要忘记添加按钮

参数类型只能支持String加8种基本数据类型
参数值:5
限流阈值:200
添加

然后访问接口

springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

如果在testHotKey方法添加一行代码:
int age=10/0;
springcloud H版+alibaba cloud4
所以直接的@SentinelResource不管运行时异常

                     系统自适应限流

sentinel
点击系统规则—新增系统规则
load自适应
只支持linux,unix系统。

如果设置了入口qps则掌握全局qps阈值设置1

  #####           SentinelResource配置

1,按资源名称限流+后续处理
#编码
(1)修改pom
增加一个

<dependency>
    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

(2)新建controller
@RestController

public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handlerException")
    public CommonResult byResource(){
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handlerException(BlockException exception){
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }
    }

(3)sentinel–流控规则–新建资源名byResource-阈值类型qps-单机阈值1

启动nacos,sentinel,8401项目
访问localhost:8401/byResource狂点会进入兜底
springcloud H版+alibaba cloud4

正常点。正常访问

springcloud H版+alibaba cloud4
平时关闭8401微服务。流控规则会消失。所以它是临时节点

2,按照url地址限流

在controller增加一个方法
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value=“byUrl”)
public CommonResult byurl(){
return new CommonResult(200,“按url限流ok”,new Payment(2020L,“Serial002”));
}

这个sentinel设置直接加一个左斜杠

springcloud H版+alibaba cloud4
不好用就去掉斜杠,好用了
,但是没有指定blockHandler肯定刷快了会爆异常页面

3,兜底方法存在问题
每个业务都有一个兜底方法,像hystrix一样,可以解耦。写一个fallback类?可以!

客户自定义限流
(1)创建CustomerBlockHandler类

package com.atguigu.springcloud.alibaba.handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
/**
 * @author wsk
 * @date 2020/3/24 22:04
 */
public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(4444,"按客户自定义,global handlerException-----1");
    }
    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(4444,"按客户自定义,global handlerException-----2");
    }
}

(2)RatelimitController添加方法

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class,
        blockHandler = "handlerException2")
public CommonResult customerBlockHandler(){
    return new CommonResult(200,"按客户自定义",new Payment(2020L,"serial002"));
}

启动8401

访问这个接口

然后设置sentinel
springcloud H版+alibaba cloud4

平时访问
springcloud H版+alibaba cloud4
兜底访问
springcloud H版+alibaba cloud4
sentinel熔断
sentinel注解不支持private只支持public

服务熔断采取整合ribbon或openFeign+fallback

1,修改cloudalibaba-provider-payment9003/9004

package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
/**
 * @author wsk
 * @date 2020/3/24 22:36
 */
@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static {
        hashMap.put(1L,new Payment(1L,"28adsasadsafas5d1as5fad5as31d3as0"));
        hashMap.put(2L,new Payment(2L,"4das5d5asda4dad5asda5s3dasd53das0"));
        hashMap.put(3L,new Payment(3L,"6ads65d351as3d1as53d1a53d3as13as0"));
    }

    @GetMapping("/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200, "from mysql ,serverPort: " + serverPort);
        return result;
    }
}

2,84作为消费者端调9003/9004实现负载均衡
修改84
第一种情况。没有任何配置的

package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author wsk
 * @date 2020/3/24 23:06
 */
@RestController
@Slf4j
public class CircleBreakerController {
    private static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
   @SentinelResource(value = "fallback") //没有配置
//    @SentinelResource(value = "fallback",fallback = "handlerFallback") //配置了fallback的,fallback只负责业务异常
//    @SentinelResource(value = "fallback",blockHandler = "blockHandler") // 配置了blockHandler,只负责sentinel控制台配置违规
    //@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler",
         //   exceptionsToIgnore = {IllegalArgumentException.class}) // 配置了blockHandler和fallback
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult<Payment> commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
        }else if(commonResult.getData() == null){
            throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
        }
        return commonResult;
    }
    // 本例是fallback
    public CommonResult handlerFallback(Long id, Throwable e){
        Payment payment = new Payment(id, null);
        return new CommonResult(444, "兜底异常handler,exception内容"+e.getMessage(), payment);
    }

    public CommonResult blockHandler(Long id, BlockException exception){
        Payment payment = new Payment(id, null);
        return new CommonResult<>(445, "blockHandler-sentinel 限流,无此流水号:blockException" + exception.getMessage(), payment);
    }

    // --------------- open feign---------

    @Resource
    private PaymentService paymentService;

    @GetMapping("/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        return paymentService.paymentSQL(id);
    }
}

3,启动nacos,sentinel,9003,9004,84
测试http://localhost:84/consumer/fallback

如果是4,报页面的非法参数异常。。。。
springcloud H版+alibaba cloud4
4以外的报空指针异常
springcloud H版+alibaba cloud4

第二种情况
@SentinelResource(value = “fallback”,fallback = “handlerFallback”)

加代码。方法

// 本例是fallback
public CommonResult handlerFallback(Long id, Throwable e){
    Payment payment = new Payment(id, null);
    return new CommonResult(444, "兜底异常handler,exception内容"+e.getMessage(), payment);
}

springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

第三种情况

@SentinelResource(value = "fallback",blockHandler = "blockHandler")

加代码

public CommonResult blockHandler(Long id, BlockException exception){
      Payment payment = new Payment(id, null);
      return new CommonResult<>(445, "blockHandler-sentinel 限流,无此流水号:blockException" + exception.getMessage(), payment);
  }

设置sentinel
springcloud H版+alibaba cloud4

然后访问接口方法
springcloud H版+alibaba cloud4

多访问几次进入兜底
springcloud H版+alibaba cloud4

第四种情况

@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler")

放开刚才两个方法
sentinel设置流控规则
qps:1
访问接口。

快速点击进入限流

springcloud H版+alibaba cloud4

普通点击走兜底

springcloud H版+alibaba cloud4

快速点击fallback4,是走兜底还是限流。是走限流的

异常忽略属性

@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler",
       exceptionsToIgnore = {IllegalArgumentException.class})

然后访问localhost:84/consumer/fallback/4

走的是error page
springcloud H版+alibaba cloud4

localhost:84/consumer/fallback/5

springcloud H版+alibaba cloud4

走的兜底

                      sentinel服务熔断+openfeign

修改84.
1,pom
新增

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2,yml
#激活sentinel对feign的支持

feign:
  sentinel:
    enabled: true

3,主启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients

4,service

package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * @author wsk
 * @date 2020/3/25 9:02
 */
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {

    @GetMapping("/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.stereotype.Component;

/**
 * @author wsk
 * @date 2020/3/25 9:06
 */
@Component
public class PaymentFallbackService implements PaymentService{
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444,"服务降级返回,------------PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

5,controller

// --------------- open feign---------

  @Resource
  private PaymentService paymentService;

  @GetMapping("/consumer/paymentSQL/{id}")
  public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
      return paymentService.paymentSQL(id);
  }

springcloud H版+alibaba cloud4

故意关闭9003

触发降级
springcloud H版+alibaba cloud4

                sentinel持久化

当我们配置规则以后,关闭服务。配置规则自动消失。
将限流,熔断这些配置规则持久化进nacos。只要刷新8401某个rest地址。sentinel控制台的流控规则就可以看到。只要nacos里面配置不删除。针对8401上sentinel上的流控规则持续有效

1,修改cloudalibaba-sentinel-service8401
pom新增

<!--springcloud alibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

2,yml

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # sentinel dashboard 地址
        dashboard: localhost:8080
        # 默认为8719,如果被占用会自动+1,直到找到为止
        port: 8719
      # 流控规则持久化到nacos
      datasource:
        dsl:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
management:
  endpoints:
    web:
      exposure:
        include: "*"
feign:
  sentinel:
    enabled: true        

3,添加nacos业务规则配置(必须是做了持久化的nacos)
可以用本地的8848 nacos配置

新建配置
DataID:cloudalibaba-sentinel-service
配置格式:json
配置内容:

[
    {
        "resource":"/rateLimit/byUrl",
        "limitApp":"default",
        "grade":1,
        "count":1,
        "strategy":0,
        "controllBehavior":0,
        "clusterMode":false
    }
]

其中各项含义:
resource:资源名称
limitApp:来源应用
grade:阈值类型 0,线程数 1,qps
count:单机阈值
strategy:流控模式,0直接 1关联 2链路
controlBehavior:流程效果 0快速失败 1warm up 2排队等待
clusterMode 是否集群

4,启动8401,访问接口。刷新sentinel发现流控规则有了
springcloud H版+alibaba cloud4
停止8401发现
没了
springcloud H版+alibaba cloud4

不怕,再重启8401

快速访问刚才那个localhost:8401/rateLimit/byUrl
springcloud H版+alibaba cloud4

我并没有重新配置流控。他快速访问就已经生效。说明。sentinel持久化成功

23,seata

seata处理分布式事务

以前的单体应用被拆分成多个应用,分别使用多个独立的数据源(数据库),每个服务内部数据的一致性由本地事务来保证,但全局无法保证,所以出现了分布式事务

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题

seata是alibaba开源的分布式事务解决方案。致力于在微服务架构下提供高性能和简单易用的分布式事务服务

官网http://seata.io/zh-cn/

全局唯一事务id+三组件(TC,TM,RM)

TC:事务协调器,维护全局事务运行状态,负责协调并驱动全局事务提交或回滚
TM:控制全局事务的边界。负责开启一个全局事务,并最终发起全局提交式全局回滚的决议
RM:控制分支事务,负责分支注册,状态汇报,并接受事务协调器指令,驱动本地事务提交和回滚

seata分布式事务执行原理
1,TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
2,XID在微服务调用链路的上下文中传播
3,RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
4,TM向TC发起针对XID的全局提交或回滚决议
5,TC调度XID下管辖的全部后支事务完成提交或回滚请求

                      seata安装配置

1,地址:https://github.com/seata/seata/releases

springcloud H版+alibaba cloud4
2,解压到固定的路径
3,修改:自定义事务组名称+事务日志存储模式db+数据库连接信息
4,修改conf下的file.conf
(1)修改service里的

 vgroup_mapping.my_test_tx_group = "fsp_tx_group"

(2)修改store模块的

mode="db"

然后下面改db的配置

url="jdbc:mysql://127.0.0.1:3306/seata"
user="root"
password="ljs"

5,在数据库新建库seata
6,在seata库里建表
建表sql在\seata-server-1.0.0\seata\conf里
db_store.sql

drop table if exists `global_table`;
create table `global_table` (
  `xid` varchar(128)  not null,
  `transaction_id` bigint,
  `status` tinyint not null,
  `application_id` varchar(32),
  `transaction_service_group` varchar(32),
  `transaction_name` varchar(128),
  `timeout` int,
  `begin_time` bigint,
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`xid`),
  key `idx_gmt_modified_status` (`gmt_modified`, `status`),
  key `idx_transaction_id` (`transaction_id`)
);


drop table if exists `branch_table`;
create table `branch_table` (
  `branch_id` bigint not null,
  `xid` varchar(128) not null,
  `transaction_id` bigint ,
  `resource_group_id` varchar(32),
  `resource_id` varchar(256) ,
  `lock_key` varchar(128) ,
  `branch_type` varchar(8) ,
  `status` tinyint,
  `client_id` varchar(64),
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`branch_id`),
  key `idx_xid` (`xid`)
);


drop table if exists `lock_table`;
create table `lock_table` (
  `row_key` varchar(128) not null,
  `xid` varchar(96),
  `transaction_id` long ,
  `branch_id` long,
  `resource_id` varchar(256) ,
  `table_name` varchar(32) ,
  `pk` varchar(36) ,
  `gmt_create` datetime ,
  `gmt_modified` datetime,
  primary key(`row_key`)
);

分支,全局,锁表生成成功

7,修改seata-server-1.0.0\seata\conf目录下的registry.conf配置文件
第三行

type="nacos"
然后找到nacos{
serverAddr="localhost:8848"

8,启动nacos,再启动seata-server.bat。如果dos下出现
springcloud H版+alibaba cloud4

表示内存不足。要找到
seata-server.bat里修改
springcloud H版+alibaba cloud4

改成这样,改小
springcloud H版+alibaba cloud4

-load Registry Provider表示执行成功

springcloud H版+alibaba cloud4

            订单/库存/账户数据库准备

都需要先启动nacos,再启动seata
业务:当用户下单时,会在订单服务中创建一个订单。然后通过远程调用库存服务扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额。最后订单服务中修改订单状态为已完成

创建业务数据库
seata-order:存储订单的数据库
seata-storage:存储库存的数据库
seata-account:存储账户信息的数据库
库对应表:
order库对应t_order表
storage库对应t_storage表
account库对应t_account表
springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4
springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

springcloud H版+alibaba cloud4

订单,库存,账户3个库下都需要建各自的回滚日志表
在\seata-server-1.0.0\seata\conf目录下有
db_undo_log.sql

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;

在三个库下分别粘贴执行

最终的数据库准备结果
springcloud H版+alibaba cloud4

订单。库存。账户微服务编码
下订单–减库存—减余额–改订单状态

订单模块。创建seata-order-service2001
1,pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>seata-order-service2001</artifactId>

    <dependencies>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--hutool 测试雪花算法-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-captcha</artifactId>
            <version>5.2.0</version>
        </dependency>
    </dependencies>
</project>

2,yml

server:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        # 自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    # 当前数据源操作类型
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://47.115.93.213:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: ljs
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info
#ribbon的超时时间,这里测试用
ribbon:
   ReadTimeout: 60000
   ConnectTimeout: 60000

mybatis:
  mapper-locations: classpath*:mapper/*.xml

3,file.conf

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
  #vgroup->rgroup
  # 事务组名称
  vgroup_mapping.fsp_tx_group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

## transaction log store
store {
  ## store mode: file、db
  #mode = "file"
  mode = "db"

  ## file store
  file {
    dir = "sessionStore"

    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    max-branch-session-size = 16384
    # globe session size , if exceeded throws exceptions
    max-global-session-size = 512
    # file buffer size , if exceeded allocate new buffer
    file-write-buffer-cache-size = 16384
    # when recover batch read size
    session.reload.read_size = 100
    # async, sync
    flush-disk-mode = async
  }

  ## database store
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://47.115.93.213:3306/seata"
    user = "root"
    password = "ljs"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}
lock {
  ## the lock store mode: local、remote
  mode = "remote"

  local {
    ## store locks in user's database
  }

  remote {
    ## store locks in the seata's server
  }
}
recovery {
  #schedule committing retry period in milliseconds
  committing-retry-period = 1000
  #schedule asyn committing retry period in milliseconds
  asyn-committing-retry-period = 1000
  #schedule rollbacking retry period in milliseconds
  rollbacking-retry-period = 1000
  #schedule timeout retry period in milliseconds
  timeout-retry-period = 1000
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  #schedule delete expired undo_log in milliseconds
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

## metrics settings
metrics {
  enabled = false
  registry-type = "compact"
  # multi exporters use comma divided
  exporter-list = "prometheus"
  exporter-prometheus-port = 9898
}

support {
  ## spring
  spring {
    # auto proxy the DataSource bean
    datasource.autoproxy = false
  }
}

4,regustry.conf

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    #serverAddr = "localhost"
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

5,domain
CommonResult

package com.atguigu.springcloud.alibaba.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author wsk
 * @date 2020/3/25 20:37
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {

    private Integer code;
    private String message;
    private T       data;

    public CommonResult(Integer code, String message){
        this(code,message,null);
    }
}

Order

package com.atguigu.springcloud.alibaba.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
 * @author wsk
 * @date 2020/3/25 20:25
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private Long id;
    private Long userId;
    private Long productId;
    private Integer count;
    private BigDecimal money;
    private Integer status;
}

6,dao

package com.atguigu.springcloud.alibaba.dao;
import com.atguigu.springcloud.alibaba.domain.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * @author wsk
 * @date 2020/3/25 20:41
 */
@Mapper
public interface OrderDao {
    //1 新建订单
    void createOrder(Order order);

    //2 修改订单状态 从0改为1
    void update(@Param("userId") Long userId,@Param("status") Integer status);
}

7,xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.alibaba.dao.OrderDao">

    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Order">
        <id column="id" property="id" jdbcType="BIGINT"></id>
        <result column="user_id" property="userId" jdbcType="BIGINT"></result>
        <result column="product_id" property="productId" jdbcType="BIGINT"></result>
        <result column="count" property="count" jdbcType="INTEGER"></result>
        <result column="money" property="money" jdbcType="DECIMAL"></result>
        <result column="status" property="status" jdbcType="INTEGER"></result>
    </resultMap>

    <insert id="createOrder" parameterType="com.atguigu.springcloud.alibaba.domain.Order" useGeneratedKeys="true"
            keyProperty="id">
        insert into t_order(user_id,product_id,count,money,status) values (#{userId},#{productId},#{count},#{money},0);
    </insert>

    <update id="update">
        update t_order set status =1 where user_id =#{userId} and status=#{status};
    </update>
</mapper>

8,service
orderService

package com.atguigu.springcloud.alibaba.service;

import com.atguigu.springcloud.alibaba.domain.Order;

/**
 * @author wsk
 * @date 2020/3/25 20:58
 */
public interface OrderService {
    void create(Order order);


}

StorageService

package com.atguigu.springcloud.alibaba.service;

import com.atguigu.springcloud.alibaba.domain.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @author wsk
 * @date 2020/3/25 21:00
 */
@FeignClient(value = "seata-storage-service")
public interface StorageService {

    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count);
}

AccountService

package com.atguigu.springcloud.alibaba.service;
import com.atguigu.springcloud.alibaba.domain.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;

/**
 * @author wsk
 * @date 2020/3/25 21:00
 */
@FeignClient(value = "seata-account-service")
public interface AccountService {

    @PostMapping(value = "/account/decrease")
    CommonResult decrease(@RequestParam("userId") Long userId,@RequestParam("money") BigDecimal money);
}

9,serviceImpl

package com.atguigu.springcloud.alibaba.service.impl;
import com.atguigu.springcloud.alibaba.dao.OrderDao;
import com.atguigu.springcloud.alibaba.domain.Order;
import com.atguigu.springcloud.alibaba.service.AccountService;
import com.atguigu.springcloud.alibaba.service.OrderService;
import com.atguigu.springcloud.alibaba.service.StorageService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

/**
 * @author wsk
 * @date 2020/3/25 20:59
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderDao orderDao;
    @Resource
    private AccountService accountService;
    @Resource
    private StorageService storageService;

    /**
     * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
     * 简单说:
     * 下订单->减库存->减余额->改状态
     * GlobalTransactional seata开启分布式事务,异常时回滚,name保证唯一即可
     */
    @Override
    @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
    public void create(Order order) {
        log.info("----->开始创建新订单");
        //1 新建订单
        orderDao.createOrder(order);

        log.info("----->订单微服务开始调用库存,做扣减Count");
        //2 扣减库存
        storageService.decrease(order.getProductId(),order.getCount());
        log.info("----->订单微服务开始调用库存,做扣减end");

        log.info("----->订单微服务开始调用账户,做扣减Money");
        //3 扣减账户
        accountService.decrease(order.getUserId(),order.getMoney());
        log.info("----->订单微服务开始调用库存,做扣减end");

        //修改订单状态,从0到1,1代表以及完成
        log.info("----->修改订单状态开始");
        orderDao.update(order.getUserId(),0);
        log.info("----->修改订单状态结束");

        log.info("----->下订单结束了,O(∩_∩)O哈哈~");
    }
}

10.OrderController

package com.atguigu.springcloud.alibaba.controller;

import com.atguigu.springcloud.alibaba.domain.CommonResult;
import com.atguigu.springcloud.alibaba.domain.Order;
import com.atguigu.springcloud.alibaba.service.OrderService;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author wsk
 * @date 2020/3/25 21:24
 */
@RestController
public class OrderController {

    @Resource
    private OrderService orderService;
    @GetMapping("/order/create")
    public CommonResult create(Order order){
        orderService.create(order);
        return new CommonResult(200,"订单创建成功");
    }
}

11,config

package com.atguigu.springcloud.alibaba.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author wsk
 * @date 2020/3/25 21:26
 */
@Configuration
@MapperScan({"com.atguigu.springcloud.alibaba.dao"})
public class MyBatisConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    /**
     * @param sqlSessionFactory SqlSessionFactory
     * @return SqlSessionTemplate
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 从配置文件获取属性构造datasource,注意前缀,这里用的是druid,根据自己情况配置,
     * 原生datasource前缀取"spring.datasource"
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    /**
     * 构造datasource代理对象,替换原来的datasource
     *
     * @param druidDataSource
     * @return
     */
    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources(mapperLocations));

        SqlSessionFactory factory;
        try {
            factory = bean.getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return factory;
    }
}

12.主启动类

package com.atguigu.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author wsk
 * @date 2020/3/25 20:40
 */
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自带创建
public class SeataOrderMainApp2001 {
    public static void main(String[] args) {
        SpringApplication.run(SeataOrderMainApp2001.class,args);
    }
}

库存模块 seataStorageService2002
1,pom跟之前一样
2,xml

server:
  port: 2002


spring:
  application:
    name: seata-storage-service
  cloud:
    alibaba:
      seata:
        # 自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    # 当前数据源操作类型
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://47.115.93.213:3306/seata_storage?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: ljs
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info
#ribbon的超时时间
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

mybatis:
  mapper-locations: classpath*:mapper/*.xml

3,file.conf
跟上一个一样

4,registry.conf
跟上一个一样

5,domain
CommonResult跟之前一样
storage

package com.atguigu.springcloud.alibaba.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author wsk
 * @date 2020/3/25 21:45
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Storage {
    private Long id;

    /**
     * 产品id
     */
    private Long productId;

    /**
     * 总库存
     */
    private Integer total;

    /**
     * 已用库存
     */
    private Integer used;

    /**
     * 剩余库存
     */
    private Integer residue;
}

6,dao

package com.atguigu.springcloud.alibaba.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * @author wsk
 * @date 2020/3/25 21:47
 */
@Mapper
public interface StorageDao {
    /**
     * 减库存
     * @param productId
     * @param count
     * @return
     */
    int decrease(@Param("productId") Long productId, @Param("count") Integer count);
}

7,dao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.alibaba.dao.StorageDao">

    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Storage">
        <id column="id" property="id" jdbcType="BIGINT"></id>
        <result column="product_id" property="productId" jdbcType="BIGINT"></result>
        <result column="total" property="total" jdbcType="INTEGER"></result>
        <result column="used" property="used" jdbcType="INTEGER"></result>
        <result column="residue" property="residue" jdbcType="INTEGER"></result>
    </resultMap>

    <!--减库存-->
    <update id="decrease">
        update t_storage
        set used =used + #{count},residue= residue - #{count}
        where product_id=#{productId};
    </update>
</mapper>

8,service

package com.atguigu.springcloud.alibaba.service;
/**
 * @author wsk
 * @date 2020/3/25 21:51
 */
public interface StorageService {
    /**
     * 扣减库存
     * @param productId
     * @param count
     */
    void decrease(Long productId,Integer count);
}

9,serviceImpl

package com.atguigu.springcloud.alibaba.service;

import com.atguigu.springcloud.alibaba.dao.StorageDao;
import com.atguigu.springcloud.alibaba.service.StorageService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class StorageServiceImpl implements StorageService {

    @Resource
    private StorageDao storageDao;

    /**
     * 减库存
     *
     * @param productId
     * @param count
     * @return
     */
    @Override
    public void decrease(Long productId, Integer count) {
        storageDao.decrease(productId, count);
    }
}



10,controller
package com.atguigu.springcloud.alibaba.controller;
import com.atguigu.springcloud.alibaba.domain.CommonResult;
import com.atguigu.springcloud.alibaba.service.StorageService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @author wsk
 * @date 2020/3/25 21:55
 */
@RestController
public class StorageController {

    @Resource
    private StorageService storageService;

    @RequestMapping("/storage/decrease")
    public CommonResult decrease(Long productId, Integer count){
        storageService.decrease(productId,count);
        return new CommonResult(200,"扣减库存成功");
    }
}

11,config
和之前一样

12,主启动类和之前一样

seata-account-service2003
1,pom一样
2。xml

server:
  port: 2003
spring:
  application:
    name: seata-account-service
  cloud:
    alibaba:
      seata:
        # 自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    # 当前数据源操作类型
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://47.115.93.213:3306/seata_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: ljs
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info
#ribbon的超时时间
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

mybatis:
  mapper-locations: classpath*:mapper/*.xml

3,file.conf
一样
4,registry.conf
一样
5,domain
CommonResult一样
Account

package com.atguigu.springcloud.alibaba.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 账户实体类
 *
 * @author zzyy
 * @date 2020/3/8 12:28
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
    private Long id;

    /**
     * 用户id
     */
    private Long userId;

    /**
     * 总额度
     */
    private Integer total;

    /**
     * 已用额度
     */
    private Integer used;

    /**
     * 剩余额度
     */
    private Integer residue;
}

6,dao

package com.atguigu.springcloud.alibaba.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
/**
 * @author wsk
 * @date 2020/3/25 22:05
 */
@Mapper
public interface AccountDao {

    /**
     * 扣减账户余额
     * @param userId
     * @param money
     */
    void decrease(@Param("userId") Long userId, @Param("money")BigDecimal money);
}

7,dao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.alibaba.dao.AccountDao">

    <update id="decrease">
        update t_account set residue = residue- #{money},used = used + #{money}
        where user_id =#{userId};
    </update>
</mapper>

8,service

package com.atguigu.springcloud.alibaba.service;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
/**
 * @author wsk
 * @date 2020/3/25 22:09
 */
public interface AccountService {
    /**
     * 扣减账户余额
     */
    void decrease(@RequestParam("userId") Long userId, @RequestParam("money")BigDecimal money);
}

9,serviceImpl

package com.atguigu.springcloud.alibaba.service.impl;
import com.atguigu.springcloud.alibaba.dao.AccountDao;
import com.atguigu.springcloud.alibaba.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
/**
 * @author wsk
 * @date 2020/3/25 22:10
 */
@Service
public class AccountServiceImpl implements AccountService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);

    @Resource
    AccountDao accountDao;

    @Override
    public void decrease(Long userId, BigDecimal money) {
        LOGGER.info("------>account-service中扣减账户余额开始");
        //模拟异常,全局事务回滚
       // int age=10/0;
        accountDao.decrease(userId,money);
        LOGGER.info("------>account-service中扣减账户余额结束");
    }
}

10,controller

package com.atguigu.springcloud.alibaba.controller;
import com.atguigu.springcloud.alibaba.domain.CommonResult;
import com.atguigu.springcloud.alibaba.service.AccountService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
 * @author wsk
 * @date 2020/3/25 22:14
 */
@RestController
public class AccountController {

    @Resource
    AccountService accountService;

    @RequestMapping("/account/decrease")
    public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money")BigDecimal money){
        accountService.decrease(userId,money);
        return new CommonResult(200,"扣减账户余额成功");
    }
}

11,config
一样

12,主启动类
一样

然后访问localhost:2001/order/create?userId=1&productId=1&count=10&money=100

将会完成数据库操作

这时候在AccountServiceImpl里模拟异常
int age=10/0;

将会导致数据库。。。。。数据不一致的问题。。。

如何让全局事务回滚?

在事务发起者OrderServiceImpl方法前上写
@GlobalTransactional(name = “fsp-create-order”,rollbackFor = Exception.class)

                              seata原理

尽量使用1.0版本,支持集群,1.0版本支持大规模商用
TC:seata服务器
TM:事务的发起方
RM:数据库

AT模式:两阶段提交
一阶段:加载。seata拦截业务SQL在我们执行sql前seata保存到 before image
执行业务sql后保存成 after image。再加一个行锁。保证了一个阶段的原子性

二阶段:业务sql在一阶段已经提交至数据库。seata框架只需将一阶段保存的快照数据(after和before和行锁)删掉。完成数据的清除即可

二阶段回滚:
利用反向补偿机制。seata回滚。阶段已经执行的业务sql,还原业务数据。回滚则用before image还原业务数据。还原前校验脏写。对比数据库当前业务数据库和
after image的数据完全一致,则没有脏写。出现脏写。则转人工处理

本文地址:https://blog.csdn.net/ljs13168734665/article/details/107125778