nacos+seata (seata highly available docker deployment, rancher deployment)
Description: record seata integration
1) Whole
- Parent dependency
<properties> <!--Project compilation environment--> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!--Dependent version management--> <fastjson.version>1.2.73</fastjson.version> <commons-lang3.version>3.11</commons-lang3.version> <spring-boot.version>2.2.10.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version> <mysql-connector.version>8.0.16</mysql-connector.version> <druid-spring-boot-starter.version>1.2.4</druid-spring-boot-starter.version> <mybatis-plus-boot-starter.version>3.4.1</mybatis-plus-boot-starter.version> </properties> <dependencyManagement> <dependencies> <!-- SpringBoot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- SpringCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- SpringCloud Alibaba --> <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> <!-- MySQL Database connection driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector.version}</version> </dependency> <!-- MybatisPlus Mybatis Enhanced plug-in SpringBoot integration --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus-boot-starter.version}</version> </dependency> <!-- Druid Alibaba database connection pool --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid-spring-boot-starter.version}</version> </dependency> <!-- Ali FastJson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- Apache Toolkit for processing basic data type data --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> <scope>provided</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
-
Sub dependency
<dependencies> <!-- nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <!-- Because of the compatible version,So it needs to eliminate its own seata My bag --> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <!--Introduce our own seata Dependency of corresponding version,Instead of using starter Default version--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.3.0</version> </dependency> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>3.1.0</version> </dependency> <!--web starter--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.2</version> </dependency> <!--alibaba druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!--jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>2.6.1</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--seata_account service--> <dependency> <groupId>org.example</groupId> <artifactId>seata_account</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> <!--seata_storage service--> <dependency> <groupId>org.example</groupId> <artifactId>seata_storage</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> </dependencies>
Microservice profile
Note: in case of problem 1, you need to add file Conf and registry Conf file
- bootstrap.yml
server: port: 7630 spring: application: name: account cloud: alibaba: seata: # The user-defined transaction group name needs to correspond to the name in seata server, which we previously configured in the configuration file of seata tx-service-group: order_tx_group nacos: discovery: server-addr: localhost:8860 # namespace: ffee2bbe-5ba5-4e6e-a822-6a00abe11769 datasource: # Current data source operation type type: com.alibaba.druid.pool.DruidDataSource # mysql driver class driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: 123 logging: level: io: seata: info mybatis: mapperLocations: classpath*:mapper/*.xml
- file.conf
transport { # tcp udt unix-domain-socket type = "TCP" #NIO NATIVE server = "NIO" #enable heartbeat heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" worker-thread-prefix = "NettyServerNIOWorker" server-executor-thread-prefix = "NettyServerBizHandler" share-boss-worker = false client-selector-thread-prefix = "NettyClientSelector" client-selector-thread-size = 1 client-worker-thread-prefix = "NettyClientWorkerThread" # netty boss thread size,will not be used for UDT boss-thread-size = 1 #auto default pin or 8 worker-thread-size = 8 } shutdown { # when destroy server, wait seconds wait = 3 } serialization = "seata" compressor = "none" } service { #vgroup->rgroup vgroupMapping.order_tx_group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" } client { async.commit.buffer.limit = 10000 lock { retry.internal = 10 retry.times = 30 } report.retry.count = 5 } ## transaction log store, only used in seata-server store { ## store mode: file,db,redis mode = "db" 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" url = "jdbc:mysql://172.16.1.240:33067/eplus_seata?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8" user = "root" password = "xxx" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } }
- registry.conf
registry { # file ,nacos ,eureka,redis,zk,consul,etcd3,sofa type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8860" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { # file,nacos ,apollo,zk,consul,etcd3 type = "nacos" nacos { serverAddr = "127.0.0.1:8860" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } }
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;
seata
AT mode
TC (transmission Coordinator) transaction coordinator: maintains the status of global and branch transactions and drives the submission or rollback of global transactions
TM (transmission manager) transaction manager: defines the scope of global transactions, starts global transactions, commits or rolls back global transactions
RM(Resource Manager) resource manager: manages the resources of branch transaction processing, talks with TC to register branch transactions and report the status of branch transactions, and drives branch transaction submission or rollback.
1) seata download (connection 1: seata download)
Download address: http://seata.io/zh-cn/blog/download.html
Version Description (nacos and seata versions): https://github.com/alibaba/spring-cloud-alibaba/wiki/ Version Description
2) File modification
- file.conf
db mode is selected here
## transaction log store, only used in seata-server service { #transaction service group mapping #Specifies the transaction group name for the test vgroupMapping.order_tx_group = "default" #only support when registry.type=file, please don't set multiple addresses #Specify the default group address and port, and you can set multiple addresses default.grouplist = "127.0.0.1:8091" #disable seata #Disable global transaction = false starts the service disableGlobalTransaction = false } store { ## store mode: file,db,redis mode = "db" 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" url = "jdbc:mysql://172.16.1.240:33067/eplus_seata?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8" user = "root" password = "xxxx" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } }
- registry.conf
Note: Here I use nacos. For simplicity, other items are deleted and can be retained.
registry { # file ,nacos ,eureka,redis,zk,consul,etcd3,sofa type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8860" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { # file,nacos ,apollo,zk,consul,etcd3 type = "nacos" nacos { serverAddr = "127.0.0.1:8860" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } }
- config.txt
Note: only the necessary information is reserved here. Others have been deleted. Most of them are default parameters and can be configured as needed.
service.vgroupMapping.order_tx_group=default service.default.grouplist=127.0.0.1:8091 service.enableDegrade=false service.disableGlobalTransaction=false store.mode=db store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://172.16.1.240:33067/eplus_seata?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 store.db.user=root store.db.password=xxx 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
- Seata server database
Go to github to download seata, find the sql file in the (Figure:) directory, and select the corresponding
- Register the configuration with nacos and view it
open git bash or linux Class command line, executing sh Script (note whether the script has permission to execute) nacos-config.sh -h localhost -p 8860 -g SEATA_GROUP -t f46bbdaa-f11e-414f-9530-e6a18cbf91f6 -u nacos -w nacos Parameter Description: -g SEATA_GROUP(Group name) -t f46bbdaa-f11e-414f-9530-e6a18cbf91f6 (Namespace id) Remove-t Default to public
- Launch and view
Operation effect
First, our Seata After the server is built, the database is generally used to build it. At this time, a database will be generated SeaTa There are three tables in the database: global_table,branch_table,lock_table Corresponding to global transaction, branch transaction and global lock respectively. global_table: Every time a global transaction is initiated, the of the global transaction will be recorded in the table ID. branch_table: Record the of each branch transaction ID,Information such as which database the branch transaction operates on lock_table: Used to apply for a global lock.
springcloud alibaba project integration (actual case) effect:
Steps of the first stage: 1)Start a global transaction in global_table Of global transactions generated in ID,And in branch_table Generate branch transactions in ID. 2)In each local transaction: perform pre mirroring——>Execute business SQL->Perform post mirror——>Insert rollback log. Pre image: query and execute real business SQL Previous data. Post image: Business SQL The data after execution is used to verify whether the data is modified by other transactions during rollback. Insert rollback data: in the database UNDO_LOG Insert in table SQL Relevant information before and after execution( JSON String), used for two-phase rollback. 3)Local transaction commit: SQL Implementation results and UNDO_LOG Commit the local transaction with the written data of the table. Phase II steps: 1)Rollback: if the commit of any local transaction in a phase is wrong or timeout, the global rollback will be executed. adopt XID and Branch ID Find corresponding UNDO LOG The record verifies whether the current data is consistent with the post image data. The inconsistency indicates that other transactions have modified the data again; If so, pass UNDO LOG Execute rollback statement. Commit the local transaction after the rollback log is executed. 2)Commit: when all local transactions in the first phase are successful, submit to the global transaction manager TC Returns the result of successful submission. In addition, delete the corresponding UNDO_LOG Logging.
seata parameters and concurrency problems
https://www.csdn.net/tags/NtTakg4sMTY5NzMtYmxvZwO0O0OO0O0O.html
common problem
1. Error reporting: endpoint format should like ip:port
Solution: (recommended method 2)
Method 1: add file to all microservices that register global transactions Conf and registry Conf file, the content is similar to the server, and The yml file configuration is shown in the figure below
Client port file Conf content
transport { # tcp udt unix-domain-socket type = "TCP" #NIO NATIVE server = "NIO" #enable heartbeat heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" worker-thread-prefix = "NettyServerNIOWorker" server-executor-thread-prefix = "NettyServerBizHandler" share-boss-worker = false client-selector-thread-prefix = "NettyClientSelector" client-selector-thread-size = 1 client-worker-thread-prefix = "NettyClientWorkerThread" # netty boss thread size,will not be used for UDT boss-thread-size = 1 #auto default pin or 8 worker-thread-size = 8 } shutdown { # when destroy server, wait seconds wait = 3 } serialization = "seata" compressor = "none" } service { #vgroup->rgroup vgroupMapping.eplus_tx_group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support enableDegrade = false #disable disable = false #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" } client { async.commit.buffer.limit = 10000 lock { retry.internal = 10 retry.times = 30 } report.retry.count = 5 } ## transaction log store, only used in seata-server store { ## store mode: file,db,redis mode = "db" 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" url = "jdbc:mysql://172.16.1.240:33067/eplus_seata?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8" user = "root" password = "xxxx" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } }
Client port registry Conf content
registry { # file ,nacos ,eureka,redis,zk,consul,etcd3,sofa type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { # file,nacos ,apollo,zk,consul,etcd3 type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } }
Method 2: configure all in yml file, as shown in the figure
2. The user-defined transaction group name needs to correspond to that in Seata server
tx-service-group: order_tx_group
3. The concurrent delivery thread is unsafe, dirty reading and writing may occur, and the inventory of delivered goods may become negative
1. First, let's transform the code of the official website into a code for ordering goods
@GlobalTransactional public void purchase(String userId, String commodityCode, int orderCount) { try { Product product = productService.getById(commodityCode); if (product.getStock()-orderCount > 0) { //xxxxx ordersService.save(orders); } } catch (Exception e) { // TODO: handle exception throw new RuntimeException(); } }
We can see that in this case, the thread is unsafe, dirty reading and writing will occur, and the delivered goods inventory may become negative
2. Modify the code lock to ensure thread safety
@GlobalTransactional public void purchase(String userId, String commodityCode, int orderCount) { try { lock.lock(); Product product = productService.getById(commodityCode); if (product.getStock()-orderCount > 0) { //xxxx ordersService.save(orders); } } catch (Exception e) { // TODO: handle exception throw new RuntimeException(); } finally { lock.unlock(); } }
At this time, this code can be used normally However, it is found that the transaction timeout is easy to occur after the pressure test
You can see the rollback timeout after an exception occurs
Analyze the cause
1. According to the above code, we can see that there are more descriptions on the official website, all of which are annotation situations, and less api references. Let's first analyze the code just now
Service call - > discover annotation - > create transaction - > wait for lock - > acquire lock - > business processing
It can be found that priority has been given to the creation of transactions. At this time, if the concurrency is large and the lock cannot be obtained after waiting all the time At this time, various timeout exceptions shown in the above figure will be thrown
Solution
1. Let's look at the next process: service call - > find annotation - > create transaction - > wait for lock - > obtain lock - > business processing
2. Have you found the problem? As long as we can put the operation of waiting for lock and obtaining lock before creating transaction, this problem can be solved
User request - > wait for lock - > acquire lock - > service call - > discover annotation - > create transaction - > business processing
@GetMapping(value = "purchase") public Object purchase() throws TransactionException { try { lock.lock(); return demoService.purchase(1,2,3); } finally { // TODO: handle finally clause lock.unlock(); } }
For example, the above code can be changed directly before calling, so that the lock in the service can also be removed and changed to the interface
3. What if someone says that a distributed lock is not a local lock, or that the place where I lock is in the service?
Never mind. seata also provides a set of transaction api creation methods
public void xxx(String userId, String commodityCode, int orderCount) { try { lock.lock(); Product product = xxxservic.getById(commodityCode); if (product.getStock()-orderCount > 0) { GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate(); tx.begin(300000, "test-group"); try { //Business code tx.commit(); } catch (Exception e) { // TODO: handle exception tx.rollback(); } } } finally { lock.unlock(); } }
Change to the above code and use the api to commit and rollback, so as to ensure that the transaction can be created only after the lock is robbed
Write isolation and read isolation of seata
seata single node deployment under linux+docker
-
Startup directory: (local mount)
docker run --name seata -d -p 8091:8091 -v /home/work/seata/resources:/seata-server/resources seataio/seata-server:1.3.0
-
File copy
docker cp 35116:/seata-server/resources /home/work/seata
-
View mount status
docker inspect seata
seata cluster deployment under linux+docker
-
Startup directory: (local mount)
docker run -d --restart always --name seata-server01 -p 8091:8091 -v /home/work/seata/resources:/seata-server/resources -e SEATA_IP=172.16.1.241 -e SEATA_PORT=8091 seataio/seata-server:1.3.0
docker run -d --restart always --name seata-server02 -p 8092:8091 -v /home/work/seata/resources2:/seata-server/resources -e SEATA_IP=172.16.1.241 -e SEATA_PORT=8092 seataio/seata-server:1.3.0
Seata - rancher deployment
-
New configuration
-
Create a new Seata server service and mount the configuration
-
Service RM and TM registration
Related links:
Rapid integration of spring Cloud with Seata
https://github.com/seata/seata-samples/blob/master/doc/quick-integration-with-spring-cloud.md
seata Download Center
http://seata.io/zh-cn/blog/download.html
Corresponding Version Description
https://github.com/alibaba/spring-cloud-alibaba/wiki/ Version Description
Detailed case I
Detailed case II of actual combat
https://blog.csdn.net/qq_40298902/article/details/108966561
Operation result description https://www.csdn.net/tags/MtTaEgysMTkzOTEwLWJsb2cO0O0O.html
seata parameter and concurrent dirty read and write problems
https://www.csdn.net/tags/NtTakg4sMTY5NzMtYmxvZwO0O0OO0O0O.html
seata official website
http://seata.io/zh-cn/docs/overview/what-is-seata.html
Detailed explanation of seata transaction grouping
https://blog.csdn.net/weixin_39800144/article/details/100740420
seata linux docker single node deployment
https://www.cnblogs.com/binz/p/12841125.html
Cluster deployment under seata linux docker
https://www.csdn.net/tags/NtjaQg0sMjczOTItYmxvZwO0O0OO0O0O.html