45、创建Seata 的三个微服务代码编写,测试Seata的事务控制
业务需求:下订单-------》减库存------》扣余额-----》改状态
1、创建 seata-order-service2001 订单微服务构建
编写pom.xml 添加依赖
<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-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- seata -->
<dependency>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml 配置文件
server: port: 2001 spring: application: name: seata-order-service cloud: nacos: discovery: server-addr: localhost:8848 alibaba: seata: tx-service-group: fsp_tx_group datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://8.129.215.115:3307/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 feign: hystrix: enabled: false logging: level: io: seata: info mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: com.atguigu.springcloud.alibaba.domain
编写file.conf 文件
transport { type = "TCP" server = "NIO" heartbeat = true 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" boss-thread-size = 1 worker-thread-size = 8 } shutdown { wait = 3 } serialization = "seata" compressor = "none" } service { vgroup_mapping.fsp_tx_group = "default" default.grouplist = "127.0.0.1:8091" enableDegrade = false disable = false 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 } store { mode = "db" file { dir = "sessionStore" max-branch-session-size = 16384 max-global-session-size = 512 file-write-buffer-cache-size = 16384 session.reload.read_size = 100 flush-disk-mode = async } db { datasource = "dbcp" db-type = "mysql" driver-class-name = "com.mysql.jdbc.Driver" url = "jdbc:mysql://8.129.215.115:3307/seata" user = "root" password = "123456" min-conn = 1 max-conn = 3 global.table = "global_table" branch.table = "branch_table" lock-table = "lock_table" query-limit = 100 } } lock { mode = "remote" local { } remote { } } recovery { committing-retry-period = 1000 asyn-committing-retry-period = 1000 rollbacking-retry-period = 1000 timeout-retry-period = 1000 } transaction { undo.data.validation = true undo.log.serialization = "jackson" undo.log.save.days = 7 undo.log.delete.period = 86400000 undo.log.table = "undo_log" } metrics { enabled = false registry-type = "compact" exporter-list = "prometheus" exporter-prometheus-port = 9898 } support { spring { datasource.autoproxy = false } }
编写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 { 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" } }
编写实体类domain
@Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "t_order") public class Order { /** * `id` bigint NOT NULL COMMENT '主键', * `user_id` bigint DEFAULT NULL COMMENT '用户id', * `product_id` bigint DEFAULT NULL COMMENT '产品id', * `count` int DEFAULT NULL COMMENT '数量', * `money` decimal(11,1) DEFAULT NULL COMMENT '金额', * `status` int DEFAULT NULL COMMENT '状态0:创建中,1:已完成', */ private Long id; private Long userId; private Long productId; private Integer count; private BigDecimal money; private Integer status; }
编写Mapper接口
@Mapper public interface OrderMapper extends BaseMapper<Order> { @Insert("insert into t_order (user_id,product_id,count,money,status) values(#{userId},#{productId},#{count},#{money},#{status}) ") Integer create (Order order); @Update(" update t_order set status = 1 where user_id = #{userId} ") Integer updateByStatus(@Param("userId") Long userId); }
编写 OrderService 接口
public interface OrderService { Order create (Order order); }
编写 OrderService 接口 的实现类
@Service @Slf4j public class OrderServiceImpl implements OrderService { @Resource private OrderMapper orderMapper; @Resource private AccountService accountService; @Resource private StorageService storageService; @Override @GlobalTransactional(name = "fsp_order_create",rollbackFor = Exception.class) public Order create(Order order) { log.info("-------------------》开始新建订单"); orderMapper.insert(order); log.info("-------------> 订单微服务开始调库存,做扣减Count"); storageService.decrease(order.getProductId(),order.getCount()); log.info("-------------> 订单微服务开始调库存,做扣减end"); log.info("-------------> 订单微服务开始调账户,做扣减Money"); accountService.decrease(order.getUserId(),order.getMoney()); log.info("-------------> 订单微服务开始调账户,做扣减end"); log.info("-------------> 订单微服务修改订单状态开始"); orderMapper.updateByStatus(order.getUserId()); log.info("-------------> 订单微服务修改订单状态 end"); log.info("下订单结束了 哈哈哈"); return order; } }
编写Feign 接口 StorageService
@FeignClient(value = "seata-storage-service") public interface StorageService { @PostMapping(value = "/storage/decrease") CommonResult decrease(@RequestParam(name = "productId") Long productId, @RequestParam(name = "count") Integer count); }
编写Feign 接口 AccountService
@FeignClient(value = "seata-account-service") public interface AccountService { @PostMapping(value = "/storage/decrease") CommonResult decrease(@RequestParam(name = "userId") Long userId, @RequestParam(name = "money") BigDecimal money); }
编写controller 类
@RestController @Slf4j public class OrderController { @Resource private OrderService orderService; @GetMapping(value = "/order/create") public CommonResult create (Order order) { Order result = orderService.create(order); return new CommonResult(200,"下单成功!",result); } }
编写主启动类
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class OrderApplication2001 { public static void main(String[] args) { SpringApplication.run(OrderApplication2001.class, args); } }
2、创建 seata-storage-service2002 订单微服务构建
编写pom.xml
<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-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- seata -->
<dependency>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml
server: port: 2002 spring: application: name: seata-storage-service cloud: nacos: discovery: server-addr: localhost:8848 alibaba: seata: tx-service-group: fsp_tx_group datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://8.129.215.115:3307/seata_storage?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 feign: hystrix: enabled: false logging: level: io: seata: info mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: com.atguigu.springcloud.alibaba.domain
编写file.conf 和registry.conf 两个文件 都和 seata-order-service2001 是一样的
编写实体类
@Data @AllArgsConstructor @NoArgsConstructor public class Storage { /** * `id` bigint NOT NULL COMMENT '主键', * `product_id` bigint DEFAULT NULL COMMENT '产品id', * `total` int DEFAULT NULL COMMENT '总共', * `used` int DEFAULT NULL COMMENT '已使用', * `resdiue` int DEFAULT NULL COMMENT '剩余数量', */ private Long id; private Long productId; private Integer total; private Integer used; private Integer resdiue; }
编写Mapper接口
@MapperScan public interface StorageMapper extends BaseMapper<Storage> { @Update(" update t_storage set used = used + #{count},resdiue = resdiue - #{count} where product_id = #{productId}") Integer decrease (@Param("productId")Long productId , @Param("count") Integer count); }
编写StorrageService 接口
public interface StorageService { Integer decrease (Long productId , Integer count); }
编写StorrageService 接口 的实现类
@Service public class StorageServiceImpl implements StorageService { @Autowired private StorageMapper storageMapper; @Override public Integer decrease(Long productId, Integer count) { Integer decrease = storageMapper.decrease(productId, count); return decrease; } }
编写controller业务类
@RestController @Slf4j public class StorageController { @Resource private StorageService storageService; @PostMapping(value = "/storage/decrease") public CommonResult decrease(@RequestParam(name = "productId") Long productId, @RequestParam(name = "count") Integer count){ Integer decrease = storageService.decrease(productId, count); return new CommonResult(200,"库存扣减成功!"); } }
编写主启动类
@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class StorageApplication2002 { public static void main(String[] args) { SpringApplication.run(StorageApplication2002.class, args); } }
3、创建 seata-account-service2003 订单微服务构建
编写pom.xml
<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-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- seata -->
<dependency>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
编写application.yml
server: port: 2003 spring: application: name: seata-account-service cloud: nacos: discovery: server-addr: localhost:8848 alibaba: seata: tx-service-group: fsp_tx_group datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://8.129.215.115:3307/seata_account?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 feign: hystrix: enabled: false logging: level: io: seata: info mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: com.atguigu.springcloud.alibaba.domian
编写file.conf 和registry.conf 两个文件 都和 seata-order-service2001 是一样的
编写主启动类
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class AccountApplication2003 { public static void main(String[] args) { SpringApplication.run(AccountApplication2003.class, args); } }
编写实体类
@Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "t_account") public class Account { /** * `id` bigint NOT NULL COMMENT '主键', * `user_id` bigint DEFAULT NULL COMMENT '用户id', * `total` int DEFAULT NULL COMMENT '总共余额', * `used` int DEFAULT NULL COMMENT '花了多少钱', * `residue` int DEFAULT NULL COMMENT '剩余多少', */ private Long id; private Long userId; private BigDecimal total; private BigDecimal used; private BigDecimal residue; }
编写Mapper接口
@Mapper public interface AccountMapper extends BaseMapper<Account> { @Update(" update t_account set used = used + #{money}, residue = residue - #{money} where user_id = #{userId}") Integer decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); }
编写AccountService 接口
public interface AccountService { Integer decrease(Long userId, BigDecimal money); }
编写AccountService 接口 的实现类
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override public Integer decrease(Long userId, BigDecimal money) { try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } Integer decrease = accountMapper.decrease(userId, money); return decrease; } }
编写controller业务类
@RestController @Slf4j public class AccountController { @Resource private AccountService accountService; @PostMapping(value = "/storage/decrease") public CommonResult decrease (@RequestParam(name = "userId") Long userId, @RequestParam(name = "money")BigDecimal money) { Integer decrease = accountService.decrease(userId, money); return new CommonResult(200,"扣减余额成功!"); } }
6、测试 :首先启动Nacos 、Seata 服务器、启动 我们三个微服务
访问地址:http://localhost:2011/order/create?userId=2&productId=10&count=10&money=10
如果其中一个地方报错,那么数据会发送错误,因为没得到事务的控制
@GlobalTransactional(name = "fsp_order_create",rollbackFor = Exception.class) 这个就是控制全局事务


浙公网安备 33010602011771号