Basic usage of Redisson

1.  Redisson

Redisson is the Redis client for Java Edition officially recommended by Redis. It provides many functions and is very powerful. Here we only use its distributed lock function.

https://github.com/redisson/redisson

1.1. Basic usage

 <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.11.1</version>
 </dependency>

1.2.  Distributed locks and synchronizers

RedissonClient provides many kinds of locks, and there are many other practical methods

1.2.1.  Lock

Default, unfair lock

The most concise method

Specify the timeout period 

asynchronous

1.2.2  Fair Lock 

1.2.3  MultiLock

1.2.4  RedLock

1.3. Examples

pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.1.6.RELEASE</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
     <groupId>com.cjs.example</groupId>
     <artifactId>cjs-redisson-example</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <name>cjs-redisson-example</name>
 
     <properties>
         <java.version>1.8</java.version>
     </properties>
 
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 
         <!-- https://github.com/redisson/redisson#quick-start -->
         <dependency>
             <groupId>org.redisson</groupId>
             <artifactId>redisson</artifactId>
             <version>3.11.1</version>
         </dependency>
 
 
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <version>3.9</version>
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
             <version>1.2.58</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-pool2</artifactId>
             <version>2.6.2</version>
         </dependency>
 
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <optional>true</optional>
         </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
             </plugin>
         </plugins>
     </build>
 
 </project>

application.yml

 server:
   port: 8080
 spring:
   application:
     name: cjs-redisson-example
   redis:
     cluster:
       nodes: 10.0.29.30:6379, 10.0.29.95:6379, 10.0.29.205:6379
     lettuce:
       pool:
         min-idle: 0
         max-idle: 8
         max-active: 20
   datasource:
     url: jdbc:mysql://127.0.0.1:3306/test
     username: root
     password: 123456
     driver-class-name: com.mysql.cj.jdbc.Driver
     type: com.zaxxer.hikari.HikariDataSource

 RedissonConfig.java

 package com.cjs.example.lock.config;
 
 import org.redisson.Redisson;
 import org.redisson.api.RedissonClient;
 import org.redisson.config.Config;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 /**
  * @author ChengJianSheng
  * @date 2019-07-26
  */
 @Configuration
 public class RedissonConfig {
 
     @Bean
     public RedissonClient redissonClient() {
         Config config = new Config();
         config.useClusterServers()
                 .setScanInterval(2000)
                 .addNodeAddress("redis://10.0.29.30:6379", "redis://10.0.29.95:6379")
                 .addNodeAddress("redis://10.0.29.205:6379");
 
         RedissonClient redisson = Redisson.create(config);
 
         return redisson;
     }
 
 }

CourseServiceImpl.java 

package com.cjs.example.lock.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.cjs.example.lock.constant.RedisKeyPrefixConstant;
 import com.cjs.example.lock.model.CourseModel;
 import com.cjs.example.lock.model.CourseRecordModel;
 import com.cjs.example.lock.repository.CourseRecordRepository;
 import com.cjs.example.lock.repository.CourseRepository;
 import com.cjs.example.lock.service.CourseService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.HashOperations;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 
 import java.util.concurrent.TimeUnit;
 
 /**
  * @author ChengJianSheng
  * @date 2019-07-26
  */
 @Slf4j
 @Service
 public class CourseServiceImpl implements CourseService {
 
     @Autowired
     private CourseRepository courseRepository;
     @Autowired
     private CourseRecordRepository courseRecordRepository;
     @Autowired
     private StringRedisTemplate stringRedisTemplate;
     @Autowired
     private RedissonClient redissonClient;
 
     @Override
     public CourseModel getById(Integer courseId) {
 
         CourseModel courseModel = null;
 
         HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
 
         String value = hashOperations.get(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId));
 
         if (StringUtils.isBlank(value)) {
             String lockKey = RedisKeyPrefixConstant.LOCK_COURSE + courseId;
             RLock lock = redissonClient.getLock(lockKey);
             try {
                 boolean res = lock.tryLock(10, TimeUnit.SECONDS);
                 if (res) {
                     value = hashOperations.get(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId));
                     if (StringUtils.isBlank(value)) {
                         log.info("从数据库中读取");
                         courseModel = courseRepository.findById(courseId).orElse(null);
                         hashOperations.put(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId), JSON.toJSONString(courseModel));
                     }
                 }
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } finally {
                 lock.unlock();
             }
         } else {
             log.info("从缓存中读取");
             courseModel = JSON.parseObject(value, CourseModel.class);
         }
 
         return courseModel;
     }
 
     @Override
     public void upload(Integer userId, Integer courseId, Integer studyProcess) {
 
         HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
 
         String cacheKey = RedisKeyPrefixConstant.COURSE_PROGRESS + ":" + userId;
         String cacheValue = hashOperations.get(cacheKey, String.valueOf(courseId));
         if (StringUtils.isNotBlank(cacheValue) && studyProcess <= Integer.valueOf(cacheValue)) {
             return;
         }
 
         String lockKey = "upload:" + userId + ":" + courseId;
 
         RLock lock = redissonClient.getLock(lockKey);
 
         try {
             lock.lock(10, TimeUnit.SECONDS);
 
             cacheValue = hashOperations.get(cacheKey, String.valueOf(courseId));
             if (StringUtils.isBlank(cacheValue) || studyProcess > Integer.valueOf(cacheValue)) {
                 CourseRecordModel model = new CourseRecordModel();
                 model.setUserId(userId);
                 model.setCourseId(courseId);
                 model.setStudyProcess(studyProcess);
                 courseRecordRepository.save(model);
                 hashOperations.put(cacheKey, String.valueOf(courseId), String.valueOf(studyProcess));
             }
 
         } catch (Exception ex) {
             log.error("获取所超时!", ex);
         } finally {
             lock.unlock();
         }
 
     }
 }

StockServiceImpl.java

 package com.cjs.example.lock.service.impl;
 
 import com.cjs.example.lock.constant.RedisKeyPrefixConstant;
 import com.cjs.example.lock.service.StockService;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.HashOperations;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 
 /**
  * @author ChengJianSheng
  * @date 2019-07-26
  */
 @Service
 public class StockServiceImpl implements StockService {
 
     @Autowired
     private StringRedisTemplate stringRedisTemplate;
 
     @Override
     public int getByProduct(Integer productId) {
         HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
         String value = hashOperations.get(RedisKeyPrefixConstant.STOCK, String.valueOf(productId));
         if (StringUtils.isBlank(value)) {
             return 0;
         }
         return Integer.valueOf(value);
     }
 
     @Override
     public boolean decrease(Integer productId) {
         int stock = getByProduct(productId);
         if (stock <= 0) {
             return false;
         }
         HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
         hashOperations.put(RedisKeyPrefixConstant.STOCK, String.valueOf(productId), String.valueOf(stock - 1));
         return true;
     }
 }

OrderServiceImpl.java

 package com.cjs.example.lock.service.impl;
 
 import com.cjs.example.lock.model.OrderModel;
 import com.cjs.example.lock.repository.OrderRepository;
 import com.cjs.example.lock.service.OrderService;
 import com.cjs.example.lock.service.StockService;
 import lombok.extern.slf4j.Slf4j;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.Date;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
 /**
  * @author ChengJianSheng
  * @date 2019-07-30
  */
 @Slf4j
 @Service
 public class OrderServiceImpl implements OrderService {
 
     @Autowired
     private StockService stockService;
     @Autowired
     private OrderRepository orderRepository;
     @Autowired
     private RedissonClient redissonClient;
 
     /**
      * 乐观锁
      */
     @Override
     public String save(Integer userId, Integer productId) {
         int stock = stockService.getByProduct(productId);
         log.info("剩余库存:{}", stock);
         if (stock <= 0) {
             return null;
         }
 
         //  如果不加锁,必然超卖
 
         RLock lock = redissonClient.getLock("stock:" + productId);
 
         try {
             lock.lock(10, TimeUnit.SECONDS);
 
             String orderNo = UUID.randomUUID().toString().replace("-", "").toUpperCase();
 
             if (stockService.decrease(productId)) {
 
                 OrderModel orderModel = new OrderModel();
                 orderModel.setUserId(userId);
                 orderModel.setProductId(productId);
                 orderModel.setOrderNo(orderNo);
                 Date now = new Date();
                 orderModel.setCreateTime(now);
                 orderModel.setUpdateTime(now);
                 orderRepository.save(orderModel);
 
                 return orderNo;
             }
 
         } catch (Exception ex) {
             log.error("下单失败", ex);
         } finally {
             lock.unlock();
         }
 
         return null;
     }
 
 }

OrderModel.java

 1 
package com.cjs.example.lock.model;
  
  import lombok.Data;
 
  import javax.persistence.*;
  import java.io.Serializable;
  import java.util.Date;
  
  /**
  * @author ChengJianSheng
  * @date 2019-07-30
  */
 @Data
 @Entity
 @Table(name = "t_order")
 public class OrderModel implements Serializable {
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Integer id;
 
     @Column(name = "order_no")
     private String orderNo;
 
    @Column(name = "product_id")
     private Integer productId;
 
     @Column(name = "user_id")
    private Integer userId;

     @Column(name = "create_time")
     private Date createTime;
 
     @Column(name = "update_time")
     private Date updateTime;

Database script.sql

SET NAMES utf8mb4;
 SET FOREIGN_KEY_CHECKS = 0;
  
  -- ----------------------------
  -- Table structure for t_course
  -- ----------------------------
  DROP TABLE IF EXISTS `t_course`;
  CREATE TABLE `t_course` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `course_name` varchar(64) NOT NULL,
   `course_type` tinyint(4) NOT NULL DEFAULT '1',
   `start_time` datetime NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
 
 -- ----------------------------
 -- Table structure for t_order
 -- ----------------------------
 DROP TABLE IF EXISTS `t_order`;
 CREATE TABLE `t_order` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `order_no` varchar(256) CHARACTER SET latin1 NOT NULL,
   `user_id` int(11) NOT NULL,
   `product_id` int(11) NOT NULL,
   `create_time` datetime NOT NULL,
   `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
 
 -- ----------------------------
 -- Table structure for t_user_course_record
 -- ----------------------------
 DROP TABLE IF EXISTS `t_user_course_record`;
 CREATE TABLE `t_user_course_record` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `user_id` int(11) NOT NULL,
   `course_id` int(11) NOT NULL,
   `study_process` int(11) NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4;
 
 SET FOREIGN_KEY_CHECKS = 1; 

1.4 Engineering structure

https://github.com/chengjiansheng/cjs-redisson-example 

1.5 Redis cluster creation

1.6 Testing

Test/course/upload

Test/order/create

2.  Spring Integration

Usage is similar to Redisson

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-integration</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.integration</groupId>
     <artifactId>spring-integration-redis</artifactId>
 </dependency>
 package com.kaishustory.base.conf;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.integration.redis.util.RedisLockRegistry;
 
 /**
  * @author ChengJianSheng
  * @date 2019-07-30
  */
 @Configuration
 public class RedisLockConfig {
 
     @Bean
     public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
         return new RedisLockRegistry(redisConnectionFactory, "asdf")
     }
 
 }
 @Autowired
 private RedisLockRegistry redisLockRegistry;
 
 public void save(Integer userId) {
 
     String lockKey = "order:" + userId;
 
     Lock lock = redisLockRegistry.obtain(lockKey);
     try {
         lock.lock();
 
         //todo
 
     } finally {
         lock.unlock();
     }
 
 }

3. Other

https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers 

https://www.cnblogs.com/cjsblog/p/9831423.html 

Guess you like

Origin blog.csdn.net/mrlin6688/article/details/106074547