-
启动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
-
创建数据库及表(回滚日志表,在对应的数据库下执行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;
-
建父工程,写父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>
-
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>
-
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
-
拷贝 seata/conf目录下的file.conf和registry.conf文件到order子模块的resources目录下
-
创建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); } }
-
创建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; }
-
创建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); } }
-
创建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); }
-
创建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>
-
创建order子模块的service.OrderService接口
package com.kenai.springcloud.service; import com.kenai.springcloud.domain.Order; public interface OrderService { void create(Order order); }
-
创建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); }
-
创建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); }
-
创建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("更改订单状态成功"); } }
-
创建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, "创建订单成功"); } }
-
编写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代码和上面代码基本一样,是学习时写的.上面代码是自己练习手撸的代码
nacos(服务注册与发现)+seata(分布式事务)+nginx(负载均衡) 订单-库存-用户编码实例
最新推荐文章于 2023-09-10 22:47:43 发布