seata + nacos + openfeign

  • 使用nacos做注冊中心和配置中心
  • 使用openfeign做http調(diào)用
  • mysql 業(yè)務(wù)庫:seata_order俏险、seata_stock

1. 創(chuàng)建業(yè)務(wù)庫

1.1. seata_order

CREATE TABLE `order_tbl` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int DEFAULT '0',
  `money` int DEFAULT '0',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int 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 DEFAULT CHARSET=utf8;

1.2. seata_stock

CREATE TABLE `stock_tbl` (
  `id` int NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int DEFAULT '0',
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `seata_stock`.`stock_tbl` (`id`, `commodity_code`, `count`, `update_time`) VALUES ('1', 'product-1', '-17', '2023-01-30 09:07:36');
INSERT INTO `seata_stock`.`stock_tbl` (`id`, `commodity_code`, `count`, `update_time`) VALUES ('2', 'product-2', '-7', '2023-01-30 01:39:01');

CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int 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 DEFAULT CHARSET=utf8;

2. demo項(xiàng)目

  • parent項(xiàng)目 springboot-openfeign-seataat
  • 三個(gè)子項(xiàng)目
    • rm-stock,使用seata_stock庫涤浇《漳扣除商品津畸,更新stock_tbl表
    • rm-order蠢护,使用seata_order庫。新建訂單箍土,向order_tbl表插入記錄
    • tm-portal逢享,調(diào)用rm-order下單,調(diào)用rm-stock扣除商品

2.1. parent項(xiàng)目

刪除src目錄吴藻,修改pom.xml

<groupId>com.cccc</groupId>
    <artifactId>springboot-openfeign-seataat</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>rm-stock</module>
        <module>rm-order</module>
        <module>tm-portal</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.4</spring-cloud.version>
        <spring-boot.version>2.6.11</spring-boot.version>
        <spring.cloud.alibaba.version>2021.0.4.0</spring.cloud.alibaba.version>

        <seata.version>1.4.2</seata.version>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- Maven properties -->
        <mysql-connector.version>5.1.44</mysql-connector.version>
        <guava.version>27.0.1-jre</guava.version>
        <jackson.version>2.13.4.1</jackson.version>
        <druid.version>1.1.12</druid.version>
        <jakarta-annotation-api.version>1.3.5</jakarta-annotation-api.version>
        <javax.annotation-api.version>1.3.1</javax.annotation-api.version>
        <lombok.version>1.18.8</lombok.version>

        <maven-source-plugin.version>3.0.1</maven-source-plugin.version>
        <maven-surefire-plugin.version>2.22.1</maven-surefire-plugin.version>
        <mybatis-plus-boot-starter.version>3.3.0</mybatis-plus-boot-starter.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--mybatis-spring-boot-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.2.0</version>
            </dependency>
            <!--druid-spring-boot連接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.2.11</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.2. rm-stock

  1. pom.xml
<parent>
        <artifactId>springboot-openfeign-seataat</artifactId>
        <groupId>com.cccc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rm-stock</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <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>
            <version>${spring.cloud.alibaba.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-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <!-- mybatis -->

        <!--mybatis-generator-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
        <!--mybatis-generator-->

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
            <version>11.8</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>
  1. applicaion.properties
spring.application.name=stock-service
server.port=9091
# Nacos 注冊中心地址
spring.cloud.nacos.discovery.server-addr = xx.xx.xx.xx:8848
spring.cloud.nacos.discovery.namespace = nnnnn
spring.cloud.nacos.discovery.register-enabled = true
spring.cloud.nacos.discovery.username = uuuuu
spring.cloud.nacos.discovery.password=ppppp
spring.cloud.nacos.discovery.group=SEATA_GROUP

# 數(shù)據(jù)源配置
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/seata_stock?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=ppppp
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#事務(wù)分組配置(在v1.5之后默認(rèn)值為default_tx_group)
seata.tx-service-group=seata-storage-service
#指定事務(wù)分組至集群映射關(guān)系(等號右側(cè)的集群名需要與Seata-server注冊到Nacos的cluster保持一致)
seata.service.vgroup-mapping.seata-storage-service=default
#使用nacos作為注冊中心
seata.registry.type=nacos
#nacos注冊中心IP:端口
seata.registry.nacos.server-addr=xx.xx.xx.xx:8848
seata.registry.nacos.username = uuuuu
seata.registry.nacos.password = ppppp
seata.registry.nacos.namespace = nnnnn
seata.registry.nacos.cluster = default
#Seata服務(wù)名(應(yīng)與seata-server實(shí)際注冊的服務(wù)名一致)
seata.registry.nacos.application=seata-server
#Seata分組名(應(yīng)與seata-server實(shí)際注冊的分組名一致)
seata.registry.nacos.group=SEATA_GROUP

#log
#logging.level.io.seata=debug
#logging.level.org.springframework = debug

3.啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class StockServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(StockServiceApplication.class, args);
    }
}
  1. 實(shí)體類
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@Accessors(chain = true)
@TableName("stock_tbl")
public class Stock {
    private Long id;
    private String commodityCode;
    private Long count;
    private Date updateTime;
}
  1. dao
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cccc.entity.Stock;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface StockDAO extends BaseMapper<Stock> {
}
  1. service
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cccc.entity.Stock;
import com.cccc.repository.StockDAO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;

@Service
public class StockService {

    @Resource
    private StockDAO stockDAO;

    /**
     * 減庫存
     *
     * @param commodityCode
     * @param count
     */
    @Transactional(rollbackFor = Exception.class)
    public void deduct(String commodityCode, int count) {
        if (commodityCode.equals("product-2")) {
            throw new RuntimeException("異常:模擬業(yè)務(wù)異常:stock branch exception");
        }

        QueryWrapper<Stock> wrapper = new QueryWrapper<>();
        wrapper.setEntity(new Stock().setCommodityCode(commodityCode));
        Stock stock = stockDAO.selectOne(wrapper);
        stock.setCount(stock.getCount() - count);
        stock.setUpdateTime(new Date());

        stockDAO.updateById(stock);
    }
}
  1. controller
import com.cccc.service.StockService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("stock")
public class StockController {

    @Resource
    private StockService stockService;

    /**
     * 減庫存
     *
     * @param commodityCode 商品代碼
     * @param count         數(shù)量
     * @return
     */
    @RequestMapping(path = "/deduct")
    public Boolean deduct(String commodityCode, Integer count) {
        stockService.deduct(commodityCode, count);
        return true;
    }
}

2.3. rm-order

  1. pom.xml
<parent>
        <artifactId>springboot-openfeign-seataat</artifactId>
        <groupId>com.cccc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rm-order</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <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>
            <version>${spring.cloud.alibaba.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-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <!-- mybatis -->

        <!--mybatis-generator-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
        <!--mybatis-generator-->

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
            <version>11.8</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>
  1. applicaion.properties
spring.application.name=order-service
server.port=9092
# Nacos 注冊中心地址
spring.cloud.nacos.discovery.server-addr = xx.xx.xx.xx:8848
spring.cloud.nacos.discovery.namespace = nnnnn
spring.cloud.nacos.discovery.register-enabled = true
spring.cloud.nacos.discovery.username = uuuuu
spring.cloud.nacos.discovery.password=ppppp
spring.cloud.nacos.discovery.group=SEATA_GROUP

# 數(shù)據(jù)源配置
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/seata_order?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=uuuuu
spring.datasource.password=ppppp
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#事務(wù)分組配置(在v1.5之后默認(rèn)值為default_tx_group)
seata.tx-service-group=seata-storage-service
#指定事務(wù)分組至集群映射關(guān)系(等號右側(cè)的集群名需要與Seata-server注冊到Nacos的cluster保持一致)
seata.service.vgroup-mapping.seata-storage-service=default
#使用nacos作為注冊中心
seata.registry.type=nacos
#nacos注冊中心IP:端口
seata.registry.nacos.server-addr=xx.xx.xx.xx:8848
seata.registry.nacos.username = uuuuu
seata.registry.nacos.password = ppppp
seata.registry.nacos.namespace = nnnnn
seata.registry.nacos.cluster = default
#Seata服務(wù)名(應(yīng)與seata-server實(shí)際注冊的服務(wù)名一致)
seata.registry.nacos.application=seata-server
#Seata分組名(應(yīng)與seata-server實(shí)際注冊的分組名一致)
seata.registry.nacos.group=SEATA_GROUP
logging.level.io.seata=debug

3.啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

  1. 實(shí)體類
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.util.Date;

@Data
@Accessors(chain = true)
@TableName("order_tbl")
public class Order {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String userId;
    private String commodityCode;
    private Integer count;
    private BigDecimal money;
    private Date createTime;
}
  1. dao
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cccc.model.Order;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface OrderDAO extends BaseMapper<Order> {
}
  1. service
import com.cccc.model.Order;
import com.cccc.repository.OrderDAO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;

@Service
public class OrderService {

    @Resource
    private OrderDAO orderDAO;

    /**
     * 創(chuàng)建訂單
     *
     * @param userId
     * @param commodityCode
     * @param count
     */
    @Transactional(rollbackFor = Exception.class)
    public void placeOrder(String userId, String commodityCode, Integer count) {
        BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5));
        Order order = new Order().setUserId(userId).setCommodityCode(commodityCode).setCount(count)
                .setMoney(orderMoney).setCreateTime(new Date());
        orderDAO.insert(order);
    }

}
  1. controller
import com.cccc.service.OrderService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("order")
public class OrderController {

    @Resource
    private OrderService orderService;

    /**
     * 下單:插入訂單表瞒爬、扣減庫存,模擬回滾
     *
     * @return
     */
    @RequestMapping("/placeOrder/commit")
    public Boolean placeOrderCommit(String userId, String commodityCode, Integer count) {
        orderService.placeOrder(userId, commodityCode, count);
        return true;
    }
}

2.4. rm-portal

  1. pom.xml
<parent>
        <artifactId>springboot-openfeign-seataat</artifactId>
        <groupId>com.cccc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>tm-portal</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <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>
            <version>${spring.cloud.alibaba.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-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
            <version>11.8</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>
  1. applicaion.properties
spring.application.name=portal-service
server.port=9090
# Nacos 注冊中心地址
spring.cloud.nacos.discovery.server-addr = xx.xx.xx.xx:8848
spring.cloud.nacos.discovery.namespace = nnnnn
spring.cloud.nacos.discovery.register-enabled = true
spring.cloud.nacos.discovery.username = uuuuu
spring.cloud.nacos.discovery.password=ppppp
spring.cloud.nacos.discovery.group=SEATA_GROUP


#事務(wù)分組配置(在v1.5之后默認(rèn)值為default_tx_group)
seata.tx-service-group=seata-storage-service
#指定事務(wù)分組至集群映射關(guān)系(等號右側(cè)的集群名需要與Seata-server注冊到Nacos的cluster保持一致)
seata.service.vgroup-mapping.seata-storage-service=default
#使用nacos作為注冊中心
seata.registry.type=nacos
#nacos注冊中心IP:端口
seata.registry.nacos.server-addr=xx.xx.xx.xx:8848
seata.registry.nacos.username = uuuuu
seata.registry.nacos.password = ppppp
seata.registry.nacos.namespace = nnnnn
seata.registry.nacos.cluster = default
#Seata服務(wù)名(應(yīng)與seata-server實(shí)際注冊的服務(wù)名一致)
seata.registry.nacos.application=seata-server
#Seata分組名(應(yīng)與seata-server實(shí)際注冊的分組名一致)
seata.registry.nacos.group=SEATA_GROUP
logging.level.io.seata=debug

3.啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

  1. feign
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "order-service")
public interface OrderFeignClient {

    @GetMapping("order/placeOrder/commit")
    Boolean placeOrderCommit(@RequestParam("userId") String userId, @RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count);

}
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "stock-service")
public interface StockFeignClient {

    @GetMapping("stock/deduct")
    Boolean deduct(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count);
}
  1. service
import com.cccc.feign.OrderFeignClient;
import com.cccc.feign.StockFeignClient;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PortalService {

    @Resource
    private StockFeignClient stockFeignClient;
    @Resource
    private OrderFeignClient orderFeignClient;

    /**
     * 下單:創(chuàng)建訂單沟堡、減庫存侧但,涉及到兩個(gè)服務(wù)
     *
     * @param userId
     * @param commodityCode
     * @param count
     */
    @GlobalTransactional
    public void placeOrder(String userId, String commodityCode, Integer count) {
        orderFeignClient.placeOrderCommit(userId, commodityCode, count);
        stockFeignClient.deduct(commodityCode, count);
    }

    /**
     * 下單:創(chuàng)建訂單、減庫存弦叶,涉及到兩個(gè)服務(wù)
     *
     * @param userId
     * @param commodityCode
     * @param count
     */
    public void placeOrder2(String userId, String commodityCode, Integer count) {
        orderFeignClient.placeOrderCommit(userId, commodityCode, count);
        stockFeignClient.deduct(commodityCode, count);
    }

}
  1. controller
import com.cccc.service.PortalService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("portal")
public class PortalController {

    @Resource
    private PortalService portalService;

    /**
     * 下單:插入訂單表俊犯、扣減庫存,模擬回滾
     *
     * @return
     */
    @RequestMapping("/commit")
    public Boolean placeOrderCommit() {
        portalService.placeOrder("1", "product-1", 1);
        return true;
    }

    /**
     * 下單:插入訂單表伤哺、扣減庫存,模擬回滾
     *
     * @return
     */
    @RequestMapping("/rollback")
    public Boolean placeOrderRollback() {
        // product-2 扣庫存時(shí)模擬了一個(gè)業(yè)務(wù)異常,
        portalService.placeOrder("1", "product-2", 1);
        return true;
    }

    /**
     * 下單:插入訂單表者祖、扣減庫存立莉,模擬回滾
     *
     * @return
     */
    @RequestMapping("/commit2")
    public Boolean placeOrderCommit2() {
        portalService.placeOrder2("1", "product-1", 1);
        return true;
    }

    /**
     * 下單:插入訂單表、扣減庫存七问,模擬回滾
     *
     * @return
     */
    @RequestMapping("/rollback2")
    public Boolean placeOrderRollback2() {
        // product-2 扣庫存時(shí)模擬了一個(gè)業(yè)務(wù)異常,
        portalService.placeOrder2("1", "product-2", 1);
        return true;
    }

}

3. 演示

3.1. 運(yùn)行rm-order蜓耻、rm-stock、tm-portal

3.2. tm-portal提供4個(gè)url

  1. http://localhost:9090/portal/commit
    開分布式事務(wù)械巡,下單成功刹淌,扣除商品成功。order_tbl表有新記錄讥耗,stock_tbl表商品數(shù)量減少
  2. http://localhost:9090/portal/rollback
    開分布式事務(wù)有勾,下單成功,扣除商品拋異常古程。order_tbl表無新記錄蔼卡,stock_tbl表商品數(shù)量不變
    rm-order、rm-stock的數(shù)據(jù)庫操作都回滾掉了
  3. http://localhost:9090/portal/commit2
    不開分布式事務(wù)挣磨,下單成功雇逞,扣除商品成功荤懂。order_tbl表有新記錄,stock_tbl表商品數(shù)量減少
  4. http://localhost:9090/portal/rollback2
    不開分布式事務(wù)塘砸,下單成功节仿,扣除商品拋異常。order_tbl表有新記錄掉蔬,stock_tbl表商品數(shù)量不變
    rm-order的數(shù)據(jù)庫操作沒有回滾掉了

對比2和4廊宪,可以看出seata的分布式事務(wù)效果

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市眉踱,隨后出現(xiàn)的幾起案子挤忙,更是在濱河造成了極大的恐慌,老刑警劉巖谈喳,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件册烈,死亡現(xiàn)場離奇詭異,居然都是意外死亡婿禽,警方通過查閱死者的電腦和手機(jī)赏僧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扭倾,“玉大人淀零,你說我怎么就攤上這事√乓迹” “怎么了驾中?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長模聋。 經(jīng)常有香客問我肩民,道長,這世上最難降的妖魔是什么链方? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任持痰,我火速辦了婚禮,結(jié)果婚禮上祟蚀,老公的妹妹穿的比我還像新娘工窍。我一直安慰自己,他們只是感情好前酿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布患雏。 她就那樣靜靜地躺著,像睡著了一般薪者。 火紅的嫁衣襯著肌膚如雪纵苛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音攻人,去河邊找鬼取试。 笑死,一個(gè)胖子當(dāng)著我的面吹牛怀吻,可吹牛的內(nèi)容都是我干的瞬浓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼蓬坡,長吁一口氣:“原來是場噩夢啊……” “哼猿棉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屑咳,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤萨赁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后兆龙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杖爽,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年紫皇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慰安。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聪铺,死狀恐怖化焕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铃剔,我是刑警寧澤撒桨,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站键兜,受9級特大地震影響元莫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝶押,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望火欧。 院中可真熱鬧棋电,春花似錦、人聲如沸苇侵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榆浓。三九已至于未,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烘浦。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工抖坪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闷叉。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓擦俐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親握侧。 傳聞我的和親對象是個(gè)殘疾皇子蚯瞧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容