Distributed lock generally has three implementation methods: 1. Database optimistic lock; 2. 2. Distributed lock based on Redis; 3. Distributed lock based on ZooKeeper

This blog introduces the second, redis-based distributed lock, which is actually implemented using Redisson, a powerful framework.

Code example: gitee.com/zhang-xiao-…

Code structure preview

1: Create a basic springBoot project and write some basic interfaces

The basic POM dependencies required

<! <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <! Java Web interface --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! <dependency> <groupId>org.projectlombok</groupId> <artifactId> Lombok </artifactId> <optional>true</optional> </dependency> <! <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> The < version > 3.5.0 < / version > < / dependency >Copy the code

Application. Properties file configuration (note that ports 8081 and 8082 can be configured for multi-instance startup, which simulates peptide Tomcat server. The multi-instance startup mode of IDEA is shown below)

server.port=8082
spring.redis.url=redis://localhost:6379
Copy the code
Write a RedissonConfig config class to configure your RedissonCopy the code
package com.zhang.zxx.redis.lock.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; / * * * RedissonAutoConfiguration: write a RedissonConfig configuration class to configure your redisson * * @ author zhangxiaoxiang * @ date 2020/09/10 * / @Configuration public class RedissonAutoConfiguration { @Value("${spring.redis.url}") private String addressUrl; @Bean public RedissonClient getRedisson() { Config config = new Config(); config.useSingleServer() .setAddress(addressUrl) .setReconnectionTimeout(10000) .setRetryInterval(5000) .setTimeout(10000) .setConnectTimeout(10000); return Redisson.create(config); }}Copy the code

Write the basic interface (annotated is optimized, using distributed locks, to demonstrate the effect, first annotated, then compared)

package com.zhang.zxx.redis.lock.controller; import com.zhang.zxx.redis.lock.config.RedissonAutoConfiguration; import org.redisson.api.RLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Objects; /** * RedisLockController: Test distributed lock ** @author zhangxiaoxiang * @date 2020/09/10 */ @restController @RequestMapping("/order") public class RedisLockController { @Autowired private StringRedisTemplate redisTemplate; @Autowired private RedissonAutoConfiguration redissonAutoConfiguration; @Value("${server.port:}") private String port; * @return */ @requestMapping ("/ setProdnum ") public String addProdNum() { redisTemplate.opsForValue().set("stock","50"); System. The out. Println (" cache number is: "+ redisTemplate. OpsForValue () get (" stock")); return redisTemplate.opsForValue().get("stock")+""; ** @requestMapping ("/ getCommodity ") public String getCommodity() {return requestCommodity ("/ getCommodity ") public String getCommodity() { // Key String lockKey = "prod_lock"; RequireNonNull; // Check whether the specified object reference is not empty objects.requirenonNULL Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get("stock"))); if (stock>0){ int realStock=stock-1; redisTemplate.opsForValue().set("stock",realStock+""); System.out.println(" + port+", inventory remaining ===>"+realStock); +realStock; }else {system.err. Println (" failed to subtract, stock is low "); Return "deduction failed, stock is low "; }} / / / / / * * * seconds kill goods, inventory reduction interface, @requestMapping ("/ getCommodity ") // public String getCommodity() {// String lockKey  = "prod_lock"; // RLock rLock = redissonAutoConfiguration.getRedisson().getLock(lockKey); // try { // rLock.lock(); // int stock = Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get("stock"))); // if (stock>0){ // int realStock=stock-1; // redisTemplate.opsForValue().set("stock",realStock+""); // system.out. println(" + port+"); // system.out. println(" + port+"); // return "+realStock; //}else {// system.err. Println (); // return "deduction failed, inventory is insufficient "; / / / /}} finally {/ / / / note that releases the lock / / rLock unlock (); //} //Copy the code

At this point, two instances can be started, both interfaces can be accessed, and inventory reduction can be achieved, as shown below

HTTP: / / 127.0.0.1:8081 / order/getcommodity

http://127.0.0.1:8082/order/getcommodity

 

2: Configure NGINX proxy (simulate simultaneous access of multiple Tomcat servers) and Jmeter pressure test

Add the download address (nginx.org/download/ng…) to the NGINX configuration file in the last curly braces. This is the Windows version

# add custom configuration, pay attention to is a big parentheses at the end of the configuration file -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # you can specify the domain name or project code for the service Upstream upstream_name {server 127.0.0.1:8081; Server 127.0.0.1:8082; } server { listen 8080; server_name localhost; location / { proxy_pass http://upstream_name; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }} # add custom configuration, the attention is at the end of the configuration files within a curly braces -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # # visit http://127.0.0.1:8080/order/getcommodity when can achieve access to http://127.0.0.1:8081/order/getcommodity # http://127.0.0.1:8082/order/getcommodity #, the purpose of this a few have no configuration weights, using the default policy (load balancing - polling)Copy the code

The way to start NGINX is

To check whether the startup is successful, visit http://localhost. The default page of NGINX after normal startup is shown in the figure.

Configure Jmeter, configure simulated concurrency and access interface, as shown in the following two pictures (those that are not familiar with configuration can be used by Baidu Jmeter)

The following red box is http://127.0.0.1:8080/order/getcommodity Split up, the purpose is to distribute NGINX proxy requests to the HTTP: : / / 127.0.0.1:8081 / order/getcommodity and HTTP: / / 127.0.0.1:8082 / order/getcommodity

3: Start the project, parallel two demo projects 8081 and 8082

Comparison results (obviously messed up, data concurrent access exception)

At this point, restore the annotated interface of the control layer, annotate the general interface getCommodity, and compare again (note that modify redis inventory try again).

Restart the project and compare the results

At this point, the correct data has been fully rendered. Done.

If use, is very similar to already (you can refer to my another blog zhangxiaoxiang.blog.csdn.net/article/det…).