spring cloud & spring cloud alibaba study note

一、技术选型

在这里插入图片描述

服务注册中心:Nacos/ZooKeeper/Consul

服务调用:Ribbon/LoadBalancer

服务调用2:OpenFegin

服务降级:sentinel

服务网关:gateway

服务配置:Nacos

服务总线:Nacos(bus+stream)

事务管理:Seata

二、工程搭建

1.父工程

新建一个maven工程

删除src文件夹,取消单元测试

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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.poplar.cloud2020</groupId>
    <artifactId>springcloudTest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <!--统一管理jar包版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.18.10</lombok.version>
        <mysql.version>8.0.29</mysql.version>
        <druid.version>1.1.10</druid.version>
        <mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
    </properties>

    <!--子模块继承之后,提供作用:锁定版本+子module不用写groupId和version-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </dependency>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud 阿里巴巴-->
            <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>
            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
            </dependency>
            <!-- druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!--mybatis-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>

    </dependencyManagement>

</project>

2.rest接口

1指定端口号

在这里插入图片描述

3.eureka(服务注册)集群搭建

1.单机版yml文件

server:
  port: 7001

eureka:
  instance:
    hostname: localhost #eureka服务端的实例名称
  client:
      #false表示不用自己注册自己
    register-with-eureka: false
      #false表示自己就是注册中心,自身职责是维护服务实例,不需要检索服务
    fetch-registry: false
    service-url:
        #设置与rureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.单机版80yml
server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进eureka默认为true
    register-with-eureka: true
    #是否从eurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
2.单机版8001yml
server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/clouddb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 111111
      db-type: mysql

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.poplar.cloud2020.entities

eureka:
  client:
    #表示是否将自己注册进eureka默认为true
    register-with-eureka: true
    #是否从eurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

2.修改配置文件

在这里插入图片描述
在这里插入图片描述

3.集群版yml文件

1.7001
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
      #false表示不用自己注册自己
    register-with-eureka: false
      #false表示自己就是注册中心,自身职责是维护服务实例,不需要检索服务
    fetch-registry: false
    service-url:
        #设置与rureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7002.com:7002/eureka/
2.7002
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
    #false表示不用自己注册自己
    register-with-eureka: false
    #false表示自己就是注册中心,自身职责是维护服务实例,不需要检索服务
    fetch-registry: false
    service-url:
      #设置与rureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/
3.80
server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进eureka默认为true
    register-with-eureka: true
    #是否从eurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.8001
server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/clouddb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 111111
      db-type: mysql

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.poplar.cloud2020.entities

eureka:
  client:
    #表示是否将自己注册进eureka默认为true
    register-with-eureka: true
    #是否从eurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

4.注册进入zookeeper(服务注册)

1.安装zookeeper

docker search zookeeper
docker pull zookeeper:3.4.9
docker images
docker run -p 2181:2181 --name zookeeper01 -d zookeeper:3.4.9
docker exec -it zookeeper01 zkCli.sh
ls /

在这里插入图片描述

get /zookeeper

在这里插入图片描述

2.cloud-provider-payment moudle搭建

1.pom文件
    <dependencies>
        <!--引用自己定义的api通用包-->
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--springCloud整合zookeeper-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!--先排除自带的zookeeper3.5.3-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--使用与os内一样的zookeeper版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
            <!--排除这个slf4j-log4j12-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </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>
        <!--绑定-->
        <!--开启热部署devTools-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>

如果没有

<exclusion>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
</exclusion>

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.9</version>
</dependency>

会报错

在这里插入图片描述

这是由于自带的默认zookeeper版本号是3.5.3-beta版本而我们CentOS上的版本是3.4.9

如果没有

<exclusions>
	<exclusion>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	</exclusion>
</exclusions>

则会报错
在这里插入图片描述

这是由于zookeepr包内自带slf4j-log4j12日志包,和我们的sl4j有冲突(即使不加@sl4j注解也会报错)

2.yml文件
server:
  port: 8003

spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.216.133:2181
3.运行正常

在这里插入图片描述

这时候进入zookeeper查看

ls /

在这里插入图片描述
可以看到多出一个services

ls /services

在这里插入图片描述
可以看到我们上文在yml文件中配置的cloud-provider-payment

ls /services/cloud-provider-payment

在这里插入图片描述

get /services/cloud-provider-payment/2be8abfb-1ea1-46db-a9b8-8dfd8c72d9c9

在这里插入图片描述

4.代码核对
1.controller

在这里插入图片描述

2.本地访问8003端口

打开localhost:8003/payment/zk可以看到如下界面
在这里插入图片描述

5.consul(服务注册)

1.consul启动

在consul.exe所在路径进入命令行
在这里插入图片描述

consul agent -dev

在这里插入图片描述
再打开 http://localhost:8500
在这里插入图片描述

2.cloud-provider-payment moudle搭建

1.pom文件
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</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>
        <!--绑定-->
        <!--开启热部署devTools-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>

    </dependencies>
2.yml文件
server:
  port: 8004
spring:
  application:
    name: cloud-provider-payment

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname:127.0.0.1
        service-name: ${spring.application.name}
3.运行正常

在这里插入图片描述
在这里插入图片描述

6.CAP

一个分布式系统不可能同时很好地满足一致性,可用性和分区容错性这三个需求,因此,根据CAP原理将NoSQL数据库分成了满足CA,CP,AP原则三大类

c:consistency:强一致性

a:availablity:可用性

p:partition tolerance:分区容错性

CA:单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大

CP:满足一致性,分区容忍性的系统,通常性能不是很高(zookeeper,consul)

AP:满足可用性,分区容忍性的系统,通常对一致性的要求低一点(eureka)

在这里插入图片描述

7.Ribbon(服务调用+负载均衡)(进程内负载均衡)(负载均衡+RestTemplate调用)

1.配合eureka

与eureka工作时分两步

1.选择EurekaServer,优先选择在同一个区域内负载较少的server

2.根据用户指定的策略(轮询,随机,根据响应时间加权),从server取到的服务注册列表中选择一个地址

2.相应的规则

在这里插入图片描述

8.openFeign(服务调用)(继承ribbon)

1.日志级别

None:默认的,不显示任何日志

Basic:仅记录请求方法,url,响应状态码和执行时间

Headers:除了basic中定义的信息外,还有请求和响应的头信息

Full:除了headers中定义的信息外,还有请求和响应的正文及数据

添加配置类

@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

application.yml

logging:
  level:
    com.poplar.cloud2020.service.PaymentService: debug

9.Hystrix(服务降级、服务熔断、服务限流)

1.服务降级(fallback)

不让客户端等待,立刻返回一个友好提示(兜底)

在可能出现问题的方法上增加

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")//设置峰值
    })

并新建方法名为 paymentInfo_TimeOutHandler 的方法

    public String paymentInfo_TimeOutHandler(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + "****paymentInfo_TimeOutHandler****OvO";
    }

去启动类新增 @EnableCircuitBreaker 注释

2.服务熔断(fallbreak)

1.Hystrix监控(DashBoard)
1.9001pom
        <!--hystrix仪表盘图形化界面-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
2.9001yml
server:
  port: 9001
spring:
  application:
    name: payment-provider-hystrix-dashboard
3.9001启动类
@EnableHystrixDashboard
4.被监控8005主启动类
    //此配置为了服务监控而配,与服务容错本身无关,SpringCloud升级后的问题
    //ServletRegistrationBean因为springboot的默认路径不是“/hystrix.stream”
    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricStreamServlet");
        return registrationBean;
    }
5.localhost:9001/hystrix

在这里插入图片描述

6.访问8005
http://localhost:8005/payment/getpaymentok/1
http://localhost:8005/payment/getpayment/break/-1

在这里插入图片描述

3.服务限流(flowlimit)

10.gateway(服务网关)

1.特性:

基于spring framework 5,project reactor和spring boot 2.0 进行构建

①动态路由,能够匹配任何请求属性
②对路由指定Predicate(断言)和Filter(过滤器)
③集成Hystrix的断路器功能
④集成SpringCloud服务发现功能
⑤易于编写的Predicate(断言)和Filter(过滤器)
⑦请求限流功能
⑧支持路径重写

2.pom

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--引用自己定义的api通用包-->
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>

注意 --gateway不能与spring-boot-starter-web和spring-boot-starter-actuator同时使用–

3.yml

server:
  port: 9527

spring:
  application:
    name: payment-gateway-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh1 #paument_route #路由器的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**

        - id: payment_routh2 #paument_route #路由器的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001
          predicates:
            - Path=/payment/lb/**

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    register-with-eureka: true
    fetch-registry: true
  instance:
    instance-id: payment8005 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址
    hostname: cloud-gateway-service

4.启动类

@SpringBootApplication
@EnableEurekaClient
public class PaymentGateWayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentGateWayMain9527.class, args);
    }
}

5.启动成功

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.Config配置

1.pom

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.yml

server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/Yyj814/springCloudConfig.git
          search-paths: springCloudConfig
          default-label: main
          username: Yyj814
          password: 2331886187yyj

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

3.启动类

@SpringBootApplication
@EnableConfigServer
public class ConfigCenterMain3344 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigCenterMain3344.class, args);
    }
}

4.hosts文件

127.0.0.1 config3344.com

5.在此之前已经在github上建立了相关repository

6.启动成功

在这里插入图片描述
在这里插入图片描述

7.客户端(client)

application.yml是用户级的资源配置项

bootstrap.yml是系统级的资源配置项,优先级更高

1.pom
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
2.bootstrap.yml
server:
  port: 3355
spring:
  application:
    name: config-client
  cloud:
    config:
      label: main
      profile: dev
      name: config
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: config3355 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址
3.启动类
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientMain3355.class, args);
    }
}
4.启动成功

在这里插入图片描述
在这里插入图片描述

5.动态刷新

如果修改gitee上的代码,3344可以立几生效,而3355则需要重启,这就需要动态刷新

1.(已完成)增加actuator
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
2.修改yml

增加

#暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"
3.controller类

增加**@RefreshScope**注解

4.curl
curl -X POST "http://localhost:3355/actuator/refresh"

12.bus消息总线(对config的加强)

1.安装RabbitMQ

1.安装erlang
2.安装rabbitMQ server
3.http://localhost:15672

用户名:guest

密码:guest

4.pom
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </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>

    </dependencies>
5.bootstrap.yml
server:
  port: 3366
spring:
  application:
    name: config-client
  cloud:
    config:
      label: main
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: config3366 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址

#暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"
6.3366controller
@RestController
@Slf4j
public class ConfigClientController {
    @Value("${config.info}")
    private String configInfo;

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

    @GetMapping("/configInfo")
    public String getInfo() {
        return "serverPort: " + serverPort + " configInfo: " + configInfo;
    }
}
7.3344pom新增
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
8.3344yml
server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/Yyj814/springCloudConfig.git
          search-paths: springCloudConfig
          default-label: main
          username: Yyj814
          password: 2331886187yyj
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: config3344 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址

management:
  endpoints: #暴露bus刷新配置的端点
    web:
      exposure:
        include: 'bus-refresh'
9.3355pom新增
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
10.3355yml新增
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
11.3366pom新增
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
12.3366yml新增
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
13.全部启动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://config3344.com:3344/config-dev.yml
在这里插入图片描述

localhost:3355/configInfo

在这里插入图片描述

localhost:3366/configInfo
在这里插入图片描述

修改gitee上的版本号
在这里插入图片描述

3344刷新
在这里插入图片描述

curl -X POST "http://localhost:3344/actuator/bus-refresh"

在这里插入图片描述
在这里插入图片描述

定向通知

在这里插入图片描述

13.stream(消息驱动)

spring cloud stream 是一个构建消息驱动微服务的框架

应用程序通过inputs或者outputs来与springcloud stream中的binder对象交互,通过我们配置来绑定,而Spring Cloud Stream的binder对象负责与消息中间件交互

1.主要模式

发布-订阅模式 消息生产者:output 消息消费者:input 使用topic进行广播(在RabbitMQ中是exchange,在Kafka中是Topic)

2.常用API和注解

  • Middleware ----------中间件,目前只支持RabbitMQ和Kafka

  • Binder -----------中间件的封装

  • @Input ------------输入通道

  • @Output ------------输出通道

  • @StreamListener -----监听队列

  • @EnableBinding ----通道channel与exchange绑定在一起

3.8801-provider

1.pom
    <dependencies>
        <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.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </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>
    </dependencies>
2.yml
server:
  port: 8801
spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitMQ信息
        defaultRabbit: #表示定义的名称,用于与binding整合
          type: rabbit #消息组件类型
          environment:  #设置rabbitMQ相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: #服务的整合处理
        output: #是一个通道的名称
          destination: studyExchange #要使用的exchange名称定义
          content-type: application/json #消息类型  文本是"text/plain"
          binder: defaultRabbit #要绑定的消息服务具体设置

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: config8801 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址
3.主启动类
@SpringBootApplication
public class StreamProviderMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamProviderMain8801.class, args);
    }
}
4.servcie&serviceImpl
1.service
public interface IMessageProvideService {
    String send();
}
2.serviceImpl
@EnableBinding(Source.class)//定义消息的推送管道
public class IMessageProvideServiceImpl implements IMessageProvideService {

    @Resource
    private MessageChannel output;//消息发送管道

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("serial: " + serial);
        return null;
    }
}
5.controller
@RestController
public class SendMessageController {
    @Resource
    private IMessageProvideService provideService;

    @GetMapping(value = "/sendMessage")
    public String sendMessage() {
        return provideService.send();
    }
}
6.启动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.8802-consumer

1.pom
    <dependencies>
        <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.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </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>
    </dependencies>
2.yml
server:
  port: 8802
spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitMQ信息
        defaultRabbit: #表示定义的名称,用于与binding整合
          type: rabbit #消息组件类型
          environment:  #设置rabbitMQ相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: #服务的整合处理
        input: #是一个通道的名称
          destination: studyExchange #要使用的exchange名称定义
          content-type: application/json #消息类型  文本是"text/plain"
          binder: defaultRabbit #要绑定的消息服务具体设置

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: stream-consumer8802 #rureka显示主机名
    prefer-ip-address: true #访问路径显示ip地址
3.controller
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageController {
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    private void input(Message<String> message) {
        System.out.println("消费者1号,接收到的消息:---->" + message.getPayload() + " port: " + serverPort);
    }
}
4.启动

在这里插入图片描述
在这里插入图片描述

localhost:8801/sendMessage

在这里插入图片描述
在这里插入图片描述

5.分组消费与持久化

1.再建一个和8802相同的8803
2.启动7001,7002,8801,8802,8803
3.存在问题:重复消费(原因:默认的group是不同的)

在这里插入图片描述

4.解决

自定义配置分组,自定义配置为同一个组,解决重复消费问题

1.8802

在这里插入图片描述

2.8803

在这里插入图片描述

14.(sleuth)分布式请求链路跟踪(sleuth负责搜集整理)

1.zipkin(负责展现)

下载:https://repo1.maven.org/maven2/io/zipkin/zipkin-server/

安装:在下载路径

java -jar .....

在这里插入图片描述

localhost:9411

在这里插入图片描述
在这里插入图片描述

Trace:Span集合,表示一条调用链路,存在唯一标识

Span:表示调用链路来源,通俗理解就是一次请求信息

在要监控的项目中增加

1.pom
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
2.yml
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1 #介于0-1,1表示全部采集

在这里插入图片描述

3.8001controller
    @GetMapping(value = "/payment/zipkin")
    public String paymentZipKin() {
        return "zipkin  QAQ";
    }
4.80controller
    @GetMapping(value = "/consumer/payment/zipkin")
    public String getZipKin() {
        return restTemplate.getForObject("http://localhost:8001/payment/zipkin", String.class);
    }
5.启动

在这里插入图片描述
在这里插入图片描述

15.spring cloud alibaba

1.Nacos(服务注册与配置中心) Naming Configuration Service = Eureka+Bus+Config

1.下载

https://github.com/alibaba/nacos/tags

2.安装

bin->cmd startup.cmd -m standalone

安装成功后访问 http://localhost:8848/nacos

用户名:nacos

密码:nacos
在这里插入图片描述

3.服务提供者6001
1.pom
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
2.yml
server:
  port: 6001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'
3.启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderMain6001 {
    public static void main(String[] args) {
        SpringApplication.run(NacosProviderMain6001.class, args);
    }
}
4.controller
@RestController
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id) {
        return "nacos registry, serverPort: " + serverPort + " id: " + id;
    }
}
4.启动
1.启动nacos
2.localhost:6001/payment/nacos/1

在这里插入图片描述

3.nacos网站

在这里插入图片描述

4.新建虚拟6001并指定端口号为6002

在这里插入图片描述
ctrl+D
在这里插入图片描述

修改名字
在这里插入图片描述

点击之后选择 Add Vm options

在这里插入图片描述
输入

-DServer.port=6002

然后运行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.服务消费者83
1.pom
    <dependencies>
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
2.yml
server:
  port: 83

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

#消费者想要访问的微服务名称(注册成功进nacos的服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider
3.主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerMain83 {
    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerMain83.class, args);
    }
}
4.配置类
@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
5.controller
@RestController
@Slf4j
public class OrderController {
    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String ServerURL;

    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String getOrder(@PathVariable("id") Long id) {
        return restTemplate.getForObject(ServerURL + "/payment/nacos/" + id, String.class);
    }
}
6.启动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里可以看到也实现了负载均衡
在这里插入图片描述

切换Nacos的CP和AP模式

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
7.config配置3377
1.pom
    <dependencies>
        <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>
        <!--绑定-->
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
2.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: yaml #指定yaml格式的配置
3.application.yml
spring:
  profiles:
    active: dev
4.启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfigClientMain3377 {
    public static void main(String[] args) {
        SpringApplication.run(NacosConfigClientMain3377.class, args);
    }
}
5.controller
@RestController
@Slf4j
@RefreshScope //支持nacos的动态刷新
public class ConfigClientController {
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getInfo() {
        return configInfo;
    }
}
6.官网配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.启动3377

在这里插入图片描述

即使在nacos中修改配置后
在这里插入图片描述

刷新localhost:3377/config/info

在这里插入图片描述

可以看见实现了动态刷新

8.NameSpace+Group+Data ID

在这里插入图片描述

默认情况

NameSpace = public

Group = DEFAULT_GROUP

Cluster = DEFAULT

配置两个不同group的同名yaml文件

在这里插入图片描述

application.yml

在这里插入图片描述

bootstrap.yml

在这里插入图片描述

访问localhost:3377/config/info

在这里插入图片描述

将bootstrap.yml改成

在这里插入图片描述

再次访问localhost:3377/config/info

在这里插入图片描述

新建两个命名空间dev 和 test

在这里插入图片描述

bootstrap.yml修改,增加dev对应的namespace
在这里插入图片描述

9.配置nacos集群
1.将nacos默认数据库从derby换成mysql
1.执行自带的sql

在这里插入图片描述
在这里插入图片描述

2.application.properties

修改如下
在这里插入图片描述

将这些打开,账号密码设置为自己的

3.重启nacos和3377

startup.cmd -m standalone
在这里插入图片描述

在这里插入图片描述

3377访问正常

新建配置后在mysql的config_info表中可以看到有数据新增
在这里插入图片描述
配置成功

2.linux部署nacos集群
1.nginx1+nacos3+mysql*1
1. mysql建库建表
2. 修改application.properties
3. 将cluster.conf.example复制一份为cluster.conf
hostname -i

在这里插入图片描述

可以看到自己的ip

去cluster.conf中进行修改
在这里插入图片描述

4. 编辑nacos的启动脚本startup.sh

在这里插入图片描述

本次使用2.1.0可以看到默认p被占用,则修改为其他
在这里插入图片描述
在这里插入图片描述

2.sentinel(细粒度化的配置流控,速率控制,服务熔断,服务降级)

1.下载

https://github.com/alibaba/Sentinel/releases/tag/1.8.5

2.组成
1.核心库(java客户端)
2.控制台(dashBoard)
3.安装
java -jar sentinel-dashboard-1.8.5.jar

在这里插入图片描述

访问localhost:8080

在这里插入图片描述

账号:sentinel

密码:sentinel

4.8401
1.pom
    <dependencies>
        <!--springCloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--持久化-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--openFeign-->
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
2.application.yml
server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: '*'
3.controller
@RestController
public class FlowLimitController {

    @GetMapping("/testA")
    public String testA() {
        return "this is Test-A";
    }

    @GetMapping("/testB")
    public String testB() {
        return "this is Test-B";
    }
}
4.启动类
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelServiceMain8401 {
    public static void main(String[] args) {
        SpringApplication.run(SentinelServiceMain8401.class, args);
    }
}
5.启动
1.启动nacos

startup.cmd -m standalone

2.启动sentinel

java -jar sentinel-dashboard-1.8.5.jar

3.启动8401

在这里插入图片描述

4.访问localhost:8401/testA或者localhost:8401/testB

在这里插入图片描述
在这里插入图片描述

5.刷新sentinel

在这里插入图片描述

5.流控
1.QPS

每秒请求数
在这里插入图片描述

表示每秒只允许单击访问一次,超过则直接失败

多次刷新localhost:8401/testA
在这里插入图片描述
可以看到被限流

2.线程数
3.关联

当关联的资源达到阈值则限流自己,这时候用A关联B
在这里插入图片描述
在这里插入图片描述

这时候去访问A,可以看到如下效果

在这里插入图片描述

4.链路

多个请求调用了同一个微服务

5.warm up(预热)

公式:阈值/coldFactor(默认为3)

6.匀速排队

阈值必须为QPS

6.服务降级
1.RT

平均响应时间,超出阈值且在时间窗口内通过的请求≥5,两个条件同时满足后触发降级

窗口期过后关闭断路器

RT时间最大为49.00(更大时间则需要-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

2.异常比例

QPS≥5且异常比例(秒级)超过阈值触发降级,时间窗口结束后关闭降级

7.热点规则(hot key)(正对频繁访问的进行限流)
1.controller
    @GetMapping("/testC")
    @SentinelResource(value = "testC",blockHandler = "deal_hotKey")//名称唯一即可
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2) {
        return "testC";

    }

    //testHotKey兜底方法
    public String deal_hotKey(String p1, String p2, BlockException exception) {
        return "deal_hotKey";

    }

表示如果在调用/testC时违背了sentinel的规则,则调用deal_hotKey方法

2.配置

在这里插入图片描述

testC对应 SentinelResource(value = “testC” 参数索引从0开始,我们上文有p1和p2所以这里的索引1指的是p2

这里的意思是调用testC这个方法时每秒访问p2次数超过1则熔断,经过1秒后尝试恢复

正常访问

在这里插入图片描述

多次刷新,可以看到进入了兜底方法
在这里插入图片描述

1.参数例外项

想要被限流又不想被限流(想限流p2又不想限流p2=5)

在这里插入图片描述

意思是第二个参数每秒访问超过1次则被限流,但是当为String类型的p2值为5时,访问超过200才被限流

在这里插入图片描述

即使疯狂刷新也没用(没达到每秒200次访问)

注意 @SentinelResource 只处理违反Sentinel控制台配置的情况,当程序出现错误(java运行错误时还是会返回异常页面)

8系统自适应规则

全局适配,很少使用

9.@SentinelResource
1.按资源名称限流
1.8401.pom新增
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
2.新建RateLimitController
@RestController
public class RateLimitController {
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "按资源名称限流", new Payment(2022L, "serial001"))
    }

    public CommonResult handleException(BlockException exception) {
        return new CommonResult(404, exception.getClass().getCanonicalName() + " 服务不可用");
    }
}
3.启动8401
4.控制台配置

在这里插入图片描述

疯狂刷新

在这里插入图片描述

2.按URL限流
1.RateLimitController里新增方法
    @GetMapping("/byUrl")
    @SentinelResource(value = "buUrl")
    public CommonResult byUrl() {
        return new CommonResult(200, "按URL限流", new Payment(1021L, "serial0407"));
    }
3.启动8401
4.控制台配置

在这里插入图片描述

注意上面资源名和getMapping里的路径是一致的(都加/)

配置成功后疯狂刷新

在这里插入图片描述

可以看到我们没有配置兜底方案后用的是sentinel的默认报错

3.兜底方案存在问题及解决
1.问题:
  • 系统默认的没有体现出我们自己的业务需求

  • 依照现有条件,我们自定义的方法和业务方法耦合在一块,不直观

  • 每个业务类都添加一个兜底方法,代码膨胀

  • 全局统一的处理方法没有实现

2.解决(客户自定义限流处理逻辑)

新建myhandler.CustomerBlockHandler类

public class CustomerBlockHandler {
    public static CommonResult handlerExceptionFir(BlockException exception) {
        return new CommonResult(600, "自定义处理,global handlerExceptionFir" +
                exception.getMessage());
    }

    public static CommonResult handlerExceptionSec(BlockException exception) {
        return new CommonResult(600, "自定义处理,global handlerExceptionSec" +
                exception.getMessage());
    }
}

byUrl方法代码修改为

    @GetMapping("/byUrl")
    @SentinelResource(value = "byUrl",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerExceptionSec")
    public CommonResult byUrl() {
        return new CommonResult(200, "按URL限流", new Payment(1021L, "serial0407"));
    }

启动

添加限流后狂刷出现
在这里插入图片描述
说明配置成功

4.sentinel其他注解属性

Sentinel三大核心API

  • SphU:定义资源

  • Tracer:定义统计

  • ContextUtil:定义上下文

10.服务熔断(sentinel熔断器没有半开状态)
1.OpenFeign+Nacos+Sentinel
1.9003

pom

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>

yml

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'

启动类

@SpringBootApplication
@EnableDiscoveryClient
public class NacosPaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(NacosPaymentMain9003.class, args);
    }
}

controller

@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, "Ssqajsijao5A4S6546D54D541"));
        hashMap.put(2L, new Payment(2l, "Ssqajsijao5A4S6546D54D542"));
        hashMap.put(3L, new Payment(3l, "Ssqajsijao5A4S6546D54D543"));
        hashMap.put(4L, new Payment(4l, "Ssqajsijao5A4S6546D54D544"));
    }

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

pom

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>

yml

server:
  port: 9004

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'

启动类

@SpringBootApplication
@EnableDiscoveryClient
public class NacosPaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(NacosPaymentMain9003.class, args);
    }
}

controller

@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, "Ssqajsijao5A4S6546D54D541"));
        hashMap.put(2L, new Payment(2l, "Ssqajsijao5A4S6546D54D542"));
        hashMap.put(3L, new Payment(3l, "Ssqajsijao5A4S6546D54D543"));
        hashMap.put(4L, new Payment(4l, "Ssqajsijao5A4S6546D54D544"));
    }

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

pom

    <dependencies>
        <dependency>
            <groupId>com.poplar.cloud2020</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>

yml

server:
  port: 84
spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

#消费者将要去访问的服务者名称
service-url:
  nacos-suer-service: http://nacos-payment-provider

启动类

@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain84 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain84.class, args);
    }
}

配置类

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

controller

@RestController
@Slf4j
public class CircleBreakController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/fallback/{id}")
    public CommonResult<Payment> fallBack(@PathVariable Long id) {
        CommonResult result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, CommonResult.class, id);
        if (id == 4) {
            throw new IllegalArgumentException("非法参数异常= =!");
        } else if (result.getData() == null) {
            throw new NullPointerException("没有对应记录,空指针异常TuT--");
        }
        return result;
    }
}
4.启动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.目前结论

如果没有任何配置,返回的是error页面

6.为fallBack增加兜底方法
    public CommonResult<Payment> handlerFallBack(@PathVariable Long id, Throwable exception) {
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(800, "handlerFallBack兜底异常,异常内容: " + exception.getMessage(), payment);
    }

同时fallBack变为

    @GetMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback")
    @SentinelResource(value = "fallback", fallback = "handlerFallBack")
    public CommonResult<Payment> fallBack(@PathVariable Long id) {
        CommonResult result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, CommonResult.class, id);
        if (id == 4) {
            throw new IllegalArgumentException("非法参数异常= =!");
        } else if (result.getData() == null) {
            throw new NullPointerException("没有对应记录,空指针异常TuT--");
        }
        return result;
    }
7.这时候变为

在这里插入图片描述
在这里插入图片描述

8.可以看到fallback管运行异常(相当于服务降级)
9.修改

fallBack

    @GetMapping("/consumer/fallback/{id}")
//    @SentinelResource(value = "fallback") //①
//    @SentinelResource(value = "fallback", fallback = "handlerFallBack") //②
    @SentinelResource(value = "fallback", blockHandler = "blockHandler") //③
    public CommonResult<Payment> fallBack(@PathVariable Long id) {
        CommonResult result = restTemplate.getForObject(SERVICE_URL + "/payment/" + id, CommonResult.class, id);
        if (id == 4) {
            throw new IllegalArgumentException("非法参数异常= =!");
        } else if (result.getData() == null) {
            throw new NullPointerException("没有对应记录,空指针异常TuT--");
        }
        return result;
    }

blockHandler

    public CommonResult<Payment> blockHandler(@PathVariable Long id, BlockException exception){
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(801, "blockHandler--sentinel限流,异常内容: " + exception.getMessage(), payment);
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

狂刷之后

在这里插入图片描述

10.blockhandler管sentinel配置违规
11.fallback和blockhandler各管各的(最顶端还是sentinel)
12.加入OpenFeign

84pom

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

84yml

#激活sentinel对feign支持
feign:
  sentinel:
    enabled: true

84主启动

@EnableFeignClients

84service

1.PaymentService

@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallBackService.class)
public interface PaymentService {
    @GetMapping(value = "/payment/{id}")
    CommonResult<Payment> payment(@PathVariable("id") Long id);

}

2.PaymentFallBackService

@Component
public class PaymentFallBackService implements PaymentService {
    @Override
    public CommonResult<Payment> payment(Long id) {
        return new CommonResult<>(888, "服务降级返回,PaymentFallBackService", new Payment(id, "error" + id));
    }
}

3.controller

    @Resource
    private PaymentService paymentService;

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

4.启动

此时关闭9003和9004访问84
在这里插入图片描述
发现服务降级返回,并没有报错

11.sentinel配置规则持久化

将限流配置持久化规则写进nacos中

1.pom
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
2.yml
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            data-id: cloud-alibaba-sentinel-service
		   group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow

在这里插入图片描述

3.nacos新建配置

在这里插入图片描述

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
        "resource": 资源名称
        "limitApp": 来源应用
        "grade": 阈值类型, 0直接 1关联 2链路
        "count": 单机阈值
        "strategy": 流控模式, 0直接 1关联 2链路
        "controlBehavior": 流控效果, 0快速失败 1warm up 2排队等待
        "clusterMode": 是否集群
4.启动8401

使用 localhost:8401/byUrl访问 进行刷新sentinel
在这里插入图片描述

可以发现刚刚配置的规则存在

3.seata(分布式事务管理,解决全局数据一致性问题)

1.典型的分布式事务过程(一个全局事务ID+三组件)
2.术语(三大组件)
  • TC:事务协调者,维护全局和分支事务的状态,驱动全局事务提交或回滚

  • TM:事务管理器,定义全局事务范围,开始全局事务、提交或回滚

  • RM:资源管理器,管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

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

https://github.com/seata/seata/releases/tag/v1.0.0

5.安装
1.修改file.conf(自定义事务组名称+事务日志存储模式为db+数据库连接信息)

源文件

在这里插入图片描述
新建一个名为seata的数据库
在这里插入图片描述
修改后
在这里插入图片描述
建表
在这里插入图片描述

在这里插入图片描述

2.修改registry.conf

原版

在这里插入图片描述

修改后
在这里插入图片描述

3.将0.9版本lib下的mysql5删除,换成mysql8
4.启动nacos

startup.cmd -m standalone

5.启动seata

双击 seata-server.bat

6.创建三个库

seata_order

seata_account

seata_storage

7.创建服务-订单服务1021
1.建表(seata_order库下)
create table t_order(
id bigint(11) auto_increment primary key,
user_id bigint(11) default null,
product_id bigint(11) default null,
count int(11) default null,
money decimal(11) default null,
statu bigint(11) default null
)
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;
2.pom
    <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>0.9.0</version>
        </dependency>
        <!--openFeign-->
        <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>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
3.yml
server:
  port: 1021
spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 111111
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis:
  mapper-locations: classpath:mapper/*.xml
4.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 = "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://127.0.0.1:3306/seata"
    user = "root"
    password = "111111"
    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.registry.conf
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    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包下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    private Long id; //订单id

    private Long userId;  //用户id

    private Long productId;  //产品id

    private Integer count;  //产品数量

    private BigDecimal money;  //产品价格

    private Integer statu; //订单状态  0:创建中  1:已完成

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

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

}
6.dao包下
@Mapper
public interface OrderDao {
    //新建订单
    void create(Order order);

    //修改订单状态 0->1
    void update(@Param("userId")Long userId,@Param("statu")Integer statu);

}
7.service包下
@FeignClient(value = "seata-account-service")
public interface AccountService {
    @PostMapping(value = "/account/decline")
    CommonResult decline(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
public interface OrderService {
    void create(Order order);
}
@FeignClient(value = "seata-storage-service")
public interface StorageService {
    @PostMapping(value = "/storage/decline")
    CommonResult decline(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
8.service.impl包下
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderDao orderDao;

    @Resource
    private StorageService storageService;

    @Resource
    private AccountService accountService;

    @Override
    public void create(Order order) {
        log.info("现在开始新建订单,订单所属用户编号:" + order.getUserId());
        orderDao.create(order);
        log.info("订单微服务开始调用库存,做扣减");
        storageService.decline(order.getProductId(), order.getCount());
        log.info("扣减完成");
        log.info("订单微服务开始调用账户,做扣减");
        accountService.decline(order.getUserId(), order.getMoney());
        log.info("扣减完成");
        log.info("修改订单状态开始");
        orderDao.update(order.getUserId(), 0);
        log.info("修改订单状态完成");
        log.info("下订单服务完成");
    }
}
9.controller包下
@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping("/order/create")
    public CommonResult create(Order order) {
        orderService.create(order);
        return new CommonResult(200, "订单创建成功");
    }
}
10.config包下
@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
@Configuration
@MapperScan({"com.poplar.cloud2020.dao"})
public class MybatisConfig {
}
11.resource.mapper包下
<?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.poplar.cloud2020.dao.OrderDao">
    <resultMap id="BaseResultMap" type="com.poplar.cloud2020.domain.Order">
        <id column="ID" property="id" jdbcType="BIGINT"/>
        <result column="USER_ID" property="userId" jdbcType="BIGINT"/>
        <result column="PRODUCT_ID" property="productId" jdbcType="BIGINT"/>
        <result column="COUNT" property="count" jdbcType="INTEGER"/>
        <result column="MONEY" property="money" jdbcType="DECIMAL"/>
        <result column="STATU" property="statu" jdbcType="INTEGER"/>

    </resultMap>

    <insert id="create">
        insert into t_order(user_id, product_id, count, money, statu)
        values (#{userId}, #{productId}}, #{count}, #{money}, 0)
    </insert>
    <update id="update">
        update t_order
        set statu=1
        where user_id = #{userId}
          and statu = #{stau}
    </update>
</mapper>
8.创建服务-库存服务1001
1.建表(seata_storage库下)
create table t_storage(
id bigint(11) auto_increment primary key,
product_id bigint(11) default null,
total int(11) default null, /*总库存*/
used int(11) default null, /*已用库存*/
residue decimal(11) default null /*剩余库存*/
)
insert into t_storage values(1,1,100,0,100)
select * from t_storage;
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;
2.pom
    <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>0.9.0</version>
        </dependency>
        <!--openFeign-->
        <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>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
3.yml
server:
  port: 1001
spring:
  application:
    name: seata-storage-service
  cloud:
    alibaba:
      seata:
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 111111
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis:
  mapper-locations: classpath:mapper/*.xml
4.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 = "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://127.0.0.1:3306/seata"
    user = "root"
    password = "111111"
    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.registry.conf
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    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包下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Storage {
    private Long id; //订单id

    private Long productId;  //产品id

    private Integer total;  //产品总数

    private Integer used;  //产品被用了

    private Integer residue;  //产品剩余

}
6.dao包下
@Mapper
public interface StorageDao {
    void decline(@Param("productId") Long productId, @Param("count") Integer count);
}
7.service包下
public interface StorageService {
    void decline(Long productId, Integer count);
}
8.service.impl包下
@Service
public class StorageServiceImpl implements StorageService {

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

    @Resource
    private StorageDao storageDao;

    @Override
    public void decline(Long productId, Integer count) {
        LOGGER.info("storage扣减库存开始");
        storageDao.decline(productId, count);
        LOGGER.info("storage扣减库存完成");

    }
}
9.controller包下
@RestController
public class StorageController {

    @Resource
    private StorageService storageService;

    @PostMapping("/storage/decline")
    public CommonResult decline(Long productId, Integer count) {
        storageService.decline(productId, count);
        return new CommonResult(200, "扣减库存成功");
    }
}
10.config包下
@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
@Configuration
@MapperScan({"com.poplar.cloud2020.dao"})
public class MybatisConfig {
}
11.resource.mapper包下
<?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.poplar.cloud2020.dao.StorageDao">
    <resultMap id="BaseResultMap" type="com.poplar.cloud2020.domain.Storage">
        <id column="ID" property="id" jdbcType="BIGINT"/>
        <result column="PRODUCT_ID" property="productId" jdbcType="BIGINT"/>
        <result column="TOTAL" property="total" jdbcType="BIGINT"/>
        <result column="USED" property="used" jdbcType="BIGINT"/>
        <result column="RESIDUE" property="residue" jdbcType="BIGINT"/>
    </resultMap>
    <update id="decline">
        update t_storage
        set used = used + #{count},
            residue = residue - #{count}
        where product_id = #{productId}
    </update>

</mapper>
9.创建服务-账户服务2913
1.建表(seata_account库下)
create table t_account(
id bigint(11) auto_increment primary key,
user_id bigint(11) default null,
total int(11) default null, /*总额度*/
used int(11) default null, /*已用额度*/
residue decimal(11) default null /*剩余可用额度*/
)
insert into t_account values(1,1,1000,0,1000)
select * from t_account;
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;
2.pom
    <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>0.9.0</version>
        </dependency>
        <!--openFeign-->
        <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>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
3.yml
server:
  port: 2913
spring:
  application:
    name: seata-account-service
  cloud:
    alibaba:
      seata:
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 111111
feign:
  hystrix:
    enabled: false
logging:
  level:
    io:
      seata: info

mybatis:
  mapper-locations: classpath:mapper/*.xml
4.file.comf
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 = "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://127.0.0.1:3306/seata"
    user = "root"
    password = "111111"
    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.registry.conf
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    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包下
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {

    private Integer id;

    private Integer userId;

    private BigDecimal total;

    private BigDecimal used;

    private BigDecimal residue;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

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

}
6.dao包下
@Mapper
public interface AccountDao {
    void decline(@Param("userId") Long userId, @Param("money") BigDecimal money);
}
7.service包下
public interface AccountService {
    void decline(Long userId, BigDecimal money);
}
8.service.impl包下
@Service
public class AccountServiceImpl implements AccountService {

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

    @Resource
    private AccountDao accountDao;

    @Override
    public void decline(Long userId, BigDecimal money) {
        LOGGER.info("扣减余额开始");
        accountDao.decline(userId, money);
        LOGGER.info("扣减余额结束");
    }
}
9.controller包下
@RestController
public class accountController {
    @Resource
    private AccountService accountService;

    @PostMapping("/account/decline")
    public CommonResult decline(Long userId, BigDecimal money) {
        accountService.decline(userId, money);
        return new CommonResult(200, "扣减余额成功");
    }
}
10.config包下
@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
@Configuration
@MapperScan({"com.poplar.cloud2020.dao"})
public class MybatisConfig {
}
11.resource.mapper包下
<?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.poplar.cloud2020.dao.AccountDao">
    <resultMap id="BaseResultMap" type="com.poplar.cloud2020.domain.Account">
        <id column="ID" property="id" jdbcType="BIGINT"/>
        <result column="USER_ID" property="userId" jdbcType="BIGINT"/>
        <result column="TOTAL" property="total" jdbcType="DECIMAL"/>
        <result column="USED" property="used" jdbcType="DECIMAL"/>
        <result column="RESIDUE" property="residue" jdbcType="DECIMAL"/>
    </resultMap>
    <update id="decline">
        update t_account
        set used    = used + #{money},
            residue = residue - #{money}
        where user_id = #{userId}
    </update>

</mapper>
10.在程序入口加注解
(OrderServiceImpl的create方法)增加@GlobalTransactional
    @GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
11.seata原理
1.第一阶段
  1. seata拦截业务SQL
  2. 解析SQL语义,找到"业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image"
  3. 执行"业务SQL"更新业务数据,在业务数据更新后
  4. 将其保存成"after image"最后生成行锁

以上阶段全部在一个数据库事务中完成,保证第一阶段的原子性

2.第二阶段
  • 二阶段如果顺利提交的话,因为"业务SQL"在第一阶段已经提交到数据库,所以seata框架只需要将第一阶段保存的快照和行锁删掉,完成数据清理即可

  • 二阶段如果是回滚的话,seata就要回滚第一阶段已经执行的"业务SQL",还原业务数据。回滚方式是用"before image"还原业务数据,在还原前要进行校验脏写,对比"数据库当前业务数据"和"after image"如果不一直则说明有脏写,出现脏写就要转人工处理,如果两份数据相同则说明没有脏写,可以还原数据,具体体现为:

    1.“before image”—>“你想SQL”—>“数据还原”

    2.删除"before image",“after image”,行锁

erAddr = “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包下

```java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {

    private Integer id;

    private Integer userId;

    private BigDecimal total;

    private BigDecimal used;

    private BigDecimal residue;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

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

}
6.dao包下
@Mapper
public interface AccountDao {
    void decline(@Param("userId") Long userId, @Param("money") BigDecimal money);
}
7.service包下
public interface AccountService {
    void decline(Long userId, BigDecimal money);
}
8.service.impl包下
@Service
public class AccountServiceImpl implements AccountService {

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

    @Resource
    private AccountDao accountDao;

    @Override
    public void decline(Long userId, BigDecimal money) {
        LOGGER.info("扣减余额开始");
        accountDao.decline(userId, money);
        LOGGER.info("扣减余额结束");
    }
}
9.controller包下
@RestController
public class accountController {
    @Resource
    private AccountService accountService;

    @PostMapping("/account/decline")
    public CommonResult decline(Long userId, BigDecimal money) {
        accountService.decline(userId, money);
        return new CommonResult(200, "扣减余额成功");
    }
}
10.config包下
@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}
@Configuration
@MapperScan({"com.poplar.cloud2020.dao"})
public class MybatisConfig {
}
11.resource.mapper包下
<?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.poplar.cloud2020.dao.AccountDao">
    <resultMap id="BaseResultMap" type="com.poplar.cloud2020.domain.Account">
        <id column="ID" property="id" jdbcType="BIGINT"/>
        <result column="USER_ID" property="userId" jdbcType="BIGINT"/>
        <result column="TOTAL" property="total" jdbcType="DECIMAL"/>
        <result column="USED" property="used" jdbcType="DECIMAL"/>
        <result column="RESIDUE" property="residue" jdbcType="DECIMAL"/>
    </resultMap>
    <update id="decline">
        update t_account
        set used    = used + #{money},
            residue = residue - #{money}
        where user_id = #{userId}
    </update>

</mapper>
10.在程序入口加注解
(OrderServiceImpl的create方法)增加@GlobalTransactional
    @GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
11.seata原理
1.第一阶段
  1. seata拦截业务SQL
  2. 解析SQL语义,找到"业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image"
  3. 执行"业务SQL"更新业务数据,在业务数据更新后
  4. 将其保存成"after image"最后生成行锁

以上阶段全部在一个数据库事务中完成,保证第一阶段的原子性

2.第二阶段
  • 二阶段如果顺利提交的话,因为"业务SQL"在第一阶段已经提交到数据库,所以seata框架只需要将第一阶段保存的快照和行锁删掉,完成数据清理即可

  • 二阶段如果是回滚的话,seata就要回滚第一阶段已经执行的"业务SQL",还原业务数据。回滚方式是用"before image"还原业务数据,在还原前要进行校验脏写,对比"数据库当前业务数据"和"after image"如果不一直则说明有脏写,出现脏写就要转人工处理,如果两份数据相同则说明没有脏写,可以还原数据,具体体现为:

    1.“before image”—>“你想SQL”—>“数据还原”

    2.删除"before image",“after image”,行锁

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值