鋒哥原創的分布式事務框架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元;