锋哥原创的分布式事务框架Seata视频教程:
实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)_哔哩哔哩_bilibili实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)共计10条视频,包括:1 阿里分布式事务框架Seata简介、2 分布式事务简介、3 SpringCloud Alibaba分布式基础案例搭建等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV1Uf4y1579F/我们模拟一个简单下单业务,客户端调用rest对外服务,rest服务再调用订单服务实现创建订单和账户服务实现账户扣钱操作,最终来完整下单业务;
2.1 案例架构设计
所有服务都注册到nacos中,方便feign远程调用;订单服务,账户服务各自有独立数据库;架构设计如下图:
整体项目结构如下图:
seatatest
是父项目,主要是做一些依赖管理,依赖版本管理,管理所有子module项目;
seata-common
子项目,主要是引入其他子项目需要的公共依赖,以及公共实体,工具类,配置类的统一封装;
seata-order
子项目,主要提供订单服务,生成订单;
seata-account
子项目,主要提供账户服务,根据订单扣钱操作;
seata-web
子项目,主要处理客户端下单请求,feign远程调用order,和account服务接口,最终完成下单处理;
2.2 数据库设计
我们新建两个数据库,分别是db_order
(订单数据库),db_account
(账户数据库),
db_order
数据库里面新建表t_order
订单表:
CREATE TABLE `t_order` (`id` int(11) NOT NULL AUTO_INCREMENT,`orderNo` varchar(100) DEFAULT NULL,`userId` int(11) DEFAULT NULL,`count` int(11) DEFAULT NULL,`amount` int(11) DEFAULT NULL,`remark` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
db_account
数据库里面新建表t_account
用户账户表:
CREATE TABLE `t_account` (`id` int(11) NOT NULL AUTO_INCREMENT,`userId` int(11) DEFAULT NULL,`balance` int(11) DEFAULT NULL,`remark` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
插入数据:
insert into `t_account` (`id`, `userId`, `balance`, `remark`) values('1','1','2000','jack的账户');
insert into `t_account` (`id`, `userId`, `balance`, `remark`) values('2','2','1000','marry的账户');
2.3 seatatest父项目搭建
seatatest是父项目,主要是做一些依赖管理,依赖版本管理,管理所有子module项目;
注意,它的packaging
类型是pom
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.java1234</groupId><artifactId>seatatest</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>seata-common</module><module>seata-order</module><module>seata-account</module><module>seata-web</module></modules><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><druid.version>1.1.10</druid.version><spring-cloud.version>Hoxton.SR8</spring-cloud.version><springboot.version>2.3.2.RELEASE</springboot.version><springcloudalibaba.version>2.2.4.RELEASE</springcloudalibaba.version><fastjson.version>1.2.35</fastjson.version><commons-lang3.version>3.6</commons-lang3.version><seata-common.version>1.0-SNAPSHOT</seata-common.version><mybatis.version>2.1.0</mybatis.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-dependencies</artifactId><version>${springboot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${springcloudalibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- 连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${commons-lang3.version}</version></dependency><dependency><groupId>com.java1234</groupId><artifactId>seata-common</artifactId><version>${seata-common.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2.4 seata-common子项目搭建
seata-common
子项目,主要是引入其他子项目需要的公共依赖,以及公共实体,工具类,配置类的统一封装;
项目结构:
<?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>seatatest</artifactId><groupId>com.java1234</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-common</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></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-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId></dependency><!-- spring boot redis 缓存引入 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- lettuce pool 缓存连接池 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies></project>
订单实体Order
:
package com.java1234.entity;/**订单表实体* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-13 10:26*/
public class Order {private Integer id; // 编号private String orderNo; // 订单号private Integer userId; // 用户编号private Integer count; // 购买数量private Integer amount; // 购买金额private String remark; // 备注public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOrderNo() {return orderNo;}public void setOrderNo(String orderNo) {this.orderNo = orderNo;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}public Integer getAmount() {return amount;}public void setAmount(Integer amount) {this.amount = amount;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}
}
账户实体Account
:
package com.java1234.entity;/*** 用户账户表* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-13 10:36*/
public class Account {private Integer id; // 编号private Integer userId; // 用户编号private Integer balance; // 账户余额private String remark; // 备注public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Integer getBalance() {return balance;}public void setBalance(Integer balance) {this.balance = balance;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}
}
2.5 seata-order子项目搭建
seata-order
子项目,主要提供订单服务,生成订单;
项目结构:
pom.xml
:
<?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>seatatest</artifactId><groupId>com.java1234</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-order</artifactId><dependencies><dependency><groupId>com.java1234</groupId><artifactId>seata-common</artifactId></dependency></dependencies></project>
application.yml
:
server:port: 8081servlet:context-path: /spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_order?serverTimezone=Asia/Shanghaiusername: rootpassword: 123456cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-ordermybatis:mapper-locations: classpath:mybatis/mapper/*.xml
启动类OrderApplication
:
package com.java1234;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@MapperScan("com.java1234.mapper")
@EnableDiscoveryClient
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}}
OrderMapper.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java1234.mapper.OrderMapper"><select id="createOrder" parameterType="com.java1234.entity.Order" >insert into t_order values(null,#{orderNo},#{userId},#{count},#{amount},#{remark})</select>
</mapper>
OrderMapper
接口:
package com.java1234.mapper;import com.java1234.entity.Order;/*** @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-13 10:43*/
public interface OrderMapper {/*** 创建订单* @param order*/void createOrder(Order order);}
OrderService
接口:
package com.java1234.service;import com.java1234.entity.Order;/*** 订单service接口* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-14 11:00*/
public interface OrderService {/*** 创建订单* @param order*/void createOrder(Order order);}
OrderServiceImpl
实现类:
package com.java1234.service.impl;import com.java1234.entity.Order;
import com.java1234.mapper.OrderMapper;
import com.java1234.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-14 11:02*/
@Service("orderService")
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Overridepublic void createOrder(Order order) {orderMapper.createOrder(order);}
}
OrderController
:
package com.java1234.controller;import com.java1234.entity.Order;
import com.java1234.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.UUID;/*** @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-14 11:13*/
@RestController
@RequestMapping("/seata")
public class OrderController {@Autowiredprivate OrderService orderService;/*** 创建订单* @param order* @return*/@PostMapping("/createOrder")public boolean createOrder(@RequestBody Order order){System.out.println("order:"+order);order.setOrderNo(UUID.randomUUID().toString()); // 生成订单IDorderService.createOrder(order);return true;}
}
2.6 seata-account子项目搭建
seata-account
子项目,主要提供账户服务,根据订单扣钱操作;
项目结构:
pom.xml
:
<?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>seatatest</artifactId><groupId>com.java1234</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-account</artifactId><dependencies><dependency><groupId>com.java1234</groupId><artifactId>seata-common</artifactId></dependency></dependencies></project>
application.yml
:
server:port: 8082servlet:context-path: /spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_account?serverTimezone=Asia/Shanghaiusername: rootpassword: 123456cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-accountmybatis:mapper-locations: classpath:mybatis/mapper/*.xml
AccountApplication
启动类:
package com.java1234;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@MapperScan("com.java1234.mapper")
@EnableDiscoveryClient
public class AccountApplication {public static void main(String[] args) {SpringApplication.run(AccountApplication.class, args);}}
AccountMapper.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java1234.mapper.AccountMapper"><select id="decrease" parameterType="Map" >UPDATE t_account SET balance=balance-#{amount} WHERE userId=#{userId}</select>
</mapper>
AccountMapper
接口:
package com.java1234.mapper;import java.util.Map;/*** 账户Mapper接口* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-13 10:43*/
public interface AccountMapper {/*** 账户扣钱*/void decrease(Map map);}
AccountService
接口:
package com.java1234.service;import java.util.Map;/*** 账户service接口* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-15 12:58*/
public interface AccountService {/*** 账户扣钱*/void decrease(Map map);
}
`AccountServiceImpl`接口实现类:
package com.java1234.service.impl;import com.java1234.mapper.AccountMapper;
import com.java1234.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Map;/*** 账户Service实现类* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-15 13:00*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic void decrease(Map map) {accountMapper.decrease(map);}
}
AccountController
:
package com.java1234.controller;import com.java1234.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;/*** 账户Controller* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-15 13:06*/
@RestController
@RequestMapping("/account")
public class AccountController {@Autowiredprivate AccountService accountService;/*** 给指定用户账户扣钱* @param amount* @param userId* @return*/@PostMapping("/decrease")public boolean decrease(@RequestParam("amount")Integer amount, @RequestParam("userId")Integer userId){System.out.println("amount:"+amount+",userId:"+userId);Map<String,Object> map=new HashMap<>();map.put("amount",amount);map.put("userId",userId);accountService.decrease(map);return true;}}
2.7 seata-web子项目搭建
seata-web
子项目,主要处理客户端下单请求,feign远程调用order,和account服务接口,最终完成下单处理;
项目结构:
pom.xml
:
<?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>seatatest</artifactId><groupId>com.java1234</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-web</artifactId><dependencies><dependency><groupId>com.java1234</groupId><artifactId>seata-common</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency></dependencies></project>
application.yml
:
server:port: 80servlet:context-path: /spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848application:name: seata-web
WebApplication
:
package com.java1234;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableFeignClients(basePackages = "com.java1234.feign")
@EnableDiscoveryClient
public class WebApplication {public static void main(String[] args) {SpringApplication.run(WebApplication.class, args);}}
OrderFeignService
:
package com.java1234.feign;import com.java1234.entity.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;/*** 订单接口feign远程调用* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-16 10:55*/
@FeignClient("seata-order")
public interface OrderFeignService {/*** 创建订单* @param order* @return*/@PostMapping("/seata/createOrder")public boolean createOrder(@RequestBody Order order);}
AccountFeignService
:
package com.java1234.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** 账号接口feign远程调用* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-16 10:55*/
@FeignClient("seata-account")
public interface AccountFeignService {/*** 账号扣钱* @param amount* @param userId* @return*/@PostMapping("/account/decrease")public boolean decrease(@RequestParam("amount")Integer amount, @RequestParam("userId")Integer userId);}
WebController
:
package com.java1234.controller;import com.java1234.entity.Order;
import com.java1234.feign.AccountFeignService;
import com.java1234.feign.OrderFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;/*** web-rest接口* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2021-07-15 16:32*/
@RestController
public class WebController {@Autowiredprivate OrderFeignService orderFeignService;@Autowiredprivate AccountFeignService accountFeignService;/*** 下单 1,创建订单 2,账户扣钱* @param order* @return*/@PostMapping("/shopping")public boolean shopping(Order order){orderFeignService.createOrder(order); // 创建订单accountFeignService.decrease(order.getAmount(),order.getUserId()); // 账户扣钱return true;}}
2.8 postman测试
首先启动Nacos服务注册中心:
项目启动:
Nacos控制台:
服务注册成功!
测试接口:http://localhost/shopping
接口测试OK:
订单表生成订单:
账户表id=1的jack账户成功扣减180元;