Seata 的AT模式需求实战_04
接上一篇:Seata 與 Nacos Config配置中心整合_03
模擬下單場景:首先去在自己的本地創建一條下單記錄,同時,還要去調用庫存服務,執行減庫存操作。
文章目錄
- 一、數據庫部分
- 1. 訂單庫創建
- 2. 表結構初始化
- 3. 庫存數據庫創建
- 4. 庫存表結構初始化
- 5. 依賴新增
- 二、訂單微服務代碼部分
- 2.1. 創建實體類
- 2.2. 創建接口類
- 2.3. 調整控制層邏輯
- 2.4. 修改配置文件
- 三、庫存微服務代碼部分
- 3.1. 創建實體類
- 3.2. 接口庫存Dao
- 3.3. 容錯代碼
- 3.4. 控制層邏輯調整
- 3.5. 配置文件修改
- 3.6. 初始化庫存
- 3.7. 容錯代碼簡述
- 四、測試驗證
- 4.1. 啟動服務
- 4.2. 發起第一輪請求
- 4.3. 拋出異常
- 4.4. 異常信息監控
- 4.5. 流程梳理
- 4.6. 數據庫驗證
- 4.7. 發起第二輪請求
- 4.8. 發起第三輪請求
- 4.9. 數據庫數據驗證
- 4.10. 發起第四輪請求
- 4.11. 數據庫驗證
一、數據庫部分
1. 訂單庫創建
新增數據庫orderdb,
2. 表結構初始化
并創建訂單表 和AT模式seata需要用到的undolog表
3. 庫存數據庫創建
新增數據庫stockdb,并創建庫存表 和AT模式seata需要用到的undolog表
4. 庫存表結構初始化
并創建庫存表 和AT模式seata需要用到的undolog表
5. 依賴新增
在parent的pom.xml中新增依賴
<!--分布式事務--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>1.3.0</version></dependency><!--Lombok引入--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- Spring Boot JPA 依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>二、訂單微服務代碼部分
我們對現在的order-serv訂單服務基礎上調整
2.1. 創建實體類
package com.gblfy.entity;import lombok.Data;import javax.persistence.*; import java.io.Serializable; import java.math.BigDecimal;@Entity @Table(name = "order_tb") @Data public class Order implements Serializable {public Order() {}@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;/*** 標題*/@Column(name = "product_id")private Integer productId;/*** 原價格*/@Column(name = "user_id")private Integer userId;}2.2. 創建接口類
package com.gblfy.dao;import com.gblfy.entity.Order; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;@Repository public interface OrderResposity extends JpaRepository<Order, Integer> { }業務代碼修改
原代碼
2.3. 調整控制層邏輯
調整后代碼邏輯如下
package com.gblfy.controller;import com.gblfy.dao.OrderResposity; import com.gblfy.entity.Order; import io.seata.spring.annotation.GlobalTransactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderResposity orderResposity;//http://localhost:8000/order/create?productId=11&userId=11222@GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")@GetMapping("/order/create")public String createOrder(Integer productId, Integer userId) {Order order = new Order();order.setProductId(productId);order.setUserId(userId);orderResposity.save(order);String result = restTemplate.getForObject("http://stock-serv/stock/reduce/" + productId, String.class);if (!result.equals("success")) {throw new RuntimeException();}return result;} }2.4. 修改配置文件
server:port: 9002 spring:datasource:url: jdbc:mysql://192.168.159.105:3306/orderdbusername: rootpassword: 123456cloud:nacos:discovery:service: order-servgroup: SEATA_GROUPserver-addr: 192.168.159.105:8848application:name: order-serv seata:enabled: truetx-service-group: order-serviceconfig:type: nacosnacos:namespace: publicserverAddr: 192.168.159.105:8848group: SEATA_GROUPuserName: "nacos"password: "nacos"registry:type: nacosnacos:application: seata-serverserverAddr: 192.168.159.105:8848group: SEATA_GROUPnamespace: publicuserName: "nacos"password: "nacos"三、庫存微服務代碼部分
我們對現在的stock-serv庫存服務基礎上調整
3.1. 創建實體類
package com.gblfy.entity;import lombok.Data;import javax.persistence.*; import java.io.Serializable;@Entity @Table(name = "stock") @Data public class Stock implements Serializable {public Stock() {}@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;/*** 標題*/@Column(name = "count")private Integer count;/****/@Column(name = "product_id")private Integer productId;}3.2. 接口庫存Dao
package com.gblfy.dao;import com.gblfy.entity.Stock; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;@Repository public interface StockResposity extends JpaRepository<Stock, Integer> {// 通過商品ID查詢庫存信息public Stock getFirstByProductId(Integer productId); }3.3. 容錯代碼
在StockApplication 中新增一個類的定義 用于獲取隨機的boolean值,模擬隨機報錯的情況
package com.gblfy;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean;import java.util.Random;@SpringBootApplication public class StockApplication {public static void main(String[] args) {SpringApplication.run(StockApplication.class);}@Beanpublic Random generate(){return new Random();} }3.4. 控制層邏輯調整
修改庫存controller,減庫存直接操作數據庫
package com.gblfy.controller;import com.gblfy.dao.StockResposity; import com.gblfy.entity.Stock; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;import java.util.Random;/*** 庫存服務** @author gblfy* @date 2021/8/19*/ @RestController public class StockController {private static final Logger LOGGER = LoggerFactory.getLogger(StockController.class);@Autowiredprivate StockResposity resposity;@Autowiredprivate Random random;@GetMapping("/stock/reduce/{productId}")public String reduce(@PathVariable Integer productId) {LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID());if (random.nextBoolean()) {throw new RuntimeException("this is a mock Exception");}Stock stock = resposity.getFirstByProductId(productId);if (stock != null) {stock.setCount(stock.getCount() - 1);resposity.save(stock);return "success";}return "fail";} }3.5. 配置文件修改
server:port: 8002 spring:datasource:url: jdbc:mysql://192.168.159.105:3306/stockdbusername: rootpassword: 123456cloud:nacos:discovery:service: stock-servgroup: SEATA_GROUPserver-addr: 192.168.159.105:8848application:name: stock-serv seata:enabled: truetx-service-group: order-serviceconfig:type: nacosnacos:namespace: publicserverAddr: 192.168.159.105:8848group: SEATA_GROUPuserName: "nacos"password: "nacos"registry:type: nacosnacos:application: seata-serverserverAddr: 192.168.159.105:8848group: SEATA_GROUPnamespace: publicuserName: "nacos"password: "nacos"tx-service-group:的值從nacos這里取,必須保持一致
3.6. 初始化庫存
在扣庫存微服務初始化數據
INSERT INTO `stock` VALUES (1, 100, 1);3.7. 容錯代碼簡述
這里在扣庫存服務塊中拋出異常,是為了模擬在創建訂單庫完成后,扣庫存失敗的場景。驗證訂單數據庫的數據是否產生,如果沒有產生從而說明分布式事務生效了,相當于跨庫進行事物的回滾。
四、測試驗證
4.1. 啟動服務
啟動訂單服務
啟動扣庫存服務
4.2. 發起第一輪請求
http://localhost:9002/order/create?productId=11&userId=11222
4.3. 拋出異常
發生錯誤,進入咱們的容錯邏輯
4.4. 異常信息監控
扣庫存微服務模塊
訂單模塊微服務
4.5. 流程梳理
①請求下單服務
②創建訂單并在訂單數據庫中插入一條訂單數據
③調用扣庫存微服務
④發生異常
如果分布式事務生效的話,事務的原子性一致性應該不會產生臟數據。對吧!
4.6. 數據庫驗證
庫存為服務發生異常后,先查看訂單數據庫是否產生訂單數據
再查看扣庫存數據庫是否扣庫存成功
從以上截圖中可以看出咱們的分布式事務生效了,既沒有產生臟數據,有沒有扣庫存成功,符合預期。
4.7. 發起第二輪請求
再次發起請求,繼續測試,由于我扣庫存的數據庫中只有productId=1的商品,不管我請求多少次,都會失敗,臟數據不會產生扣庫存也會失敗
http://localhost:9002/order/create?productId=11&userId=11222
4.8. 發起第三輪請求
把請求的地址調整成正確的路徑,再次請求測試
http://localhost:9002/order/create?productId=1&userId=11222
終于有成功的了
4.9. 數據庫數據驗證
查看訂單數據庫是是否產生訂單數據
從圖中,可以看出差生了一條訂單數據
在查看扣庫存數據是否也減少了呢
從上圖可以看出,產生一條訂單數據,就會減少一個庫存,符合咱們的預期,分布式事務也生效了。
4.10. 發起第四輪請求
繼續提高并發測試,測試結果有的成功了,有的失敗了。
4.11. 數據庫驗證
產生了5條訂單數據
相應的庫存也減少了5個
測試驗證到此為止!
總結
以上是生活随笔為你收集整理的Seata 的AT模式需求实战_04的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot2 整合 XFIRE
- 下一篇: 生产环境遇到难题,你是如何解决的?