nacos(服务注册与发现)+seata(分布式事务)+nginx(负载均衡) 订单-库存-用户编码实例

  1. 启动nacos、seata、nginx

    nacos/bin目录下启动nacos:
    bash startup.sh -p 3333  (指定端口需要修改配置文件)
    bash startup.sh -p 4444 
    bash startup.sh -p 5555
    
    启动nginx:
    nginx
    
    seata/bin目录下启动seata:
    bash seata-server.sh
    
  2. 创建数据库及表(回滚日志表,在对应的数据库下执行conf包下的db_undo_log.sql即可)

    CREATE DATABASE seata_order;
    CREATE DATABASE seata_storage;
    CREATE DATABASE seata_account;
    
    USE seata_account;
    CREATE TABLE t_account(
        id BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
        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=7 CHARSET=utf8;
    INSERT INTO t_account(id, user_id, total, used, residue) VALUES(1,1,1000,0,1000);
    
    -- the table to store seata xid data
    -- 0.7.0+ add context
    -- you must to init this sql for you business databese. the seata server not need it.
    -- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
    -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
    drop table `undo_log`;
    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;
    
    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 CHARSET=utf8;
    
    
    drop table `undo_log`;
    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;
    
    
    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=7 CHARSET=utf8;
    INSERT INTO t_storage(id, product_id, total, used, residue) VALUES(1,1,100,0,100);
    
    drop table `undo_log`;
    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;
    
    
  3. 建父工程,写父pom.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <packaging>pom</packaging>
        <modules>
            <module>seata-order-service3001</module>
            <module>seata-account-service3002</module>
            <module>seata-storage-service3003</module>
        </modules>
        <groupId>com.kenai</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springcloud</name>
        <description>Demo project for Spring Boot</description>
    
        <!--  统一管理jar包版本-->
        <properties>
            <java.version>1.8</java.version>
            <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>
            <lombok.version>1.16.18</lombok.version>
            <mysql.version>5.1.47</mysql.version>
            <druid.version>1.1.16</druid.version>
            <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
        </properties>
    
        <!--子模块继承之后,提供作用:锁定版本+子module不用写groupId和version,不会导入实际的jar包-->
        <dependencyManagement>
            <dependencies>
                <!--spring boot 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 阿里巴巴-->
                <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>
                <!--mysql-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                    <scope>runtime</scope>
                </dependency>
                <!-- druid-->
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid</artifactId>
                    <version>${druid.version}</version>
                </dependency>
                <!--mybatis-->
                <dependency>
                    <groupId>org.mybatis.spring.boot</groupId>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <version>${mybatis.spring.boot.version}</version>
                </dependency>
                <!--junit-->
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>${junit.version}</version>
                </dependency>
                <!--log4j-->
                <dependency>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                    <version>${log4j.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    </project>
    
  4. order子模块下的pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>springcloud</artifactId>
            <groupId>com.kenai</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>seata-order-service3001</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                <exclusions>
    <!--                为了和自己使用的seata版本保持一致,要把下面的剔除掉,否则分布式事务失败(该项目中t_order标回滚失败),-->
                    <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>
            <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>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  5. order子模块下的application.yml

    server:
      port: 3002
    
    spring:
      application:
        name: seata-account-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:1111
        alibaba:
          seata:
            tx-service-group: fsp_tx_group
      datasource:
        url: jdbc:mysql://localhost:3306/seata_account?characterEncoding=utf8&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: 123456
    
    mybatis:
      mapper-locations: classpath:mapper/*.xml
    
    logging:
      level:
        io:
          seata: info
    
  6. 拷贝 seata/conf目录下的file.conf和registry.conf文件到order子模块的resources目录下

  7. 创建order子模块的主启动类

    package com.kenai.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class SeataOrderServiceMain3001 {
        public static void main(String[] args) {
            SpringApplication.run(SeataOrderServiceMain3001.class, args);
        }
    }
    
  8. 创建order子模块的domain.Order.class

    package com.kenai.springcloud.domain;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.math.BigDecimal;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Order {
        private Integer id;
        private Integer userId;
        private Integer productId;
        private BigDecimal count;
        private BigDecimal money;
        private Integer status;
    }
    
  9. 创建order子模块的domain.CommonResult.class

    package com.kenai.springcloud.domain;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @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);
        }
    }
    
  10. 创建order子模块的dao.OrderDao.class

    package com.kenai.springcloud.dao;
    import com.kenai.springcloud.domain.Order;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    
    @Mapper
    public interface OrderDao {
        void create(Order order);
        void update(@Param("userId") Integer userId, @Param("status") Integer status);
    }
    
  11. 创建order子模块的resources.mapper.OrderDaoMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.kenai.springcloud.dao.OrderDao">
        <insert id="create" parameterType="com.kenai.springcloud.domain.Order">
            insert into t_order(id, user_id, product_id, count, money, status) values(NULL, #{userId}, #{productId}, #{count}, #{money}, 0)
        </insert>
        <update id="update">
            update t_order set status = 1 where user_id = #{userId} and status = #{status}
        </update>
    </mapper>
    
  12. 创建order子模块的service.OrderService接口

    package com.kenai.springcloud.service;
    import com.kenai.springcloud.domain.Order;
    public interface OrderService {
        void create(Order order);
    }
    
  13. 创建order子模块的service.StorageService接口

    package com.kenai.springcloud.service;
    import com.kenai.springcloud.domain.CommonResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import java.math.BigDecimal;
    
    @FeignClient(value = "seata-storage-service")
    public interface StorageService {
        @PostMapping("/storage/decrease")
        CommonResult decrease(@RequestParam("productId") Integer productId, @RequestParam("count") BigDecimal count);
    }
    
  14. 创建order子模块的service.AccountService接口

    package com.kenai.springcloud.service;
    import com.kenai.springcloud.domain.CommonResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import java.math.BigDecimal;
    
    @FeignClient(value = "seata-account-service")
    public interface AccountService {
        @PostMapping("/account/decrease")
        CommonResult decrease(@RequestParam("userId") Integer userId, @RequestParam("money")BigDecimal money);
    }
    
  15. 创建order子模块的service.impl.OrderServiceImpl实现类

    package com.kenai.springcloud.service.impl;
    import com.kenai.springcloud.dao.OrderDao;
    import com.kenai.springcloud.domain.Order;
    import com.kenai.springcloud.service.AccountService;
    import com.kenai.springcloud.service.OrderService;
    import com.kenai.springcloud.service.StorageService;
    import io.seata.spring.annotation.GlobalTransactional;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    
    @Service
    @Slf4j
    public class OrderServiceImpl implements OrderService {
        @Resource
        private OrderDao orderDao;
        @Resource
        private AccountService accountService;
        @Resource
        private StorageService storageService;
        @Override
        @GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
        public void create(Order order) {
            // 创建订单
            log.info("开始创建订单");
            orderDao.create(order);
            log.info("创建订单成功");
            // 修改库存
            log.info("开始修改库存");
            storageService.decrease(order.getProductId(), order.getCount());
            log.info("修改库存成功");
            // 修改账户
            log.info("开始修改账户");
            accountService.decrease(order.getUserId(), order.getMoney());
            log.info("修改账户成功");
            // 更改订单状态
            log.info("开始更改订单状态");
            orderDao.update(order.getUserId(), 0);
            log.info("更改订单状态成功");
        }
    }
    
  16. 创建order子模块的controller.OrderController.class

    package com.kenai.springcloud.controller;
    import com.kenai.springcloud.domain.CommonResult;
    import com.kenai.springcloud.domain.Order;
    import com.kenai.springcloud.service.OrderService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import javax.annotation.Resource;
    
    @RestController
    @Slf4j
    public class OrderController {
        @Resource
        private OrderService orderService;
    
        @GetMapping("/order/create")
        public CommonResult create(Order order){
            orderService.create(order);
            return new CommonResult(200, "创建订单成功");
        }
    }
    
  17. 编写order子模块的config.DataSourceProxyConfig.class

    package com.kenai.springcloud.config;
    import com.alibaba.druid.pool.DruidDataSource;
    import io.seata.rm.datasource.DataSourceProxy;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import javax.sql.DataSource;
    
    @Configuration
    public class DataSourceProxyConfig {
        //    告诉mybatis地址
        @Value("${mybatis.mapper-locations}")
        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();
        }
    }
    

    源码粘贴上来为了看的方便些,account模块和storage模块代码类似,详细代码见github.
    seata开头的端口2001-2003代码
    端口2001-2003代码和上面代码基本一样,是学习时写的.上面代码是自己练习手撸的代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值