6. Microservices family - distributed transaction Seata

Posted by remlabm on Tue, 08 Feb 2022 16:02:30 +0100

Catalog Spring Cloud Alibaba microservice series articles

1, Foreword

Distributed transactions and distributed locks are two completely different things.

Distributed transaction is to ensure that A series of operations either succeed or fail. For example, transfer: A transfers 100 yuan to B, first deduct 100 yuan from A account, and then increase 100 yuan from B account. If 100 yuan has been successfully deducted from A account, but there is A server failure in the process of increasing the money in B account, resulting in no success. Here, you need to recover the money in account A (rollback). The whole transfer process must be A transaction operation.

Seata official website address: http://seata.io/zh-cn/index.html

Seata provides AT, TCC, SAGA and XA transaction modes. Here is the simplest AT mode without code intrusion.

2, Installation configuration

2.1 download

Download address: https://github.com/seata/seata/releases

2.2 create seataserver in Nacos configuration center properties

Create seataserver in Nacos configuration center Properties configuration file, and then fill in the following contents:

store.mode=db
store.publicKey=
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://localhost:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456

Mainly related to database configuration

  • Note: mysql6 Above 0 use COM mysql. cj. jdbc. Driver drive

2.3 modify conf / registry Conf configuration file

Modify conf / registry. Under the downloaded seata package The conf file is mainly used to modify the registry and configuration center. Both are nacos. Note that the configuration parameters are consistent with the previous step.

registry {
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "83e9d384-d49e-4f40-84bf-c25612883dcc"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "83e9d384-d49e-4f40-84bf-c25612883dcc"
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}

2.4 importing Sql script

Create seata database, import sql and generate global_table,branch_table,lock_table three tables.

sql file address: https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql

2.5 start Seata

After the service has been started successfully in the NACOTA bin directory, you can see that the service has been started successfully in the NACOTA bin directory.

2.6 business database creation undo_log table

undo_log is used to record rollback logs. Undo needs to be created for every database that needs distributed transactions_ Log table.

sql file address https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql

3, Using Seata in code

3.1 maven dependency

pom.xml is added as follows:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

3.2 application.properties

The following is added to the configuration file, mainly to replace test_group.

# seata related
seata.tx-service-group=test_group
seata.service.vgroup-mapping.test_group=default

3.3 add @ GlobalTransactional annotation on the method

@GlobalTransactional this annotation can implement transactions. Is it very simple

3.4 examples

The order placing operation is simulated here. First reduce the commodity inventory, and then create an order. Where the order is created, int i = 1 / 0; The simulation throws an exception and triggers a rollback.

Conditional students can add a breakpoint to the order creation. You can see that the number of goods in the database has been successfully reduced before the order is created. An exception thrown during the order creation process triggers the rollback, and the number of goods has been restored.

ConsumerServiceImpl class

package com.llh.consumer.service.impl;

import com.llh.consumer.service.ConsumerService;
import com.llh.order.api.feign.OrderApi;
import com.llh.product.api.feign.ProductApi;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


/**
 * @author Little tiger's technology blog
 */
@Service
public class ConsumerServiceImpl implements ConsumerService {
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ProductApi productApi;

    @Resource
    private OrderApi orderApi;

    @Override
    @GlobalTransactional
    public Boolean buy(Long productId, Integer number) {
        RLock lock = redissonClient.getLock("lock_key");
        // Lock
        lock.lock();

        try {
            // Reduce commodity inventory first
            boolean decreaseResult = productApi.decrease(productId, number);
            if (decreaseResult) {
                // Create an order after the goods inventory is reduced successfully
                boolean createResult = orderApi.create(productId, number);
                // Release lock
                lock.unlock();
                return createResult;
            }
        } catch (Exception e) {
            // Catch exceptions and release the lock, otherwise the thread will be blocked and subsequent requests cannot enter
            lock.unlock();
            // Throw the exception again. If it is not thrown, the transaction cannot be rolled back
            throw e;
        }

        return false;
    }
}
package com.llh.order.service.impl;


import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.llh.order.api.entity.ProductOrder;
import com.llh.order.mapper.OrderMapper;
import com.llh.order.service.OrderService;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

/**
 * @author Little tiger's technology blog
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, ProductOrder> implements OrderService {
    @Override
    public Boolean create(Long productId, Integer number) {
        ProductOrder productOrder = new ProductOrder();

        // Generate unique order id
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        long id = snowflake.nextId();
        productOrder.setId(id);
        productOrder.setProductId(productId);
        productOrder.setNumber(number);
        productOrder.setCreateTime(LocalDateTime.now());
        // Simulate throwing an exception
        int i = 1 / 0;
        return save(productOrder);
    }
}

4, Conclusion

Source address: https://github.com/tigerleeli/xiaohuge-blog/tree/master/spring-cloud-alibaba-seata

WeChat official account: brother tiger's technology blog

Topics: Spring Cloud Microservices seata