14. Redisson distributed lock

Spring Cloud microservices series of articles, click above to collect↑

1. Beginning

In a single application, we can use Java’s synchronized or lock to use locks, but in a microservice scenario, an application will deploy multiple instances, which requires To ensure that multiple threads of multiple instances can only have one thread operating resources at the same time, distributed locks are required.

The basic principle of Redisson distributed lock is implemented through Redis’s setnx command. When a process needs to acquire a lock, it creates a key in Redis to represent the name of the lock by calling Redis’s setnx command. If this is successfully created
key means that the lock is acquired successfully and the corresponding business logic can be executed. If the creation of key fails, it means that the lock has been acquired by other processes, and the current process needs to wait until it acquires the lock. After the business logic is executed, the lock needs to be released, that is, the key of the lock needs to be deleted through the del command of Redis so that other processes can obtain it. locked.

To give a common example: when you go to the toilet, push the door to see if there is anyone inside (try to get the lock). If there is someone inside, you need to wait at the door (wait for the lock). When he finishes using the toilet, the door will be opened (release the lock). , you closed the door (locked it) when you entered, and opened the door (released the lock) after taking off your pants, defecating, and lifting your pants.

In this article, we will demonstrate it through the example of snapping up Moutai.

There are three steps to buying Moutai:

  1. Determine whether inventory is sufficient
  2. Create new order
  3. Deduct inventory.

2. Install and run Redis

Redisson is based on Redis. We need to download and run Redis first.

Redis is an in-memory database, Redisson = Redis + son (son).

2.1 Windows

  • Github download address: https://github.com/redis-windows/redis-windows/releases

  • Github download is very slow, network disk download (recommended):
    “Redis-7.0.8-Windows-x64.zip” comes from UC network disk sharing
    https://drive.uc.cn/s/4087d341fd084

Start Redis

# Start service
redis-server.exe redis.conf

# Configuration file name of the old version
redis-server.exe redis.windows.conf
  • You need to specify the redis.conf configuration file. You can set the port, password, etc. in the redis.conf file.

Enter the Redis command line interface

# redis command line interface
redis-cli.exe

# Set key-value pairs
set mykey hello
# Get value
get mykey

2.2 Linux

Taking CentOS as an example, install it through the yum command.

# Installation
sudo yum install redis

# start up
sudo systemctl start redis

# Enter the redis command line interface
redis-cli
# Set key-value pairs
set mykey hello
# Get value
get mykey

3. Business SQL script

Execute the following SQL script

  • Generate product table product
  • Generate product order table product_order
  • Insert a record into the product table. The total quantity of Kweichow Moutai is 100 bottles.
CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'primary key id',
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT 'product name',
  `number` int(11) NOT NULL DEFAULT 0 COMMENT 'item quantity',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `product_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'primary key id',
  `product_id` bigint(20) DEFAULT NULL COMMENT 'product id',
  `number` int(11) DEFAULT NULL COMMENT 'number',
  `create_time` datetime DEFAULT NULL COMMENT 'Creation time',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `product` VALUES (1, 'Kweichow Moutai', 100);

4. SpringBoot integration Redisson

4.1 pom.xml

Add redisson-spring-boot-starter package dependency

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.8</version>
</dependency>

4.2 application.properties

redis related configuration

# redis related
spring.redis.host=localhost
spring.redis.port=6379

4.3 buy()

How to purchase goods

  • Determine whether the inventory quantity is sufficient
  • Create Order
  • Deduct inventory quantity
@Resource
private RedissonClient redissonClient;

@Override
public Boolean buy(Long productId, Integer number) {<!-- -->
    RLock lock = redissonClient.getLock("lock_key_" + productId);

    try {<!-- -->
        boolean lockRes = lock.tryLock(5, TimeUnit.SECONDS);
        if (!lockRes) {<!-- -->
            throw new RuntimeException("Failed to acquire lock~");
        }

        Product product = productService.getById(productId);
        log.info("Inventory quantity: {}", product.getNumber());
        // ① Determine whether the inventory is sufficient
        if (product.getNumber() < number) {<!-- -->
            throw new RuntimeException("Insufficient stock");
        }

        // ②Create order
        ProductOrder order = new ProductOrder();
        order.setProductId(productId);
        order.setNumber(1);
        order.setCreateTime(LocalDateTime.now());
        save(order);
        log.info("Create order: {}", order);

        // ③ Reduce inventory
        product.setNumber(product.getNumber() - 1);
        productService.updateById(product);
        log.info("Inventory reduction: {}", product);
    } catch (InterruptedException e) {<!-- -->
        throw new RuntimeException("An exception occurred~");
    } finally {<!-- -->
        // release lock
        lock.unlock();
    }

    return true;
}
  • "lock_key_" + productIdThe product ID is locked here. Only one request thread can operate this product at the same time, and other request threads must wait.
  • Locking the product ID indicates that each product is independently locked, which is equivalent to closing the door of that pit when you go to the toilet instead of closing the bathroom door.
  • lock.tryLock(5, TimeUnit.SECONDS), here set the locking time to 5 seconds. If the current requesting thread has not completed the operation within 5 seconds, the lock will be automatically released and the next thread will come. Perform operations.

4.4 Analysis of locking and non-locking

What problems will occur if there is no lock?

Assume that there is only one bottle of Moutai left in stock, and User A and User B initiate a request to purchase one bottle of Moutai at the same time. User A’s request thread determines that the inventory is sufficient, but has not yet completed the order creation and inventory reduction operations (the operation requires access to the database, which is relatively time-consuming). hour). At this time, user B’s request thread determines that the inventory quantity is 1 and the inventory is sufficient. It also enters the order creation and inventory reduction operations. Finally, two orders are created and the inventory is reduced twice.

After we lock the product, when user A’s request thread performs the purchase operations of determining inventory, creating orders, and reducing inventory, user B’s request thread needs to wait for user A to complete this series of operations and release the lock before proceeding. implement.

5. Test

Interface address: http://localhost:8100/product/buy

Use JMeter to create 10 threads to simulate multiple users’ simultaneous cyclic request interfaces. When no locks are applied, the following are the results of the real test: 100 bottles of Moutai generated 204 orders.

When locked, 100 orders for 100 bottles of Moutai will be generated normally.

For the use of JMeter, you can read the previous article Sentinel Flow Control and Interface Protection.

6. Conclusion

Here we use a simple example of buying Moutai to demonstrate locks. Redisson distributed locks can be used for single entities or microservices. They are locked with the help of redis middleware. As shown above, we can create multiple microservice instances and then call the interface results. Same thing.

Remember: During limited-time sales events, a large number of users request to purchase the same product through the network at the same time, which may cause concurrent sales in the system, leading to problems such as sold out or oversold products. In this case, locks must be used, whether it is a single application or a microservice.

The complete code of the Spring Cloud microservice series is in the sourcecode/spring-cloud-demo directory of the warehouse.

gitee (recommended): https://gitee.com/cunzaizhe/xiaohuge-blog

github: https://github.com/tigerleeli/xiaohuge-blog

Follow the WeChat public account: “Brother Xiaohu’s Technology Blog”, let us become better programmers together!