博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringCloudAlibaba--Seata简单案例(一)
阅读量:2444 次
发布时间:2019-05-10

本文共 13044 字,大约阅读时间需要 43 分钟。

Seata业务数据库准备

这里会创建3个服务,一个订单服务,一个库存服务,一个账户服务。

当用户下单时,会在订单服务中创建一个订单,然后通过RPC调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。

该操作跨越3个数据库,有两次远程调用,很明显会有分布式事务问题。

创建业务数据库:

  • seata_order:存储订单的数据库;
  • seata_storage:存储库存的数据库
  • seata_account:存储账户信息的数据库

在这里插入图片描述

建业务表:

  • seata_order库下建t_order表
CREATE TABLE t_order( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id', `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id', `count` INT(11) DEFAULT NULL COMMENT '数量', `money` DECIMAL(11, 0) DEFAULT NULL COMMENT '金额', `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完成')ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
  • seata_storage库下建t_storage表
CREATE TABLE t_storage(	`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,	`product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',	`total` INT(11) DEFAULT NULL COMMENT '总库存',	`used` INT(11) DEFAULT NULL COMMENT '已用库存',	`residue` INT(11) DEFAULT NULL COMMENT '剩余库存')ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO t_storage VALUES(1, 1, 100, 0, 100);
  • 在seata_account库下建t_account表
CREATE TABLE t_account(	`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',	`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',	`total` DECIMAL(10, 0) DEFAULT NULL COMMENT '总额度',	`used` DECIMAL(10, 0) DEFAULT NULL COMMENT '已用余额',	`residue` DECIMAL(10, 0) DEFAULT '0' COMMENT '剩余可用额度')ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO t_account VALUES(1, 1, 1000, 0, 1000);

建对应的回滚日志表:

订单、库存、账户3个库下都需要建各自的回滚日志表

-- 此脚本必须初始化在你当前的业务数据库中,用于AT模式XID记录。与server端无关-- 增加唯一索引ux_undo_logCREATE 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;

在这里插入图片描述

订单/库存/账户业务微服务准备

业务需求:下订单 -> 减库存 -> 扣余额 -> 改订单状态

新建Order-Module:

  1. seata-order-service2001
  2. pom.xml
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
seata-all
io.seata
io.seata
seata-all
1.0.0
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
mysql
mysql-connector-java
5.1.37
com.alibaba
druid-spring-boot-starter
1.1.10
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.0
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
true
  1. application.yml
server:  port: 2001spring:  application:    name: seata-order-service  cloud:    alibaba:      seata:        tx-service-group: fsp_tx_group #自定义组名称,需要与seata-server中配置的对应    nacos:      discovery:        server-addr: localhost:8848  datasource:    driver-class-name: com.mysql.jdbc.Driver    url: jdbc:mysql://localhost:3306/seata_order    username: root    password: 123456feign:  hystrix:    enabled: falselogging:  level:     io:      seata: info      mybatis:  mapperLocations: classpath:mapper/*.xml
  1. file.conf
transport {  # tcp udt unix-domain-socket  type = "TCP"  #NIO NATIVE  server = "NIO"  #enable heartbeat  heartbeat = true  #thread factory for netty  thread-factory {    boss-thread-prefix = "NettyBoss"    worker-thread-prefix = "NettyServerNIOWorker"    server-executor-thread-prefix = "NettyServerBizHandler"    share-boss-worker = false    client-selector-thread-prefix = "NettyClientSelector"    client-selector-thread-size = 1    client-worker-thread-prefix = "NettyClientWorkerThread"    # netty boss thread size,will not be used for UDT    boss-thread-size = 1    #auto default pin or 8    worker-thread-size = 8  }  shutdown {    # when destroy server, wait seconds    wait = 3  }  serialization = "seata"  compressor = "none"}service {  vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称  default.grouplist = "127.0.0.1:8091"  enableDegrade = false  disable = false  max.commit.retry.timeout = "-1"  max.rollback.retry.timeout = "-1"  disableGlobalTransaction = false}client {  async.commit.buffer.limit = 10000  lock {    retry.internal = 10    retry.times = 30  }  report.retry.count = 5  tm.commit.retry.count = 1  tm.rollback.retry.count = 1}## transaction log storestore {  ## store mode: file、db  mode = "db"  ## file store  file {    dir = "sessionStore"    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions    max-branch-session-size = 16384    # globe session size , if exceeded throws exceptions    max-global-session-size = 512    # file buffer size , if exceeded allocate new buffer    file-write-buffer-cache-size = 16384    # when recover batch read size    session.reload.read_size = 100    # async, sync    flush-disk-mode = async  }  ## database store  db {    ## 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.jdbc.Driver"    url = "jdbc:mysql://127.0.0.1:3306/seata"    user = "root"    password = "123456"    min-conn = 1    max-conn = 3    global.table = "global_table"    branch.table = "branch_table"    lock-table = "lock_table"    query-limit = 100  }}lock {  ## the lock store mode: local、remote  mode = "remote"  local {    ## store locks in user's database  }  remote {    ## store locks in the seata's server  }}recovery {  #schedule committing retry period in milliseconds  committing-retry-period = 1000  #schedule asyn committing retry period in milliseconds  asyn-committing-retry-period = 1000  #schedule rollbacking retry period in milliseconds  rollbacking-retry-period = 1000  #schedule timeout retry period in milliseconds  timeout-retry-period = 1000}transaction {  undo.data.validation = true  undo.log.serialization = "jackson"  undo.log.save.days = 7  #schedule delete expired undo_log in milliseconds  undo.log.delete.period = 86400000  undo.log.table = "undo_log"}## metrics settingsmetrics {  enabled = false  registry-type = "compact"  # multi exporters use comma divided  exporter-list = "prometheus"  exporter-prometheus-port = 9898}support {  ## spring  spring {    # auto proxy the DataSource bean    datasource.autoproxy = false  }}
  1. registry.conf
registry {  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa  type = "nacos"  nacos {    serverAddr = "localhost:8848"    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 = 6000    connect.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、etcd3  type = "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 = 6000    connect.timeout = 2000  }  etcd3 {    serverAddr = "http://localhost:2379"  }  file {    name = "file.conf"  }}
  1. domain
@Data@AllArgsConstructor@NoArgsConstructorpublic class Order {
private Long id; private Long userId; private Long productId; private Integer count; private BigDecimal money; /* 订单状态: 0:创建中;1:已完成; */ private Integer status;}
@Data@AllArgsConstructor@NoArgsConstructorpublic class CommonResult
{
private Integer code; private String message; private T data; public CommonResult(Integer code, String message){
this(code, message, null); }}
  1. Dao接口及实现
@Mapperpublic interface OrderDao {
//新建订单 void create(Order order); //修改订单状态 0 -> 1 void update(@Param("userId") Long userId, @Param("status") Integer status);}
insert into t_order (id,user_id,product_id,count,money,status) values (null,#{userId},#{productId},#{count},#{money},0);
update t_order set status = 1 where user_id=#{userId} and status = #{status};
  1. Service接口及实现
public interface OrderService {
//创建订单 void create(Order order);}
@Service@Slf4jpublic class OrderServiceImpl implements OrderService {
@Resource OrderDao orderDao; @Resource StorageService storageService; @Resource AccountService accountService; @Override public void create(Order order) {
log.info("------>开始新建订单"); orderDao.create(order); log.info("------>订单微服务开始调用库存,扣减count"); storageService.decrease(order.getProductId(), order.getCount()); log.info("------>订单微服务开始调用库存,扣减end"); log.info("------>订单微服务开始调用账户,扣减money"); accountService.decrease(order.getUserId(), order.getMoney()); log.info("------>订单微服务开始调用账户,扣减end"); log.info("------>开始修改订单状态"); orderDao.update(order.getUserId(), 0); log.info("------>修改订单状态完毕"); log.info("------>下单完毕"); }}
@FeignClient(value = "seata-account-service")public interface AccountService {
//扣减账户余额 @PostMapping(value = "/account/decrease") CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);}
@FeignClient(value = "seata-storage-service")public interface StorageService {
//扣减库存 @PostMapping(value = "/storage/decrease") CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);}
  1. Controller
@RestControllerpublic class OrderController{
@Resource OrderService orderService; @GetMapping("/order/create") public CommonResult create(Order order) {
orderService.create(order); return new CommonResult(200,"订单创建成功"); }}
  1. Config配置
@Configuration@MapperScan({
"pers.zhang.springcloud.alibaba.dao"})public class MyBatisConfig {
}
@Configurationpublic class DataSourceProxyConfig {
@Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){
return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource); } @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); }}
  1. 主启动类
@EnableFeignClients@EnableDiscoveryClient@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动创建public class SeataOrderMainApp2001 {
public static void main(String[] args) {
SpringApplication.run(SeataOrderMainApp2001.class, args); }}

转载地址:http://wvpqb.baihongyu.com/

你可能感兴趣的文章
git 应用程序本身更新_如何使用Git通过SFTP正确部署Web应用程序
查看>>
laravel 绑定 区别_Laravel快速提示:模型路线绑定
查看>>
symfony_单文件Symfony应用程序? 是的,有了MicroKernelTrait!
查看>>
phpstorm许可证_PhpStorm 8发布-查看新功能并获取免费许可证
查看>>
azure免费一个月_将Windows Azure提升到一个新的水平
查看>>
app engine 入门_Google App Engine和PHP:入门
查看>>
限流 php接口限流 代码_有效地使用PHP流
查看>>
使用Pspell查找和纠正拼写错误的单词
查看>>
PHP依赖注入容器性能基准
查看>>
livereload_LiveReload
查看>>
如何在Windows上安装Ghost
查看>>
phpstorm -xmx_PhpStorm 8-新功能
查看>>
Chrome 27的新功能
查看>>
浏览器趋势(2013年5月):IE8降至10%以下
查看>>
谁偷了我的CPU?
查看>>
Microsoft将IE10更新推送到Windows 7
查看>>
liferay_云中的Liferay
查看>>
SQL或NoSQL:Google App Engine-第1部分
查看>>
SitePoint Podcast#178:Web设计过程和创造力
查看>>
移动端获取视频第一帧移动端_后端即服务-第1部分
查看>>