采购表 purchase
与库存表 stock
。而这两个表的修改是需要满足一致性,即本次操作实际是由应用程序 发起了一个分布式的事务。XA 模式是一个典型的 2PC ,其执行原理如下:
2PC 缺陷
2PC 最大的特点就是简单:原理简单,实现简单。但却存在先天缺陷:同步阻塞、中心化问题、数据不一致、太过保守等。不会,若实现方案设计的较好,这些缺陷是可以弱化的。
wget https://github.com/seata/seata/archive/v1.3.0.zip
wget https://github.com/seata/seata/releases/download/v1.3.0/seata-server-1.3.0.zip
-- create database
create database IF NOT EXISTS `seata`;
use `seata`;
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
[root@centos130 seata]# vim conf/file.conf
[root@centos130 seata]# vim conf/registry.conf
MacBook-Pro:config-center yangwei$ scp config.txt [email protected]:/usr/apps/seata
[root@centos130 seata]# vim config.txt
MacBook-Pro:nacos yangwei$ scp nacos-config.sh [email protected]:/usr/apps/seata/bin
# 运行脚本,192.168.0.100 是 nacos地址
[root@centos130 bin]# sh nacos-config.sh -h 192.168.0.100
# seata-server.sh -m db -p 8091 -h 127.0.0.1
[root@centos130 bin]# ./seata-server.sh -m db -h 192.168.254.130
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"})
public class Stock {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
// 库存
private Integer total;
}
public interface StockRepository extends JpaRepository<Stock, Integer> {
Stock findByName(String name);
}
public interface StockService {
boolean addStock(Stock stock);
Stock getStockByName(String name);
}
@Service
public class StockServiceImpl implements StockService {
@Autowired
private StockRepository stockRepository;
@Override
public boolean addStock(Stock stock) {
Stock originalStock = getStockByName(stock.getName());
originalStock.setTotal(originalStock.getTotal() + stock.getTotal());
return stockRepository.save(originalStock) != null;
}
@Override
public Stock getStockByName(String name) {
Stock stock = stockRepository.findByName(name);
return stock == null ? new Stock(null, name, 0) : stock;
}
}
@RestController
public class StockController {
@Autowired
private StockService stockService;
@PostMapping("/stock/add")
public boolean addStock(@RequestBody Stock stock) {
return stockService.addStock(stock);
}
}
@SpringBootApplication
public class Stock078081 {
public static void main(String[] args) {
SpringApplication.run(Stock078081.class, args);
}
}
spring:
application:
name: msc-stock-service
cloud:
nacos:
config:
file-extension: yml
server-addr: 192.168.0.100:8848
group: SEATA_GROUP
server:
port: 8081
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.0.100:8848
jpa:
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: none
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.254.128:3306/test?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
logging:
pattern:
console: level-%level %msg%n
level:
root: info
org.hibernate: info
org.hibernate.type.descriptor.sql.BasicBinder: trace
org.hibernate.type.descriptor.sql.BasicExtractor: trace
com.yw.sca: debug
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"})
public class Purchase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// 采购资源名称
private String name;
// 采购量
private Integer count;
}
@Data
public class Stock {
private Integer id;
private String name;
private Integer total;
}
public interface PurchaseRepository extends JpaRepository<Purchase, Integer> {
Purchase findByName(String name);
}
public interface PurchaseService {
boolean addPurchase(Purchase purchase);
Purchase getPurchaseByName(String name);
}
public class PurchaseServiceImpl implements PurchaseService {
@Autowired
private PurchaseRepository purchaseRepository;
@Override
public boolean addPurchase(Purchase stock) {
Purchase originalPurchase = getPurchaseByName(stock.getName());
originalPurchase.setCount(originalPurchase.getCount() + stock.getCount());
return purchaseRepository.save(originalPurchase) != null;
}
@Override
public Purchase getPurchaseByName(String name) {
Purchase purchase = purchaseRepository.findByName(name);
return purchase == null ? new Purchase(null, name, 0) : purchase;
}
}
@RestController
public class PurchaseController {
@Autowired
private PurchaseService purchaseService;
@Autowired
private RestTemplate restTemplate;
private static final String STOCK_SERVICE_URL = "http://msc-stock-service";
@PostMapping("/purchase/add/{deno}")
public Boolean addPurchase(@RequestBody Purchase purchase, @PathVariable("deno") int deno) {
purchaseService.addPurchase(purchase);
int i = 3 / deno;
Stock stock = new Stock();
stock.setName(purchase.getName());
stock.setTotal(purchase.getCount());
String url = STOCK_SERVICE_URL + "/stock/add";
return restTemplate.postForObject(url, stock, Boolean.class);
}
}
@SpringBootApplication
public class Purchase078080 {
public static void main(String[] args) {
SpringApplication.run(Purchase078080.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
spring:
application:
name: msc-purchase-service
cloud:
nacos:
config:
file-extension: yml
server-addr: 192.168.0.100:8848
group: SEATA_GROUP
http://localhost:8080/purchase/add/1
进行添加,没有出现异常,stock 和 purchase 表都各自增加了 50。http://localhost:8080/purchase/add/0
进行添加,会出现异常,stock 表没有增加,而 purchase 表增加了,这样就造成了数据的不一致。CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
dependency>
http://localhost:8080/purchase/add/0
,验证下当 purchase 添加后出现异常时,是否都发生了回滚。int i = 3 / deno
那里打上断点,然后查看数据库 undo_log 表:SkyWalking 系统整体由四部分构成:
wget https://www.apache.org/dyn/closer.cgi/skywalking/8.1.0/apache-skywalking-apm-es7-8.1.0.tar.gz
[root@centos130 apache-skywalking-apm-bin-es7]# vim config/application.yml
[root@centos130 apache-skywalking-apm-bin-es7]# vim webapp/webapp.yml
[root@centos130 bin]# ./startup.sh
SkyWalking OAP started successfully!
SkyWalking Web Application started successfully!