组件及版本
Spring Cloud:Greenwich.RELEASE
Spring Cloud Alibaba:2.1.0.RELEASE
Spring boot: 2.1.3.RELEASE
Mybatis-Plus: 3.4.2
Nacos:2.2.0 官网 https://nacos.io/zh-cn/index.html
seata:0.9.0 官网 http://seata.io/zh-cn/index.html
Swagger:2.9.2
安装Nacos
下载
下载zip格式的安装包,然后进行解压缩操作
启动
#切换目录
cd nacos/bin
#命令启动
startup.cmd -m standalone
访问nacos
注意刚进入事是没有这些配置的(我是搭建完后截的图,这些配置后边会提到)
搭建微服务项目
项目结构:
一共两个微服务,order通过product-service-client 提供的feign 接口调用 product 中的服务
公司项目一般也是这种结构被调用的服务提供一个客户端项目依赖,调用方依赖这个客户端项目实现服务调用。
父项目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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuweichao</groupId>
<artifactId>springcloudalibaba-seata</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>order-service</module>
<module>product-service</module>
</modules>
<name>springcloudalibaba-seata</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<!--依赖版本的锁定-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
order-service 微服务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>springcloudalibaba-seata</artifactId>
<groupId>com.xuweichao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service</artifactId>
<dependencies>
<dependency>
<groupId>com.xuweichao</groupId>
<artifactId>product-service-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
product-service 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>springcloudalibaba-seata</artifactId>
<groupId>com.xuweichao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product-service</artifactId>
<packaging>pom</packaging>
<modules>
<module>product-service-client</module>
<module>product-service-server</module>
</modules>
</project>
product-service-server 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>product-service</artifactId>
<groupId>com.xuweichao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product-service-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>com.xuweichao</groupId>
<artifactId>product-service-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
product-service-client 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>product-service</artifactId>
<groupId>com.xuweichao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuweichao</groupId>
<artifactId>product-service-client</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
数据库建表:订单表 产品表
CREATE TABLE `order_base` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(10) DEFAULT NULL,
`p_id` bigint(20) DEFAULT NULL,
`p_name` varchar(50) DEFAULT NULL,
`p_price` double(10,2) DEFAULT NULL,
`number` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
CREATE TABLE `product_base` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`price` double(10,2) DEFAULT NULL,
`stock` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4
使用 代码生成工具生成数据库试题对象和 service
order-service 微服务 添加配置 application.yml
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
password: xxx
url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/database-name?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
server:
port: 18080
swagger:
enable: true
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
product-service 微服务 添加配置 application.yml
spring:
application:
name: product-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
password: xxx
url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/database-name?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
server:
port: 18080
swagger:
enable: true
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
product-service-server 添加接口:
@Slf4j
@Api(value = "ProductBase 相关接口", tags = "ProductBase 相关接口")
@RestController
@RequestMapping("product")
public class ProductBaseController implements ProductServiceClient {
@Autowired
public IProductBaseService productBaseService;
@Override
@ApiOperation(value = "详情")
@GetMapping("detail/{id}")
public ProductBase getProductBase(@PathVariable Long id) {
log.info("获取的参数:===>>" + id);
return productBaseService.getById(id);
}
@Override
@ApiOperation(value = "修改库存")
@GetMapping("{id}")
public Boolean productUpdateStock(@PathVariable Long id) {
log.info("获取的参数:===>>" + id);
ProductBase productBase = productBaseService.getById(id);
productBase.setStock(productBase.getStock()-1);
return productBaseService.updateById(productBase);
}
}
在product-service-client 添加 Feign 服务接口
@Component
@FeignClient(name = "product-service",path = "/product/")
public interface ProductServiceClient {
@GetMapping("detail/{id}")
ProductBase getProductBase(@PathVariable("id") Long id);
@GetMapping("/{id}")
Boolean productUpdateStock(@PathVariable("id") Long id);
}
order-service 中创建接口
@Slf4j
@Api(value = "OrderBase 相关接口", tags = "OrderBase 相关接口")
@RestController
@RequestMapping("order")
public class OrderBaseController {
@Resource
public IOrderBaseService orderBaseService;
@ApiOperation(value = "添加", notes = "添加")
@GetMapping("{pid}")
public Boolean orderBaseSave(@PathVariable Long pid) {
log.info("获取的参数:===>>" + pid);
return orderBaseService.createOrder(pid);
}
}
OrderServiceImpl:
@Service
public class OrderBaseServiceImpl extends ServiceImpl<OrderBaseMapper, OrderBase> implements IOrderBaseService {
@Autowired
private ProductServiceClient productServiceClient;
@Override
public Boolean createOrder(Long pid) {
ProductBase productBase = productServiceClient.getProductBase(pid);
OrderBase orderBase=new OrderBase();
orderBase.setNumber(2);
orderBase.setPId(productBase.getId());
orderBase.setPName(productBase.getName());
orderBase.setPPrice(productBase.getPrice());
orderBase.setUserName("超超");
this.save(orderBase);
productServiceClient.productUpdateStock(orderBase.getPId());
return Boolean.TRUE;
}
}
这里是模拟的创建订单和减库存的操作
安装Seata
下载
下载地址: https://github.com/seata/seata/releases/v0.9.0/
进入conf 目录修改配置文件register.conf,我们这里是用nacos 作为注册和配置中心的,所以精简一下配置
registry {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
}
初始Seata配置
进入 seata-0.9.0\server\src\main\resources 目录 找到 nacos-config.txt 文件
第一个箭头哪里代表事务组
这里的语法为: service.vgroup_mapping.${your - service - gruop}=default ,中间的${your - service - gruop} 为自己定义的服务组名称, 这里需要我们在程序的配置文件中配置。
store 那里改成自己的数据源配置,并建表
-- the table to store GlobalSession data
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
);
-- the table to store BranchSession data
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT ,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256) ,
`lock_key` VARCHAR(128) ,
`branch_type` VARCHAR(8) ,
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
);
-- the table to store lock data
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` LONG ,
`branch_id` LONG,
`resource_id` VARCHAR(256) ,
`table_name` VARCHAR(32) ,
`pk` VARCHAR(36) ,
`gmt_create` DATETIME ,
`gmt_modified` DATETIME,
PRIMARY KEY(`row_key`)
);
创建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=3 DEFAULT CHARSET=utf8
cd conf
nacos-config.sh 127.0.0.1
执行之后 在nacos 中就能看上最上边图中展示的效果了。
启动seata,进入 seata-server-0.9.0\seata\bin 目录执行
seata-server.bat -p 9000 -m file
将register.conf 文件分别拷贝到 order-service 和product-service-server 微服务 的resources 目录下
添加服务注册发现和 Seata事务组
分别在两个微服务resources目录下创建 bootstrap.yml
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: public
group: SEATA_GROUP
discovery:
server-addr: 127.0.0.1:8848
alibaba:
seata:
tx-service-group: product_service_group
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: public
group: SEATA_GROUP
discovery:
server-addr: 127.0.0.1:8848
alibaba:
seata:
tx-service-group: order_service_group
在 ProductApplication 和OrderApplication 中添加注解 @EnableDiscoveryClient 用户服务发现
并在OrderApplication 添加 @EnableFeignClients(basePackages = "com.xxx") 开启feign服务调用。
修改creatOrder()方法添加注解 @GlobalTransactional
@GlobalTransactional
@Override
public Boolean createOrder(Long pid) {
ProductBase productBase = productServiceClient.getProductBase(pid);
OrderBase orderBase=new OrderBase();
orderBase.setNumber(2);
orderBase.setPId(productBase.getId());
orderBase.setPName(productBase.getName());
orderBase.setPPrice(productBase.getPrice());
orderBase.setUserName("超超");
this.save(orderBase);
productServiceClient.productUpdateStock(orderBase.getPId());
return Boolean.TRUE;
}
在服务调用方order-service中添加数据源代理配置
@Configuration
public class DataSourceProxyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean
public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}
product-base 中加入两条数据
然后分别启动两个微服务,在nacos界面服务列表可以看到 服务都注册成功了
浏览器或者swagger执行
http://localhost:18080/order/1
可以看到执行成功
订单表添加成功,库存也减了。然后任务增加一个异常测试一下事务的回滚。
执行后可以看到订单和库存都没有成功。