目录
1.数据库创建
1.seata_account库下建表
2.seata_order库下建表
3.seata_storage库下建表
4.在每个库下创建回滚日志
2.创建订单模块
2.1建工程
2.2加pom
2.3改yml
2.4file.conf
2.5registry.conf
2.6domain
2.7Dao
2.8Service
2.9controller
2.10config配置
2.11主启动
1.数据库创建
- 这里我们会创建三个服务,一个订单服务,一个库存服务,一个账户服务。
- 当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。
1.seata_account库下建表
CREATE TABLE `t_account` (`id` int NOT NULL,`user_id` int NOT NULL COMMENT '用户id',`total` decimal(20,0) NOT NULL COMMENT '总额度',`used` decimal(20,0) NOT NULL COMMENT '已用额度',`residue` decimal(20,0) NOT NULL COMMENT '剩余可用额度',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
2.seata_order库下建表
CREATE TABLE `t_order` (`id` int NOT NULL,`user_id` int NOT NULL COMMENT '用户id',`product_id` int NOT NULL COMMENT '产品id',`count` int NOT NULL COMMENT '数量',`money` decimal(20,0) NOT NULL COMMENT '金额',`status` int NOT NULL COMMENT '订单状态:0-创建中,1-已完结',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
3.seata_storage库下建表
CREATE TABLE `t_storage` (`id` int NOT NULL,`product_id` int NOT NULL COMMENT '产品id',`total` int NOT NULL COMMENT '总库存',`used` int NOT NULL COMMENT '已用库存',`residue` int NOT NULL COMMENT '剩余库存',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
4.在每个库下创建回滚日志
CREATE TABLE `undo_log` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`branch_id` BIGINT(20) NOT NULL,`xid` VARCHAR(100) NOT NULL,`context` VARCHAR(128) NOT NULL,`rollback_info` LONGBLOB NOT NULL,`log_status` INT(11) NOT NULL,`log_created` DATETIME NOT NULL,`log_modified` DATETIME NOT NULL,`ext` VARCHAR(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2.创建订单模块
2.1建工程
- 1.在父工程下创建seata-order-service2001
- 2.注意jdk和maven版本
2.2加pom
<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></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!--mysql-connector-java--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.example</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--springCloud alibaba Naocs--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--持久化--><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency><!--Sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><groupId>seata-all</groupId><artifactId>io.seata</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>0.9.0</version></dependency>
2.3改yml
server:port: 2001
spring:application:name: seata-order-servicecloud:alibaba:seata:#自定义事务组需要与seat-server中的对应tx-service-group: xz_groupnacos:discovery:server-addr: 192.168.20.50:1111datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_orderusername: rootpassword: 123456feign:sentinel:enabled: falselogging:level:io:seata: infomybatis:mapper-locations: classpath:mapper/*.xml
2.4file.conf
在resource下创建文件file.conf,直接粘贴seata的file.conf即可
transport {# tcp udt unix-domain-sockettype = "TCP"#NIO NATIVEserver = "NIO"#enable heartbeatheartbeat = true#thread factory for nettythread-factory {boss-thread-prefix = "NettyBoss"worker-thread-prefix = "NettyServerNIOWorker"server-executor-thread-prefix = "NettyServerBizHandler"share-boss-worker = falseclient-selector-thread-prefix = "NettyClientSelector"client-selector-thread-size = 1client-worker-thread-prefix = "NettyClientWorkerThread"# netty boss thread size,will not be used for UDTboss-thread-size = 1#auto default pin or 8worker-thread-size = 8}shutdown {# when destroy server, wait secondswait = 3}serialization = "seata"compressor = "none"
}
service {#vgroup->rgroupvgroup_mapping.my_test_tx_group = "xz_group"#only support single nodedefault.grouplist = "127.0.0.1:8091"#degrade current not supportenableDegrade = false#disabledisable = false#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanentmax.commit.retry.timeout = "-1"max.rollback.retry.timeout = "-1"
}client {async.commit.buffer.limit = 10000lock {retry.internal = 10retry.times = 30}report.retry.count = 5tm.commit.retry.count = 1tm.rollback.retry.count = 1
}## transaction log store
store {## store mode: file、dbmode = "db"## file storefile {dir = "sessionStore"# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmax-branch-session-size = 16384# globe session size , if exceeded throws exceptionsmax-global-session-size = 512# file buffer size , if exceeded allocate new bufferfile-write-buffer-cache-size = 16384# when recover batch read sizesession.reload.read_size = 100# async, syncflush-disk-mode = async}## database storedb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.datasource = "dbcp"## mysql/oracle/h2/oceanbase etc.db-type = "mysql"driver-class-name = "com.mysql.cj.jdbc.Driver"url = "jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=UTC"user = "root"password = "123456"min-conn = 1max-conn = 3global.table = "global_table"branch.table = "branch_table"lock-table = "lock_table"query-limit = 100}
}
lock {## the lock store mode: local、remotemode = "remote"local {## store locks in user's database}remote {## store locks in the seata's server}
}
recovery {#schedule committing retry period in millisecondscommitting-retry-period = 1000#schedule asyn committing retry period in millisecondsasyn-committing-retry-period = 1000#schedule rollbacking retry period in millisecondsrollbacking-retry-period = 1000#schedule timeout retry period in millisecondstimeout-retry-period = 1000
}transaction {undo.data.validation = trueundo.log.serialization = "jackson"undo.log.save.days = 7#schedule delete expired undo_log in millisecondsundo.log.delete.period = 86400000undo.log.table = "undo_log"
}## metrics settings
metrics {enabled = falseregistry-type = "compact"# multi exporters use comma dividedexporter-list = "prometheus"exporter-prometheus-port = 9898
}support {## springspring {# auto proxy the DataSource beandatasource.autoproxy = false}
}
2.5registry.conf
在resource下创建文件registryonf,直接粘贴seata的registry.conf即可
registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype = "nacos"nacos {serverAddr = "192.168.20.50:1111"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 = 6000connect.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 {# file、nacos 、apollo、zk、consul、etcd3type = "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 = 6000connect.timeout = 2000}etcd3 {serverAddr = "http://localhost:2379"}file {name = "file.conf"}
}
2.6domain
- 1.创建封装类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {private Integer code;private String message;private T data;public CommonResult(Integer code,String message){this.code=code;this.message=message;}
}
- 2.创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Order {private Integer id;private Integer userId;private Integer productId;private Integer count;private BigDecimal money;private Integer status;
}
2.7Dao
@Mapper
public interface OrderDao {//1.新建订单void create(Order order);//2.修改订单状态;从0到1void update(@Param("userId") Integer userId, @Param("status") Integer status);}
2.8Service
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderDao orderDao;@Resourceprivate StorageService storageService;@Resourceprivate AccountService accountService;@Override@GlobalTransactional(name = "xz_create_order",rollbackFor = Exception.class)public void create(Order order) {//1.新建订单log.info("====开始新建订单====");orderDao.create(order);//2.扣减库存log.info("====订单微服务开始调用库存,扣减count====start");storageService.decrease(order.getProductId(),order.getCount());log.info("====订单微服务开始调用库存,扣减count====end");//3.扣减账户log.info("====订单微服务开始调用账户,扣减money====start");accountService.decrease(order.getUserId(),order.getMoney());log.info("====订单微服务开始调用账户,扣减money====end");//4.修改订单状态log.info("====开始修改订单状态====start");orderDao.update(order.getUserId(),0);log.info("====开始修改订单状态====end");log.info("====下订单结束====");}
}
- 使用openfeign创建AccountService
@FeignClient(value = "seata-account-service")
public interface AccountService {@PostMapping(value = "/storage/decrease")CommonResult decrease(@RequestParam("userId")Integer userId,@RequestParam("money") BigDecimal money);
}
- 使用openfeign创建StorageService
@FeignClient(value = "seata-storage-service")
public interface StorageService {@PostMapping(value = "/storage/decrease")CommonResult decrease(@RequestParam("productId")Integer productId,@RequestParam("count")Integer count);
}
2.9controller
@RestController
public class OrderController {@Resourceprivate OrderService orderService;@GetMapping("/order/create")public CommonResult<Order> create(Order order){orderService.create(order);return new CommonResult<>(200,"订单创建成功");}
}
2.10config配置
@Configuration
public class DataSourceConfig {@Value("${mybatis.mapper-locations}")private String mapperLocations;@Bean@ConfigurationProperties(prefix = "spring.datasource")public DataSource dataSource(){return new DruidDataSource();}@Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource){return new DataSourceProxy(dataSource);}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}
}
@Configuration
@MapperScan("com.xz.springcloud.dao")
public class MybatisConfig {
}
2.11主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class SeataOrderMainApp2001 {public static void main(String[] args) {SpringApplication.run(SeataOrderMainApp2001.class);}
}