简单搭建分布式微服务事务--实践篇

seata微服务的执行流程:

其中

  • TM(Transaction Manager):全局事务管理器,控制全局事务边界,负责全局事务开启、全局提交、全局回滚。

  • RM(Resource Manager):资源管理器,控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

  • TC(Transaction Coordinator):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。

 下边就以最原始典型的account--order---stroge 来展示一下这个seata微服务如何使用

首先使用seata必须要弄一个seata数据库,这里边存储了一些公共的信息。

然后我们需要修改一下seata文件的几个配置文件,分别是

这两个配置文件,里边分别配置了数据库以及服务的一些信息和注册的一些信息:

我们进去修改:

file.conf:

分别有三个地方需要修改:

第一个是service中的一个组名需要修改:这个字段名可以随意更改,但是他是和springcloud交互的一个相当于标记通道之类的东西。

第二个是模型(数据)读取以什么方式来读取,默认是file,我们需要改为db,因为我们要连接数据库嘛。

第三个就是数据库的配置了:

还有一个要改的文件就是registry.conf,听名字也能想出来他是一个注册的配置文件。

需要将注册的方式改为自己用的服务治理框架。在这里我们用nacos。

这两步都做好之后,我们就可以搭建项目的数据库了,数据库结构如下:

其中seata表的话是直接可以在下载的文件夹里找到store的sql文件执行就有的。别的需要自己创建。

Use seata_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;
 
SELECT * FROM t_order;
Use seata_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 seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)
VALUES('1','1','100','0','100'); 
SELECT * FROM t_storage;

Use seata_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 seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000')

之后我们就可以写各个微服务了。按照图示创建父子工程:

注意要将父工程pom内的打包方式写为pom;

然后导入夫工程的包管理依赖:如果你用的是maven创建工程的话还要继承springboot包。

    <!--引入父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <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>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <mysql.version>5.1.47</mysql.version>
        <mybatis.spring.boot.version>1.3.2</mybatis.spring.boot.version>
    </properties>
    <!-- 子模块继承之后,提供作用:锁定版本+子模块不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!--  springboot 2.2.2    -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--  spring cloud Hoxton.SR1   -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--  spring cloud alibaba 2.1.0.RELEASE    -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

之后在子工程order里边修改pom文件:注意seata的包版本要跟你自己的一致

    <parent>
        <groupId>com.jkt</groupId>
        <artifactId>springcloud-seata</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

  <dependencies>
        <!-- nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- nacos -->

        <!-- seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>0.9.0</version>
        </dependency>
        <!-- seata-->
        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配完pom相关的东西,我们就可以写配置文件:application.yaml

server:
  port: 8005   #端口号
spring:
  application:
    name: seata-order-service  # 服务名
  cloud:
    alibaba:
      seata:
        # 自定义事务组名称需要与seata-server中的对应
        tx-service-group: jkt_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848  #将工程服务注册到nacos中
  datasource:
    # 当前数据源操作类型
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 476988

logging:
  level:
    io:
      seata: info   # 日志输出级别
mybatis:
  mapper-locations: classpath:mapper/*.xml  # 定义mybatis的xml文件映射

之后我们要把seata的两个配置文件引入到order微服务中。先看一下order的目录结构把:

这两个配置文件其中我们需要更改一下file.conf里边的服务内容,seata和springboot我理解为是一个服务传递。

seata里边fileconf配置service:vgroup_mapping.my_test_tx_group = "jkt_tx_group"

而springboot的fileconf配置service:

vgroup_mapping.jkt_tx_group = "default"

之后就是一些增删改查配置了:

在这里因为我们的seata需要链接数据库,所以我们不自动引入数据源,而选择自己配置数据源;

需要在config里边加入两个类,这两个类里边需要改一个东西:

@Configuration
public class DataSourceProxyConfig {

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources(mapperLocations));
        return bean.getObject();
    }
}
@Configuration
@MapperScan({"com.jkt.dao"})
public class MyBatisConfig {


}

 

扫描对应的dao包。

还要在启动类上做一些注解:

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) // 排除自动导入数据源
@EnableFeignClients // 开启Feign远程微服务的注解
@EnableDiscoveryClient// 开启注册中心nacos的注解
public class SeateOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(SeateOrderApplication.class, args);
    }

}

之后我们需要书写Feign的远程服务调用:

注意注解中的value是对应微服务的名字。

@FeignClient(value = "seata-storage-service")
public interface StorageFeign {
    @RequestMapping("/storage/decrease")
    public void decrease(@RequestParam("productId")Long productId, @RequestParam("count") Integer count);


}
@FeignClient(value = "seata-account-service")
public interface AccountFeign {
    @RequestMapping("account/decrease")
    public CommonResult decrease(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money);


}

dao:

@Mapper
public interface OrderDao {

    //创建订单
    void create(Order order);

    //修改订单状态
    void update(@Param("userId") Long userId, @Param("status") Integer status);
}

entity:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;
    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {
    private Long id;
    private Long userId;
    private Long productId;
    private Integer count;
    private BigDecimal money;
    private Integer status; //订单状态:0:未付款,1:付款结束
}

controller

@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping("/order/create")
    public CommonResult create(Order order) {
       orderService.create(order);
        return new CommonResult(200,"订单创建成功!");
    }
}

service:

其中GLOBALtRANSACTIONAL是全局事务注解,需要加载对应的方法上。


@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private StorageFeign storageFeign;
    @Autowired
    private AccountFeign accountFeign;
    @GlobalTransactional(name = "jkt_order_tx",rollbackFor = Exception.class) // 在应用的方法上增加

    @Override
    public void create(Order order) {
        log.info("-------->开始创建新订单");
        orderDao.create(order);

        log.info("-------订单微服务开始调用账户,做扣减");
        accountFeign.decrease(order.getUserId(),order.getMoney());
        log.info("-------订单微服务开始调用账户,做扣减end");

        log.info("--------订单微服务开始调用库存,做扣减");
        storageFeign.decrease(order.getProductId(),order.getCount());
        log.info("-------订单微服务开始调用库存,做扣减end");


        log.info("-------修改订单状态");
        orderDao.update(order.getUserId(),1);
        log.info("-------修改订单状态结束");

        log.info("--------下订单结束了,哈哈哈哈");
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值