近况

8,9月份学习了ES6,webpack,Vue的内容,10,11月份在学微服务的内容,不能说学的有多好,
但大致都了解了一下,很多名词也不再陌生,当然,很多技术仅仅学习了使用,具体原理什么的
我暂时没有深入,因为现在的时间不允许,我还有很多其他要紧的事,现在的学习是为以后学习做个铺垫。

在未来,我会弥补自身基础不足,这是道心上的坎,操作系统,计算机网络,计算机组成,数据结构和算法
这些我都不会放弃,我从始至终都认为重要,但我没有时间,我必须先找到一份工作,才有机会沉淀。
学习Java上,同样也有很多重要的内容没有完成,多线程,JVM之类的,Linux什么的都没有好好学习。
有人问我想走前端还是后端,坦白说我也不太清楚,不过我觉得国外程序员都是两者都学,没有像国内划分明显
所以,我大概也是两者都学习,既然自己实在太菜,深度不够,只有拿广度来凑了,多学点,总没有坏处。

本篇博客主要是对之前微服务的学习做一个总结,复习Feign,nacos,sentinel,gateway,seata内容,
当然,eureka,rabbitMQ,Ribbon,Hystrix都大致了解使用了下,本案例更多使用alibaba组件。
案例描述:三个微服务,订单服务,账户服务,库存服务,用户下订单,调用订单服务接口,
订单服务创建订单,并调用账户服务进行扣款,库存服务扣减库存,最后如果一切顺利,订单服务更改订单状态
表示完成该订单,如果另外两个微服务失败,则数据回滚,这是典型的分布式事务场景。

下面是本篇博客索引

案例框架搭建

  • 创建父子模块

    • 父子模块

    • 父模块的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>world.keyi</groupId>
        <artifactId>SpringCloudDemo</artifactId>
        <version>1.0-SNAPSHOT</version>
      
        <name>SpringCloudDemo</name>
        <modules>
          <module>ApiCommon</module>
          <module>OrderService</module>
          <module>AccountService</module>
          <module>StorageService</module>
          <module>GatewayService</module>
        </modules>
        <packaging>pom</packaging>
      
        <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>2.13.3</log4j.version>
          <lombok>1.18.10</lombok>
          <mysql.version>8.0.22</mysql.version>
          <druid.version>1.1.17</druid.version>
          <mybatis.spring.boot.version>2.1.0</mybatis.spring.boot.version>
        </properties>
      
        <dependencyManagement>
          <dependencies>
            <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-dependencies</artifactId>
              <version>2.3.2.RELEASE</version>
              <type>pom</type>
              <scope>import</scope>
            </dependency>
      
            <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>Hoxton.SR9</version>
              <type>pom</type>
              <scope>import</scope>
            </dependency>
      
            <!--
              alibaba依赖版本是2.2.6,但是nacos最高只有2.2.0,所以会报错spring-cloud-alibaba-nacos-config版本找不到
              我们需要自己指定版本号
            -->
            <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-alibaba-dependencies</artifactId>
              <version>2.2.6.RELEASE</version>
              <type>pom</type>
              <scope>import</scope>
            </dependency>
            <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
              <version>2.2.0.RELEASE</version>
            </dependency>
      
            <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>${mysql.version}</version>
            </dependency>
      
            <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>${druid.version}</version>
            </dependency>
      
            <dependency>
              <groupId>org.mybatis.spring.boot</groupId>
              <artifactId>mybatis-spring-boot-starter</artifactId>
              <version>${mybatis.spring.boot.version}</version>
            </dependency>
      
            <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>${junit.version}</version>
              <scope>test</scope>
            </dependency>
      
            <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <version>${lombok}</version>
              <optional>true</optional>
            </dependency>
      
            <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>${log4j.version}</version>
            </dependency>
          </dependencies>
        </dependencyManagement>
      
        <build>
          <plugins>
            <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
              <version>2.1.3.RELEASE</version>
              <configuration>
                <fork>true</fork>
                <addResources>true</addResources>
              </configuration>
            </plugin>
          </plugins>
        </build>
      </project>
    • OrderService模块POM文件

      <?xml version="1.0" encoding="UTF-8"?>
      
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <parent>
              <artifactId>SpringCloudDemo</artifactId>
              <groupId>world.keyi</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
          <artifactId>OrderService</artifactId>
          <name>OrderService</name>
      
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <maven.compiler.source>1.7</maven.compiler.source>
              <maven.compiler.target>1.7</maven.compiler.target>
          </properties>
      
          <dependencies>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                  <!--去除jackson-dataformat-xml,否则降级方法会返回xml数据,而不是JSON数据-->
                  <exclusions>
                      <exclusion>
                          <groupId>com.fasterxml.jackson.dataformat</groupId>
                          <artifactId>jackson-dataformat-xml</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
      
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              </dependency>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>io.seata</groupId>
                  <artifactId>seata-spring-boot-starter</artifactId>
                  <version>1.4.2</version>
              </dependency>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                  <exclusions>
                      <exclusion>
                          <groupId>io.seata</groupId>
                          <artifactId>seata-spring-boot-starter</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-openfeign</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-actuator</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
      
              <dependency>
                  <groupId>world.keyi</groupId>
                  <artifactId>ApiCommon</artifactId>
                  <version>1.0-SNAPSHOT</version>
              </dependency>
      
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>${mysql.version}</version>
              </dependency>
      
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid</artifactId>
                  <version>${druid.version}</version>
              </dependency>
      
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>${mybatis.spring.boot.version}</version>
              </dependency>
          </dependencies>
      
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                      <version>2.1.3.RELEASE</version>
                      <configuration>
                          <fork>true</fork>
                          <addResources>true</addResources>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>

      OrderService模块依赖相比AccountService模块,Storage模块只多了OpenFeign
      所以另外两个模块依赖省略了

    • ApiCommon模块POM文件
      本模块用来存放公共类或工具类

      <dependencies>
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
          <groupId>cn.hutool</groupId>
          <artifactId>hutool-all</artifactId>
          <version>5.1.0</version>
        </dependency>
      </dependencies>
    • Gateway模块POM文件

      <dependencies>
        <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
      </dependencies>
  • 搭建三个业务数据库
    OrderServie,AccountService,StorageService三个模块分别对应三个数据库,下面是建库建表语句

    #创建order表
    CREATE DATABASE springcloud_order
    USE springcloud_order
    CREATE TABLE t_order(
        id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
        user_id BIGINT(11) DEFAULT NULL COMMENT '用户id',
        product_id BIGINT(11) DEFAULT NULL COMMENT '产品id',
        COUNT INT(11) DEFAULT NULL COMMENT '数量',
        money DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
        STATUS INT(1) DEFAULT NULL COMMENT '订单状态:0创建中,1已完结'
    )ENGINE=INNODB AUTO_INCREMENT=1 CHARSET=utf8;
    
    #创建account表
    CREATE DATABASE springcloud_account
    USE springcloud_account
    CREATE TABLE t_account(
        id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
        user_id BIGINT(11) DEFAULT NULL COMMENT '用户id',
        total DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
        used DECIMAL(10,0) DEFAULT NULL COMMENT '已用额度',
        residue DECIMAL(10,0) DEFAULT 0 COMMENT '剩余可用额度'
    )ENGINE=INNODB AUTO_INCREMENT=1 CHARSET=utf8;
    INSERT INTO t_account(id, user_id, total, used, residue) VALUES(1,1,1000,0,1000);
    
    #创建storage表
    CREATE DATABASE springcloud_storage
    USE springcloud_storage
    CREATE TABLE t_storage(
        id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
        product_id BIGINT(11) DEFAULT NULL COMMENT '产品id',
        total INT(11) DEFAULT NULL COMMENT '总库存',
        used INT(11) DEFAULT NULL COMMENT '已用库存',
        residue INT(11) DEFAULT NULL COMMENT '剩余库存'
    )ENGINE=INNODB AUTO_INCREMENT=1 CHARSET=utf8;
    INSERT INTO t_storage(id, product_id, total, used, residue) VALUES(1,1,100,0,100);

    seata所需要的的数据库和表语句放在下面

  • 配置三个微服务的配置文件
    暂时先贴上三个和业务相关的配置文件,不引入任何微服务组件配置

    • OrderService

      server:
        port: 2346
      spring:
        datasource:
          username: root
          password: 123765
          url: jdbc:mysql://localhost:3306/springcloud_order?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8
          driver-class-name: com.mysql.cj.jdbc.Driver
      mybatis:
        mapper-locations: mapper/*.xml	#在resources资源目录下创建mapper目录
        configuration:
          map-underscore-to-camel-case: true
    • AccountService

      server:
        port: 2345
      spring:
        datasource:
          username: root
          password: 123765
          url: jdbc:mysql://localhost:3306/springcloud_account?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8
          driver-class-name: com.mysql.cj.jdbc.Driver
      mybatis:
        mapper-locations: mapper/*.xml	#在resources资源目录下创建mapper目录
        configuration:
          map-underscore-to-camel-case: true
    • StorageService

      server:
        port: 2347
      spring:
        datasource:
          username: root
          password: 123765
          url: jdbc:mysql://localhost:3306/springcloud_storage?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8
          driver-class-name: com.mysql.cj.jdbc.Driver
      mybatis:
        mapper-locations: classpath:mapper/*.xml
        configuration:
          map-underscore-to-camel-case: true

      除了数据库URL和端口号不同,其他都一样。

增加业务

上面我们创建了父子模块,增加了依赖,配置了基本的配置文件,下面我们编写基本业务

  • OrderService微服务

    • Order

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Order {
          private Integer id;
          private String userId;     //用户id
          private String productId;  //产品id
          private String count;       //数量
          private String money;       //金额
          private String status;      //订单状态:0创建中,1已完结
      }
    • OrderController

      //以上略
      @RestController
      @RequestMapping("order")
      public class OrderController {
          @Resource
          private OrderService orderService;
        
          //CommonResult是ApiCommon服务公共类
          @PostMapping("/")
          public CommonResult<Integer> create(@RequestBody Order order){
              order.setStatus("0");
              return new CommonResult<>(200,"success",orderService.create(order));
          }
      }
    • OrderService及OrderServiceImpl

      public interface OrderService {
          Integer create(Order order);
      }
      @Service
      public class OrderServiceImpl implements OrderService {
          @Resource
          private OrderDao orderDao;
      
          /*
          * 案例主方法:增加订单--->扣减账户余额--->扣减库存--->更改订单状态
          * */
          @Override
          public Integer create(Order order) {
              orderDao.addOrder(order);
              /*
                 这里需要使用feign调用账户模块,库存模块
               */
              Integer result = orderDao.updateOrderStatus(order.getUserId(),"1");
              return result;	//若修改状态成功,则返回1,否则为0
          }
      }
    • OrderDao及OrderMapper.xml

      @Mapper
      public interface OrderDao {
          Integer addOrder(Order order);
          Integer updateOrderStatus(@Param("userId") String userId,@Param("status") String status);
      }
      <?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="world.keyi.dao.OrderDao">
          <insert id="addOrder" parameterType="world.keyi.bean.Order">
              insert into t_order(user_id,product_id,count,money,status)
              values(${userId},${productId},${count},${money},${status})
          </insert>
      
          <update id="updateOrderStatus">
              update t_order set status=${status} where user_id=${userId}
          </update>
      </mapper>
  • AccountService微服务

    • Account

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Account {
          private Integer id;
          private String userId;     //用户id
          private String total;       //总额度
          private String used;        //已用额度
          private String residue;     //剩余可用额度
      }
    • AccountController

      @RestController
      @RequestMapping("/account")
      public class AccountController {
          @Resource
          private AccountService accountService;
      
          @RequestMapping("/list")
          public CommonResult<List<Account>> getAccountList(){
              return new CommonResult<>(200, "success", accountService.getAccountList());
          }
      
          @PutMapping("/")
          public CommonResult<Integer> reduceAccount(
            @RequestParam("userId") String userId,
            @RequestParam("money") String money){
              if (accountService.reduceAccount(userId,money)!=1) {
                  return new CommonResult<>(500,"error");
              }
              return new CommonResult<>(200,"success");
          }
      }
    • AccountService及AccountServiceImpl

      public interface AccountService {
          List<Account> getAccountList();
          Integer reduceAccount(String userId, String money);
      }
      @Service
      public class AccountServiceImpl implements AccountService {
          @Resource
          private AccountDao accountDao;
      
          @Override
          public List<Account> getAccountList() {
              return accountDao.getAccountList();
          }
      
          @Override
          public Integer reduceAccount(String userId, String money) {
              return accountDao.reduceAccount(userId,money);
          }
      }
    • AccountDao及AccountMapper.xml

      @Mapper
      public interface AccountDao {
          public List<Account> getAccountList();
          public Integer reduceAccount(@Param("userId") String userId,@Param("money") String money);
      }
      <?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="world.keyi.dao.AccountDao">
          <select id="getAccountList" resultType="world.keyi.bean.Account">
              select * from t_account;
          </select>
      
          <update id="reduceAccount">
              update t_account set residue=residue-${money},used=used+${money} where user_id=${userId}
          </update>
      </mapper>
  • StorageService微服务

    • Storage

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Storage {
          private Integer id;
          private String productId;      //产品id
          private String total;           //总库存
          private String used;            //已用库存
          private String residue;         //剩余库存
      }
    • StorageController

      @RestController
      @RequestMapping("/storage")
      public class StorageController {
          @Resource
          private StorageService storageService;
      
          @PutMapping("/")
          public CommonResult<Integer> reduceStorage(
            @RequestParam("productId") String productId,
            @RequestParam("count") String count){
              return new CommonResult<>(200,"success",storageService.reduceStorage(productId,count));
          }
      }
    • StorageService及StorageServiceImpl

      public interface StorageService {
          Integer reduceStorage(String productId, String count);
      }
      @Service
      public class StorageServiceImpl implements StorageService {
          @Resource
          private StorageDao storageDao;
      
          @Override
          public Integer reduceStorage(String productId, String count) {
              return storageDao.reduceStorage(productId,count);
          }
      }
    • StorageDao及StorageMapper.xml

      @Mapper
      public interface StorageDao {
          Integer reduceStorage(@Param("productId") String productId,@Param("count") String count);
      }
      <?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="world.keyi.dao.StorageDao">
          <update id="reduceStorage">
              update t_storage set used=used+${count},residue=residue-${count} where product_id=${productId}
          </update>
      </mapper>

增加nacos

  • 导入依赖

    <!--nacos服务注册-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--nacos热部署-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
    </dependency>

    有一点你可能有疑问,为什么在父模块中需要额外nacos依赖

    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>2.2.6.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
      <version>2.2.0.RELEASE</version>
    </dependency>

    我在配置seata的时候,百度到seata依赖:spring-cloud-starter-alibaba-seata最好是2.2.1及以上版本
    所以我需要将:spring-cloud-alibaba-dependencies调整到2.2.1版本,但是刷新maven依赖时报错
    找不到nacos-config依赖,原因是nacos-config最高只有2.2.0版本,又考虑到alibaba,cloud,boot三者
    需要保持一致,所以将alibaba依赖版本号更改成2.2.6并且额外配置nacos-config版本号为2.2.0.

  • 在微服务入口类上增加@EnableDiscoveryClient注解

  • 在微服务配置文件中配置nacos
    没使用nacos之前微服务配置文件是application.yaml,引入nacos后,为了实现热部署,需要将配置文件
    改成bootstrap.yaml,它会先微服务启动时,去nacos控制台上拉取相应的配置信息。
    下面是OrderService微服务的bootstrap.xml,另外两个微服务的naocs配置一样

    server:
      port: 2346
    spring:
      datasource:
        username: root
        password: 123765
        url: jdbc:mysql://localhost:3306/springcloud_order?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
      application:
        name: OrderService
      profiles:
        active: dev
      cloud:
        nacos:
          discovery:
            server-addr: 47.98.138.53:8848
            cluster-name: HZ
            namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138  
          config:
            server-addr: 47.98.138.53:8848
            file-extension: yaml
            namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138
  • 在nacos控制台配置中心创建好namespace和微服务配置文件
    配置文件的dataId规则如下:
    #dataId=${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
    下面是OrderService微服务在nacos上配置文件的内容,OrderService-dev.yaml

    info: 一只穿云箭
  • 测试nacos上的配置文件,在OrderController类上增加@RefreshScope注解
    下面是更新后的OrderController

    @RestController
    @RequestMapping("order")
    @RefreshScope
    public class OrderController {
    
        @Resource
        private OrderService orderService;
    
        @Value("${info}")
        private String info;
    
        @RequestMapping("/nacos")
        public String nacosTest(){
            return info;
        }
    
        @PostMapping("/")
        public CommonResult<Integer> create(@RequestBody Order order){
            order.setStatus("0");
            return new CommonResult<>(200,"success",orderService.create(order));
        }
    }

    以上就是配置nacos的全部内容,其他两个微服务也是如此

增加feign

三个业务微服务中,是OrderService调用另外两个微服务,所以只需要在OrderService中配置feign就好

  • 导入依赖

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  • 在微服务的入口类上增加@EnableFeignClients注解

  • 在微服务配置文件中增加feign的配置

    feign:
      client:
        config:
          default:  #全局设置为default,可以填其他微服务名称
            # ribbon的超时时间,默认等待1秒,得不到爆错,所以重新设置超时时间
            connectTimeout: 5000  # 指的是建立连接所用的时间,适用于网路状况正常情况下,两端连接所用的时间
            readTimeout: 5000   # 指的是建立连接后从服务器读取到可用资源所用的时间
            loggerLevel: full #日志级别
  • 创建另外两个微服务的feign接口

    @FeignClient(value = "AccountService")
    @RequestMapping("/account")
    public interface AccountServiceFeign {
        @PutMapping("/")
        public CommonResult<Integer> reduceAccount(
          @RequestParam("userId") String userId, 
          @RequestParam("money") String money
        );
    }
    @FeignClient(value = "StorageService")
    @RequestMapping("/storage")
    public interface StorageServiceFeign {
        @PutMapping("/")
        public CommonResult<Integer> reduceStorage(
          @RequestParam("productId") String productId, 
          @RequestParam("count") String count
        );
    }
  • 在OrderServiceImpl中调用另外两个微服务的接口方法
    底层是feign创建这两个接口的实现类,来看看更新后的OrderServiceImpl

    @Service
    public class OrderServiceImpl implements OrderService {
    
        @Resource
        private OrderDao orderDao;
    
        @Resource
        private AccountServiceFeign accountServiceFeign;
    
        @Resource
        private StorageServiceFeign storageServiceFeign;
    
        /*
        * 案例主方法:增加订单--->扣减账户余额--->扣减库存--->更改订单状态
        * */
        @Override
        public Integer create(Order order) {
            orderDao.addOrder(order);
            /*
                feign接口不能有异常处理,需要错误需要暴露在这里,不然seata任务没有报错,不回滚数据库
             */
            accountServiceFeign.reduceAccount(order.getUserId(),order.getMoney());
            storageServiceFeign.reduceStorage(order.getProductId(),order.getCount());
            Integer result = orderDao.updateOrderStatus(order.getUserId(),"1");
            return result;	//若修改状态成功,则返回1,否则为0
        }
    }

    以上就是配置feign的全部内容

增加sentinel

sentinel分为核心库和控制台dashboard,sentinel控制台独立运行,核心库则被整合进微服务,
我们在sentinel控制台配置的规则数据,会被发送到sentinel核心库中,sentinel核心库运行于内存中,
接收的规则数据立即生效。

值得注意的是:sentinel核心库与微服务整合在一起,但也开启了另一个端口,用来接收sentinel控制台规则数据
sentinel核心库,sentinel控制台,微服务三个最好在同一台机器上,否则可能sentinel监控不了微服务,
只是我推测,因为控制台在阿里云上时,监控不了我的微服务。

  • 导入依赖

    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
      <!--去除jackson-dataformat-xml,否则降级方法会返回xml数据,而不是JSON数据-->
      <exclusions>
        <exclusion>
          <groupId>com.fasterxml.jackson.dataformat</groupId>
          <artifactId>jackson-dataformat-xml</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  • 在微服务配置文件中配置sentinel

    spring:
      cloud:
        #sentinel-1
        sentinel:
          transport:
            dashboard: localhost:8080
            port: 8719  #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直到找到未被占用的端口
            #这里的port和clientIp指的是sentinel核心库,接收sentinel dashboard的数据并将规则数据注册到sentinel进程中
            clientIp: localhost 
    #sentinel-2
    management:
      endpoints:
        web:
          exposure:
            include: '*'
      endpoint:
        sentinel:
          enabled: true		#开启actuator对sentinel的支持
    
    feign:
      #sentinel与feign整合需要开启
      sentinel:
        enabled: true

    上述就是sentinel的配置,暴露微服务端点,配置sentinel控制台地址,开启feign对sentinel的支持
    之前已经展示的配置已省略

  • 为OrderController接口配置限流和降级方法

    - OrderController更新后
    @RestController
    @RequestMapping("order")
    @RefreshScope
    public class OrderController {
    
        @Resource
        private OrderService orderService;
    
        @Value("${info}")
        private String info;
    
        @RequestMapping("/nacos")
        @SentinelResource(
                value = "nacos",
                blockHandlerClass = OrderDegradeAndException.class,blockHandler = "info_degrade",
                fallbackClass = OrderDegradeAndException.class,fallback = "info_exception")
        public String nacosTest(){
            return info;
        }
    
        @PostMapping("/")
        @SentinelResource(
                value = "create",
                blockHandlerClass = OrderDegradeAndException.class,blockHandler = "create_degrade",
                fallbackClass = OrderDegradeAndException.class,fallback = "create_exception")
        public CommonResult<Integer> create(@RequestBody Order order){
            order.setStatus("0");
            return new CommonResult<>(200,"success",orderService.create(order));
        }
    }
    • OrderDegradeAndException

      @Slf4j
      @Component
      public class OrderDegradeAndException {
          //对指定方法配置熔断降级方法和异常处理方法
          public static CommonResult<Integer> create_degrade(@RequestBody Order order, BlockException exception){
              log.info("OrderController--create方法被熔断限流:"+exception.getMessage());
              return new CommonResult<>(500,"error:"+exception.getMessage(),0);
          }
          public static CommonResult<Integer> create_exception(@RequestBody Order order, Throwable throwable){
              log.info("OrderController--create方法出现异常:"+throwable.getMessage());
              return new CommonResult<>(500,"error:"+throwable.getMessage(),0);
          }
      
          public static String info_degrade(BlockException exception){
            log.info("OrderController--info方法被熔断限流:"+exception.getStackTrace());
              return "info方法被熔断限流:"+exception.getRule();
        }
          public static String info_exception(Throwable throwable){
              log.info("OrderController--info方法出现异常:"+throwable.getMessage());
              return "info方法出现异常:"+throwable.getMessage();
          }
      
          //全局异常处理
          public static CommonResult<Integer> global_exception(Throwable throwable){
              log.info("OrderController--create方法出现异常:"+throwable.getMessage());
              return new CommonResult<>(500,"error:"+throwable.getMessage(),0);
          }
      }

    注意:随着后来的认识,blockHandler指定的方法本以为专门处理限流,降级,
    fallback指定的方法本以为是专门处理接口处理异常的情况,其实并不对,
    @SentinelResource注解中blockHandler指定的方法处理的是限流,而fallback指定的方法处理异常,
    熔断降级的方法,接口熔断后或发生异常后,执行fallback方法,接口被限流后,执行blockHandler方法

  • 为feign接口配置降级方法

    ps:interceptor中是seata的配置
    • AccountServiceFeign更新后
      @FeignClient(value="AccountService",fallbackFactory=AccountServiceFallback.class)

      增加fallbackFactory配置项

    • AccountServiceFallback

      @Component
      @Slf4j
      public class AccountServiceFallback implements FallbackFactory<AccountServiceFeign> {
        @Override
        public AccountServiceFeign create(final Throwable throwable) {
          return new AccountServiceFeign() {
            @Override
            public CommonResult<Integer> reduceAccount(String userId, String money) {
              log.info("AccountServiceFeign报错:"+throwable.getMessage());
              return new CommonResult<>(500,"error:"+throwable.getMessage(),0);
            }
          };
        }
      }

    StorageServiceFeign也是同样的配置,注意,使用seata时,不能为feign配置降级方法,
    不然seata认为feign接口并没有报错,导致另外两个微服务数据库不回滚!!!

增加gateway

Gateway微服务则是独立的微服务了,一般作为业务网关,做权限验证,请求负载均衡,熔断限流等等
Gateway同样需要注册进nacos,所以也需要nacos依赖,具体依赖上面已经给出。
下面是gateway的配置文件,bootstrap.yaml

server:
  port: 2348
spring:
  application:
    name: GatewayService
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: 47.98.138.53:8848
        cluster-name: HZ
        namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138
      config:
        server-addr: 47.98.138.53:8848
        file-extension: yaml
        namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138
    gateway:
      routes:
        - id: OrderService #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://OrderService
          predicates:
            - Path=/order/**   #断言,路径相匹配的进行路由

        - id: AccountService
#          uri: http://localhost:8001
          uri: lb://AccountService   #使用服务名进行路由,需要开启discovery.locator.enabled=true
          predicates:
            - Path=/account/**   #断言,路径相匹配的进行路由
            - After=2021-10-26T22:48:16.597+08:00[Asia/Shanghai]
          filters:
            - AddRequestHeader=Truth,zhangsan is freaking awesome!
      default-filters:  # 这里是默认路由作用每一个路由
      discovery:
        locator:
          enabled: true  #开启从注册中心动态创建路由的功能,利用微服务名进行路由

      # 注意:路由实例过滤器(配置在路由里面),defaultFilter,globalFilter,这三个路由器是根据order排序的
      # 如果order值相同,则defaultFilter>路由实例过滤器>globalFilter

Gateway微服务的路由规则可以通过配置实现,还可以自定义过滤器,这里偷懒没有配置了

增加seata

seata是解决分布式事务的组件,其中三个重要的角色,TC,TM,RM,TC作为seata-server服务端
TM,RM作为seata客户端被整合进微服务中,seata有几种分布式解决方案模型,这里使用是默认的AT模式
让我们来看看在案例中如何配置seata

  • seata-server(TC)的配置

    • 下载好seata-server服务端,服务端是独立运行的,点击下载seata-server

    • 创建seata-server需要使用的数据库(数据库名任意,本案例数据库名是springcloud_seata,之后在配置文件中要指定),
      下面是建表sql语句,ps:seata-server有不同的数据存储模式,如果模式是db,则需要建数据库,当然大多都是建数据库

      -- ---------------------- The script used when storeMode is 'db' ----------------------
      -- the table to store GlobalSession data
      CREATE TABLE IF NOT EXISTS `global_table`
      (
          `xid`                       VARCHAR(128) NOT NULL,
          `transaction_id`            BIGINT,
          `status`                    TINYINT      NOT NULL,
          `application_id`            VARCHAR(32),
          `transaction_service_group` VARCHAR(32),
          `transaction_name`          VARCHAR(128),
          `timeout`                   INT,
          `begin_time`                BIGINT,
          `application_data`          VARCHAR(2000),
          `gmt_create`                DATETIME,
          `gmt_modified`              DATETIME,
          PRIMARY KEY (`xid`),
          KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
          KEY `idx_transaction_id` (`transaction_id`)
      ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;
      
      -- the table to store BranchSession data
      CREATE TABLE IF NOT EXISTS `branch_table`
      (
          `branch_id`         BIGINT       NOT NULL,
          `xid`               VARCHAR(128) NOT NULL,
          `transaction_id`    BIGINT,
          `resource_group_id` VARCHAR(32),
          `resource_id`       VARCHAR(256),
          `branch_type`       VARCHAR(8),
          `status`            TINYINT,
          `client_id`         VARCHAR(64),
          `application_data`  VARCHAR(2000),
          `gmt_create`        DATETIME(6),
          `gmt_modified`      DATETIME(6),
          PRIMARY KEY (`branch_id`),
          KEY `idx_xid` (`xid`)
      ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;
      
      -- the table to store lock data
      CREATE TABLE IF NOT EXISTS `lock_table`
      (
          `row_key`        VARCHAR(128) NOT NULL,
          `xid`            VARCHAR(96),
          `transaction_id` BIGINT,
          `branch_id`      BIGINT       NOT NULL,
          `resource_id`    VARCHAR(256),
          `table_name`     VARCHAR(32),
          `pk`             VARCHAR(36),
          `gmt_create`     DATETIME,
          `gmt_modified`   DATETIME,
          PRIMARY KEY (`row_key`),
          KEY `idx_branch_id` (`branch_id`)
      ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;
    • 上述提到的存储模式,可以在seata-server-1.4.2/conf/file.conf文件中修改,现在我们需要修改该文件
      修改后文件如下,ps:如果你打算将seata的配置放到nacos配置中心上,那么可以不修改该文件

      ## transaction log store, only used in seata-server
      store {
        ## store mode: file、db、redis
        mode = "db"
        ## rsa decryption public key
        publicKey = ""
       
        ## database store property
        db {
          datasource = "druid"
          ## mysql/oracle/postgresql/h2/oceanbase etc.
          dbType = "mysql"
          driverClassName = "com.mysql.cj.jdbc.Driver"
          ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
          url = "jdbc:mysql://localhost:3306/springcloud_seata?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8"
          user = "root"
          password = "123765"
          minConn = 5
          maxConn = 100
          globalTable = "global_table"
          branchTable = "branch_table"
          lockTable = "lock_table"
          queryLimit = 100
          maxWait = 5000
        }
      }
    • file.conf文件中内容是seata-server所必须的,我们也可以将该文件中的内容放在配置中心上,
      你需要修改的是registry.conf文件,与file.conf文件是同级目录。下面是修改后的文件内容

      registry {
        # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
        type = "nacos"
      
        nacos {
          application = "SeataServer"		#TC服务在nacos上的服务名称
          serverAddr = "47.98.138.53:8848"	#nacos地址
          group = "DEFAULT_GROUP"				#TC服务所在组
          namespace = "a3a5fee0-15a2-4023-894c-34fbde0f5138"
          cluster = "HZ"						#TC服务所在集群,client端需要配置一致的映射,重要!
          username = "nacos"
          password = "nacos"
        }
      
        file {
          #改成nacos后,file.conf就不加载了,之前配置的数据库信息也就无效了,我们需要在nacos上配置数据库信息
          name = "file.conf"  
        }
      }
      
      config {		#TC服务的配置文件在nacos上的位置
        # file、nacos 、apollo、zk、consul、etcd3
        type = "nacos"
      
        nacos {
          serverAddr = "47.98.138.53:8848"
          namespace = "a3a5fee0-15a2-4023-894c-34fbde0f5138"
          group = "SEATA_GROUP"			#这里是TC的配置文件的组,可随意
          username = "nacos"
          password = "nacos"
          dataId = "seataServer.properties"	#TC的配置文件名称,nacos配置中心需要同样配置
        }
      
         file {
           name = "file.conf"
         }
      }

      其实就是配置seata-server在nacos上的一些信息。

    • 上面配置中,我们将seata-server需要的信息,本来是放在file.conf的信息放在了nacos配置文件上
      配置文件名为:seataServer.properties,所以我们现在需要在nacos上创建该配置文件
      并添加seata-server需要的内容,该文件内容如下:

      # 数据存储方式,db代表数据库
      store.mode=db
      store.db.datasource=druid
      store.db.dbType=mysql
      store.db.driverClassName=com.mysql.cj.jdbc.Driver
      store.db.url=jdbc:mysql://localhost:3306/springcloud_seata?serverTimezone=GMT&useSSL=false&characterEncoding=utf-8
      store.db.user=root
      store.db.password=123765
      store.db.minConn=5
      store.db.maxConn=30
      store.db.globalTable=global_table
      store.db.branchTable=branch_table
      store.db.queryLimit=100
      store.db.lockTable=lock_table
      store.db.maxWait=5000
      # 事务、日志等配置
      server.recovery.committingRetryPeriod=1000
      server.recovery.asynCommittingRetryPeriod=1000
      server.recovery.rollbackingRetryPeriod=1000
      server.recovery.timeoutRetryPeriod=1000
      server.maxCommitRetryTimeout=-1
      server.maxRollbackRetryTimeout=-1
      server.rollbackRetryTimeoutUnlockEnable=false
      server.undo.logSaveDays=7
      server.undo.logDeletePeriod=86400000
      
      # 客户端与服务端传输方式
      transport.serialization=seata
      transport.compressor=none
      # 关闭metrics功能,提高性能
      metrics.enabled=false
      metrics.registryType=compact
      metrics.exporterList=prometheus
      metrics.exporterPrometheusPort=9898

      之后只需双击seata-server-1.4.2/bin/seata-server.bat即可启动seata-server
      以上就是seata服务端配置的全部过程

  • seata客户端(TM,RM)的配置

    seata客户端已经整合进了微服务,三个业务微服务都需要进行seata客户端的配置

    • 导入依赖

      <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.4.2</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        <exclusions>
          <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
          </exclusion>
        </exclusions>
      </dependency>

      因为alibaba-seata依赖中seata依赖版本是1.3,我们最好使用最新的seata版本,所以将内部自带的
      依赖排除,添加最新的seata依赖

    • 在微服务的入口类上添加@EnableAutoDataSourceProxy注解,该注解用于代理druid的数据库连接池
      注意,因为seata版本不同,会有不同的配置,使用1.4.2最新版本,或者比较新版本需要这个注解
      使用版本比较低的seata,需要使用@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

    • 因为seata默认使用AT模式,需要我们在各个微服务的数据库中增加undo_log表,用于存储seataAt模式下的前后镜像
      创建表的sql语句如下:

      CREATE TABLE IF NOT EXISTS `undo_log`
      (
          `branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',
          `xid`           VARCHAR(100) NOT NULL COMMENT 'global transaction id',
          `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
          `rollback_info` LONGraw     NOT NULL COMMENT 'rollback info',
          `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
          `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
          `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
          UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
      ) ENGINE = InnoDB
        AUTO_INCREMENT = 1
        DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
    • 接下来在各个微服务配置文件中配置Seata

      # seata的配置,用于找到seata-server(TC)的服务实例和配置文件信息
      seata:
        enabled: true		#开启seata
        enable-auto-data-source-proxy: true  #seata自动代理数据源,新版本有的
        tx-service-group: seata-demo  #配置事务组,事务组用于映射TC服务的集群名称
        data-source-proxy-mode: AT    #配置seata模式
        config:     #配置的是TC服务的配置文件在nacos上的地址	TC服务的配置中心
          type: nacos
          nacos:
            server-addr: 47.98.138.53:8848
            group: SEATA_GROUP    #这里的组与registry.conf中config一致
            namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138
            dataId: client.properties  #这里是nacos的配置文件,本应该是seataServer.properties
            username: nacos          #但我这里做了一个抽离成新的配置文件,这两个文件内容可以放一起
            password: nacos
            
        registry:    #配置的是TC服务在nacos上的地址,TC服务的注册中心
          type: nacos
          nacos:
            application: SeataServer    #TC服务名称
            server-addr: 47.98.138.53:8848
            group: DEFAULT_GROUP    #TC所在的组,需要与业务微服务同组
            namespace: a3a5fee0-15a2-4023-894c-34fbde0f5138
            username: nacos
            password: nacos
        service:
          vgroup-mapping:
            seata-demo: HZ    # 事务组与nacos中集群的映射关系,集群指的是TC所在的集群
          disable-global-transaction: false
    • 上面提到了client.properties,这个文件中的内容可以放在之前配置的seataServer.properties文件中
      这里做了一个拆分,下面是client.properties中的内容

      # 事务组映射关系
      service.vgroupMapping.seata-demo=HZ		
          
      service.enableDegrade=false
      service.disableGlobalTransaction=false
      # 与TC服务的通信配置
      transport.type=TCP
      transport.server=NIO
      transport.heartbeat=true
      transport.enableClientBatchSendRequest=false
      transport.threadFactory.bossThreadPrefix=NettyBoss
      transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
      transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
      transport.threadFactory.shareBossWorker=false
      transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
      transport.threadFactory.clientSelectorThreadSize=1
      transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
      transport.threadFactory.bossThreadSize=1
      transport.threadFactory.workerThreadSize=default
      transport.shutdown.wait=3
      # RM配置
      client.rm.asyncCommitBufferLimit=10000
      client.rm.lock.retryInterval=10
      client.rm.lock.retryTimes=30
      client.rm.lock.retryPolicyBranchRollbackOnConflict=true
      client.rm.reportRetryCount=5
      client.rm.tableMetaCheckEnable=false
      client.rm.tableMetaCheckerInterval=60000
      client.rm.sqlParserType=druid
      client.rm.reportSuccessEnable=false
      client.rm.sagaBranchRegisterEnable=false
      # TM配置
      client.tm.commitRetryCount=5
      client.tm.rollbackRetryCount=5
      client.tm.defaultGlobalTransactionTimeout=60000
      client.tm.degradeCheck=false
      client.tm.degradeCheckAllowTimes=10
      client.tm.degradeCheckPeriod=2000
          
      # undo日志配置
      client.undo.dataValidation=true
      client.undo.logSerialization=jackson
      client.undo.onlyCareUpdateColumns=true
      client.undo.logTable=undo_log
      client.undo.compress.enable=true
      client.undo.compress.type=zip
      client.undo.compress.threshold=64k
      client.log.exceptionRate=100

      这里有个特别重要的配置:service.vgroupMapping.seata-demo=HZ
      seata-demo是随意取的TC的事务组名,HZ则表示TC服务在nacos上的集群名称
      我们不仅要在nacos配置文件上配置事务组与TC服务集群名称的映射,还要在我们微服务上,也就是seata客户端上配置映射

      tx-service-group: seata-demo
      service:
          vgroup-mapping:
            seata-demo: HZ    # 事务组与nacos中集群的映射关系,集群指的是TC所在的集群

      我们还需要seata-server的registry.conf文件中registry的cluster的值和微服务上配置文件的值一致
      registry.conf中是TC集群名称,nacos配置文件上配置是事务组和集群名称,微服务配置的是事务组和集群名称
      以上就是seata客户端的配置

  • seata一些需要注意的地方

    • seata1.4.2之后,需要回滚的表中日期类型不能使用datetime,可以使用timestamp

    • 当使用@globalTransactional注解开启一个TM全局事务时,如果内部使用feign调用其他微服务,
      则当调用失败时,应该直接报错,而不是进入熔断降级方法,不然seata认为并没有报错,从而不回滚
      换句话说,使用seata时,内部的feign不能配置降级异常处理方法,报错要上报到TM方法中

    • 还记得在sentinel为feign接口配置熔断降级的时候,feign包下有个interceptor包吗,这里放的是feign
      的拦截器,有些情况下,或许是seata版本不同,或许是在某个特定场景下,会出现微服务数据库不回滚
      这是因为AT模式中全局锁XID没有传递到其他微服务中,需要我们配置feign拦截器在请求时添加上xid
      下面是feign的拦截器类:FeignInterceptor

      @Configuration
      @Slf4j
      public class FeignInterceptor implements RequestInterceptor {
          @Override
          public void apply(RequestTemplate template) {
              //解决seata的xid未传递
              String xid = RootContext.getXID();
              if (StringUtils.isNotBlank(xid)) {
                  log.info("feign传递分布式事务xid:{}", xid);
                  template.header(RootContext.KEY_XID, xid);
              }
          }
      }

      然后在feign注解上添加configuration选项,例如:

      @FeignClient(value = "AccountService"
              /*,configuration = FeignInterceptor.class*/             //用于传递xid的配置,对seata无影响,暂时还不知道作用
              /*,fallbackFactory = AccountServiceFallback.class*/     //异常处理对seata有影响
      )
      @RequestMapping("/account")
      public interface AccountServiceFeign {
          @PutMapping("/")
          public CommonResult<Integer> reduceAccount(
            @RequestParam("userId") String userId, 
            @RequestParam("money") String money
        );
      }
        
        因为本案例中并没有出现xid不传递现象,所以无需配置feign拦截器
        
      - 有些人说seata事务不回滚是因为mybatis使用了数据库主键自增导致的,我们执行sql语句时
        需要自己设置数据的id,不能使用数据库自增,但是我的案例没有这样的问题,可能还是某个版本的缘故
        我也不清楚,或者是别人自己本身的原因,或者是seata模式不同的原因。
  • 一些博客

案例中不完美的地方

  • nacos没有配置集群,如果配置nacos集群则需要更改存储到外部mysql
  • sentinel没有持久化到nacos
  • gateway的熔断限流,黑白名单没有配置
  • seata的TC集群没有配置

案例资料

本篇博客基本结束,也宣告了微服务这块暂告一段落,虽说很多东西都没有做一个深层次的了解,但学习起来
只是时间问题,接下去我应该去学习之前遗漏的知识点,主要是多线程一块吧,还有毕设要写。
本案例的资料,放在github上,有需要可以查看一下:案例资料