Docker installation Seata server

Yes ZG! 2022-05-14 14:47:17 阅读数:150

dockerinstallationseataserver

Catalog

1. Get profile
2. install MySQL
3. Create database , And implement sql Script
4. start-up nacos, And add namespace
5. modify seata-server The configuration file
6. Set up nacos Configuration center
7. start-up seata-server
8. test
9. seata-storage-service
10. seata-account-service
11. seata-order-service
12. verification





Environmental Science

  • CentOS 7
  • MySQL 8
  • Seata-server 1.4.2
  • Nacos 2.0.3







1. Get profile

# Pull the mirror image 
docker pull seataio/seata-server:1.4.2
# Open ports 
# seata-server
firewall-cmd --add-port=8091/tcp --zone=public --permanent
# nacos
firewall-cmd --add-port=8848/tcp --zone=public --permanent
firewall-cmd --add-port=9848/tcp --zone=public --permanent
# mysql
firewall-cmd --add-port=3306/tcp --zone=public --permanent
firewall-cmd --reload
# Run directly first seata Containers , Then copy the configuration file to the host machine , Used for subsequent mount operations .
docker run -d \
--name seata-server \
-p 8091:8091 \
seataio/seata-server:1.4.2
docker cp seata-server:/seata-server/resources /mydata/seata/seata-config
# Delete container 
docker rm -f seata-server
# Log file directory 
mkdir -p /mydata/seata/logs

2. install MySQL( Installed or connected to the database of other machines , Skippable )

# Pull the mirror image 
docker pull mysql:8.0
# Create and place on the host mysql Configuration file directory 、 Data directory and log directory , And authorize 
mkdir -p /mydata/mysql/{
data,conf,log}
chmod -R 755 /mydata/mysql/
# establish MySQL Configuration file for 
vim /mydata/mysql/conf/my.cnf
# Add the following to the configuration file created above 
[client]
# socket = /mydata/mysql/mysqld.sock
# Modify character encoding 
default-character-set = utf8mb4
[mysqld]
# pid-file = /var/run/mysqld/mysqld.pid
# socket = /var/run/mysqld/mysqld.sock
# datadir = /var/lib/mysql
# socket = /mydata/mysql/mysqld.sock
# pid-file = /mydata/mysql/mysqld.pid
datadir = /mydata/mysql/data
character_set_server = utf8mb4
collation_server = utf8mb4_bin
# Skip domain name check , solve MySQL The problem of slow connection 
skip-name-resolve
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Custom config should go here
!includedir /etc/mysql/conf.d/
# Create and start the container 
docker run \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf/my.cnf:/etc/mysql/my.conf \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
--restart=unless-stopped \
--name mysql \
-d mysql:8.0
# Into the container 
docker exec -it mysql /bin/bash
mysql -uroot -p
# Using a database 
use mysql
# Modify the access host and password , Set to be accessible to all hosts 
# See if remote access is running ; If root User host by localhost, To access remotely , It needs to be changed to %
select host,user,plugin from user;
# take host Change it to %
update user set host='%' where user ='root';
# Change the password verification method of the connection (mysql_native_password,mysql8.x Version must use this mode , otherwise navicate Unable to connect correctly )
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY ' New password ';
# Refresh the permissions 
flush privileges;


3. Create database seata, And execute the following sql Script

official mysql.sql Portal

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `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`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;


4. start-up nacos, And add namespace

docker run -it \
-e MODE=standalone \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e JVM_XMN=256m \
-p 8848:8848 \
-p 9848:9848 \
--name mynacos \
nacos/nacos-server:2.0.3

http://ip:8848/nacos
namespace



5. modify seata-server The configuration file

**

vim /mydata/seata/seata-config/file.conf
# modify file.conf file , take mode Change it to db, Then configure the database 
## transaction log store, only used in seata-server
store {

## store mode: file、db、redis
mode = "db"
## rsa decryption public key
publicKey = ""
## file store property
file {

## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## database store property
db {

## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://192.168.23.3:3306/seata?rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai"
user = "root"
password = "123456"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {

## redis mode: single、sentinel
mode = "single"
## single mode property
single {

host = "127.0.0.1"
port = "6379"
}
## sentinel mode property
sentinel {

masterName = ""
## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381"
sentinelHosts = ""
}
password = ""
database = "0"
minConn = 1
maxConn = 10
maxTotal = 100
queryLimit = 100
}
}
# modify registry.conf file , Change the registry and configuration center to the namespace created above and the corresponding nacos IP Address 
vim /mydata/seata/seata-config/registry.conf
registry {

# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {

application = "seata-server"
serverAddr = "192.168.23.3:8848"
group = "SEATA_GROUP"
namespace = "seata-naming"
cluster = "default"
username = "nacos"
password = "nacos"
}
eureka {

serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {

serverAddr = "localhost:6379"
db = 0
password = ""
cluster = "default"
timeout = 0
}
zk {

cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {

cluster = "default"
serverAddr = "127.0.0.1:8500"
aclToken = ""
}
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 = "nacos"
nacos {

serverAddr = "192.168.23.3:8848"
namespace = "seata-naming"
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
consul {

serverAddr = "127.0.0.1:8500"
aclToken = ""
}
apollo {

appId = "seata-server"
## apolloConfigService will cover apolloMeta
apolloMeta = "http://192.168.23.3:8801"
apolloConfigService = "http://192.168.23.3:8080"
namespace = "application"
apolloAccesskeySecret = ""
cluster = "seata"
}
zk {

serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
nodePath = "/seata/seata.properties"
}
etcd3 {

serverAddr = "http://localhost:2379"
}
file {

name = "file:/mydata/seata/seata-config/file.conf"
}
}


6. Set up nacos Configuration center

from v1.4.2 Version start , Supported from one Nacos dataId Get all configuration information from , You just need to add an extra dataId Configuration item .

stay nacos The new configuration , here dataId by seataServer.properties. Pay attention to the established namespace and group To work with registry.conf Consistency in the document .

official config.txt Portal

# newly build config.txt The configuration file , take store.mode=file Change it to store.mode=db , Change the database to the configuration of your own database ;
# service.default.grouplist=xxx;
# Three services , Namely storage-service、account-service and order-service
# service.vgroupMapping.storage-service-group=default
# service.vgroupMapping.order-service-group=default
# service.vgroupMapping.account-service-group=default
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
service.vgroupMapping.storage-service-group=default
service.vgroupMapping.order-service-group=default
service.vgroupMapping.account-service-group=default
service.default.grouplist=192.168.23.3:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
store.mode=db
store.publicKey=
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.23.3:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

 Insert picture description here



7. start-up seata-server

# SEATA_IP Specify external network ip Address 
docker run -it \
--name seata-server \
-p 8091:8091 \
-e SEATA_CONFIG_NAME=file:/seata-server/resources/registry \
-e SEATA_IP=192.168.23.3 \
-e SEATA_PORT=8091 \
-v /mydata/seata/seata-config:/seata-server/resources \
-v /mydata/seata/logs:/root/logs \
seataio/seata-server:1.4.2

 Insert picture description here



8. test

framework
SpringCloud Alibaba + MyBatis + OpenFeign + Nacos


edition

  • SpringBoot 2.3.7.RELEASE
  • SpringCloud Alibaba 2.2.7.RELEASE

Logic
The business logic for users to purchase goods . The whole business logic consists of 3 Micro services provide support :

  • Warehousing services : Deduct the storage quantity from the given goods .
  • Order service : Create an order based on purchase requirements .
  • Account Services : Deduct balance from user account .
     Insert picture description here



9. seata-storage-service

Module structure
 Insert picture description here

pom.xml

<dependencies>
<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>
<exclusion>
<artifactId>seata-spring-boot-starter</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</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>
<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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

application.yml

# Application service WEB Access port 
server:
port: 9031
# apply name 
spring:
application:
name: seata-storage-service
cloud:
nacos:
discovery:
server-addr: 192.168.23.3:8848
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.23.3:3306/seata_storage?useUnicode=true&serverTimezone=UTC&useSSL=false
username: root
password: 123456
seata:
enabled: true
application-id: ${
spring.application.name}
# Custom transaction group name 
# service.vgroup_mapping.${your-service-group}=default, In the middle of the ${your-service-group} The name of the service group defined for yourself 
# In the later version, it should be vgroupMapping At the same time, the following is like : order-service-group It can't be defined as order_service_group
# Need and seata-server Corresponding to in :service.vgroupMapping.order-service-group=default
tx-service-group: storage-service-group
# Configure the correspondence between the transaction group and the cluster 
service:
vgroup-mapping:
# order-service-group Is the name of the transaction Group ,default Name the cluster ( And registry.conf In the same )
storage-service-group: default
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# registry.conf in , To configure cluster name 
cluster: default
config:
type: nacos
nacos:
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# nacos Configuration center configuration dataId
data-id: seataServer.properties
logging:
level:
io:
seata: info

Entity class :Storage

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Storage implements Serializable {

private Long id;
private Long productId;
private Integer total;
private Integer used;
private Integer residue;
}

Result encapsulation class :CommonResult

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CommonResult<T> {

private Integer code;
private String message;
private T data;
public CommonResult(Integer code, String message) {

this(code, message, null);
}
}

StorageMapper

public interface StorageMapper {

/** * Deducting the inventory * @param productId goods id * @param count Number */
@Update("update t_storage set used = used + #{count}, residue = residue - #{count} where product_id = #{productId}")
void decrease(@Param("productId") Long productId, @Param("count") Integer count);
}

DataSourceProxyConfig

/** * You can also use seata-server Data source agent for */
@Configuration
public class DataSourceProxyConfig {

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {

return new DruidDataSource();
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml"));
return factoryBean.getObject();
}
}

StorageService

public interface StorageService {

/** * Deducting the inventory * @param productId goods id * @param count Number */
void decrease(Long productId, Integer count);
}

StorageServiceImpl

@Service
public class StorageServiceImpl implements StorageService {

@Resource
private StorageMapper storageDAO;
@Override
public void decrease(Long productId, Integer count) {

LOGGER.info("------->storage-service The deduction of inventory starts ");
storageDAO.decrease(productId, count);
LOGGER.info("------->storage-service After deducting the inventory from the inventory, the end ");
}
}

StorageController

@RestController
public class StorageController {

@Resource
private StorageService storageService;
@PostMapping("/storage/decrease")
public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) {

storageService.decrease(productId, count);
return new CommonResult(200, " Inventory reduction succeeded ");
}
}

Start class

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan("tech.zger.cloud.mapper")
public class Demo14SeataStorageService9031Application {

public static void main(String[] args) {

SpringApplication.run(Demo14SeataStorageService9031Application.class, args);
}
}


10. seata-account-service

Module structure
 Insert picture description here

Introduced dependencies and seata-storage-service The consistency of

application.yml

# Application service WEB Access port 
server:
port: 9032
# apply name 
spring:
application:
name: seata-account-service
cloud:
nacos:
discovery:
server-addr: 192.168.23.3:8848
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.23.3:3306/seata_account?useUnicode=true&serverTimezone=UTC&useSSL=false
username: root
password: 123456
seata:
enabled: true
application-id: ${
spring.application.name}
# Custom transaction group name 
# service.vgroup_mapping.${your-service-group}=default, In the middle of the ${your-service-group} The name of the service group defined for yourself 
# In the later version, it should be vgroupMapping At the same time, the following is like : account-service-group It can't be defined as account_service_group
# Need and seata-server Corresponding to in :service.vgroupMapping.account-service-group=default
tx-service-group: account-service-group
# Configure the correspondence between the transaction group and the cluster 
service:
vgroup-mapping:
# account-service-group Is the name of the transaction Group ,default Name the cluster ( And registry.conf In the same )
account-service-group: default
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# registry.conf in , To configure cluster name 
cluster: default
config:
type: nacos
nacos:
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# nacos Configuration center configuration dataId
data-id: seataServer.properties
logging:
level:
io:
seata: info
feign:
httpclient:
enabled: true

DataSourceProxyConfig、CommonResult Consistent with the previous one

Account

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Account implements Serializable {

private Long id;
private Long userId;
private BigDecimal total;
private BigDecimal used;
private BigDecimal residue;
}

AccountMapper

@Mapper
public interface AccountMapper {

/** * Deduct account balance * @param userId user id * @param money amount of money */
@Update("update t_account set residue = residue - #{money}, used = used + #{money} where user_id = #{userId}")
void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
}

AccountService

public interface AccountService {

/** * * Deduct account balance * @param userId user id * @param money amount of money */
void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
}

AccountServiceImpl

@Service
public class AccountServiceImpl implements AccountService {

@Resource
private AccountMapper accountDAO;
@Override
public void decrease(Long userId, BigDecimal money) {

LOGGER.info("------->account-service Start by deducting the account balance ");
accountDAO.decrease(userId, money);
LOGGER.info("------->account-service The account balance is deducted from the account to end ");
}
}

AccountController

@RestController
public class AccountController {

@Resource
private AccountService accountService;
@PostMapping("/account/decrease")
public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money")BigDecimal money) {

accountService.decrease(userId, money);
return new CommonResult(200, " Deduct account balance successfully !");
}
}

Start class

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Demo14SeataAccountService9032Application {

public static void main(String[] args) {

SpringApplication.run(Demo14SeataAccountService9032Application.class, args);
}
}


11. seata-order-service

Module structure
 Insert picture description here

Introduced dependencies and seata-storage-service The consistency of

application.yml

# Application service WEB Access port 
server:
port: 9030
# apply name 
spring:
application:
name: seata-order-service
cloud:
nacos:
discovery:
server-addr: 192.168.23.3:8848
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.23.3:3306/seata_order?useUnicode=true&serverTimezone=UTC&useSSL=false
username: root
password: 123456
seata:
enabled: true
application-id: ${
spring.application.name}
# Custom transaction group name 
# service.vgroup_mapping.${your-service-group}=default, In the middle of the ${your-service-group} The name of the service group defined for yourself 
# In the later version, it should be vgroupMapping At the same time, the following is like : order-service-group It can't be defined as order_service_group
# Need and seata-server Corresponding to in :service.vgroupMapping.order-service-group=default
tx-service-group: order-service-group
# Configure the correspondence between the transaction group and the cluster 
service:
vgroup-mapping:
# order-service-group Is the name of the transaction Group ,default Name the cluster ( And registry.conf In the same )
order-service-group: default
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# registry.conf in , To configure cluster name 
cluster: default
config:
type: nacos
nacos:
server-addr: 192.168.23.3:8848
group: SEATA_GROUP
namespace: seata-naming
username: nacos
password: nacos
# nacos Configuration center configuration dataId
data-id: seataServer.properties
logging:
level:
io:
seata: info

DataSourceProxyConfig、CommonResult Consistent with the previous one

Order

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {

private Long id;
private Long userId;
private Long productId;
private Integer count;
private BigDecimal money;
/** * The order status : * 0: In the create ; * 1: It's over */
private Integer status;
}

AccountApi

@FeignClient("seata-account-service")
public interface AccountApi {

/** * The commodity service calls the account service * @param userId user id * @param money amount of money * @return result */
@PostMapping(value = "/account/decrease")
CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money")BigDecimal money);
}

StorageApi

@FeignClient(value = "seata-storage-service")
public interface StorageApi {

/** * Call inventory service * @param productId goods id * @param count Number * @return In response to the results */
@PostMapping(value = "/storage/decrease")
CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

OrderMapper

@Repository
public interface OrderMapper {

/** * Create order * @param order Order * */
@Insert("insert into `t_order` (user_id,product_id,count,money,status) values (#{userId}, #{productId}, #{count}, #{money}, 0)")
void create(Order order);
/** * Modify order status , Change from zero to 1 * @param userId user id * @param status The order status */
@Update("update t_order set status = 1 where user_id = #{userId} and status = #{status}")
void update(@Param("userId") Long userId, @Param("status") Integer status);
}

OrderService

public interface OrderService {

/** * Create order * @param order Order */
void create(Order order);
}

OrderServiceImpl

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageApi storageApi;
@Autowired
private AccountApi accountApi;
/** * Create order -> Call inventory service to deduct inventory -> Call the account service to deduct the account balance -> Modify order status * In short : Place the order -> Inventory deduction -> Less balance -> Change state * * rollbackFor = Exception.class, Roll back as soon as an exception is encountered */
@Override
@GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
public void create(Order order) {

// 1. Create order 
log.info("-----> Start new order ");
orderMapper.create(order);
// 2. Inventory deduction 
log.info("-----> Start calling Storage, Make a deduction :{}", order.getCount());
storageApi.decrease(order.getProductId(), order.getCount());
log.info("-----> End call Storage, End of deduction ");
// 3. Less balance 
log.info("-----> Start calling Account, Make a deduction :{}", order.getMoney());
accountApi.decrease(order.getUserId(), order.getMoney());
log.info("-----> End call Account, End of deduction ");
// 4. Change state 
log.info("-----> Modify order status start ");
orderMapper.update(order.getUserId(), 0);
log.info("-----> Modify order status end ");
}
}

OrderController

@RestController
public class OrderController {

@Autowired
private OrderService orderService;
@GetMapping("/order/create")
public CommonResult create(Order order) {

orderService.create(order);
return new CommonResult(200, " Order created successfully ");
}
}

Start class

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = "tech.zger.cloud.mapper")
public class Demo14SeataOrderService9030Application {

public static void main(String[] args) {

SpringApplication.run(Demo14SeataOrderService9030Application.class, args);
}
}


12. verification

Normal visit :http://localhost:9030/order/create?userId=1&productId=1&count=10&money=100


Simulate payment timeout

modify seata-account-service Of AccountServiceImpl class :

@Service
public class AccountServiceImpl implements AccountService {

@Resource
private AccountMapper accountDAO;
@Override
public void decrease(Long userId, BigDecimal money) {

LOGGER.info("------->account-service Start by deducting the account balance ");
// Simulation timeout exception , Global transaction rollback 
// Pause the thread for a few seconds 
try {

TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {

e.printStackTrace();
}
accountDAO.decrease(userId, money);
LOGGER.info("------->account-service The account balance is deducted from the account to end ");
}
}

seata-order-service Medium OrderServiceImpl:
 Insert picture description here
There will be data consistency issues !!



add to seata Global transaction control

seata-order-service

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageApi storageApi;
@Autowired
private AccountApi accountApi;
/** * Create order -> Call inventory service to deduct inventory -> Call the account service to deduct the account balance -> Modify order status * In short : Place the order -> Inventory deduction -> Less balance -> Change state * * rollbackFor = Exception.class, Roll back as soon as an exception is encountered */
@Override
@GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
public void create(Order order) {

// 1. Create order 
log.info("-----> Start new order ");
orderMapper.create(order);
// 2. Inventory deduction 
log.info("-----> Start calling Storage, Make a deduction :{}", order.getCount());
storageApi.decrease(order.getProductId(), order.getCount());
log.info("-----> End call Storage, End of deduction ");
// 3. Less balance 
log.info("-----> Start calling Account, Make a deduction :{}", order.getMoney());
accountApi.decrease(order.getUserId(), order.getMoney());
log.info("-----> End call Account, End of deduction ");
// 4. Change state 
log.info("-----> Modify order status start ");
orderMapper.update(order.getUserId(), 0);
log.info("-----> Modify order status end ");
}
}

Observe again .( journal 、 Database changes )





版权声明:本文为[Yes ZG!]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/134/202205141439103344.html