赞
踩
1. Redisson
Redisson是Redis官方推荐的Java版的Redis客户端。它提供的功能非常多,也非常强大,此处我们只用它的分布式锁功能。
https://github.com/redisson/redisson
1.1. 基本用法
1 <dependency> 2 <groupId>org.redisson</groupId> 3 <artifactId>redisson</artifactId> 4 <version>3.11.1</version> 5 </dependency>
1.2. Distributed locks and synchronizers
RedissonClient中提供了好多种锁,还有其它很多实用的方法
1.2.1. Lock
默认,非公平锁
最简洁的一种方法
指定超时时间
异步
1.2.2 Fair Lock
1.2.3 MultiLock
1.2.4 RedLock
1.3. 示例
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.1.6.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.cjs.example</groupId> 12 <artifactId>cjs-redisson-example</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>cjs-redisson-example</name> 15 16 <properties> 17 <java.version>1.8</java.version> 18 </properties> 19 20 <dependencies> 21 <dependency> 22 <groupId>org.springframework.boot</groupId> 23 <artifactId>spring-boot-starter-data-jpa</artifactId> 24 </dependency> 25 <dependency> 26 <groupId>org.springframework.boot</groupId> 27 <artifactId>spring-boot-starter-data-redis</artifactId> 28 </dependency> 29 <dependency> 30 <groupId>org.springframework.boot</groupId> 31 <artifactId>spring-boot-starter-web</artifactId> 32 </dependency> 33 34 <!-- https://github.com/redisson/redisson#quick-start --> 35 <dependency> 36 <groupId>org.redisson</groupId> 37 <artifactId>redisson</artifactId> 38 <version>3.11.1</version> 39 </dependency> 40 41 42 <dependency> 43 <groupId>org.apache.commons</groupId> 44 <artifactId>commons-lang3</artifactId> 45 <version>3.9</version> 46 </dependency> 47 <dependency> 48 <groupId>com.alibaba</groupId> 49 <artifactId>fastjson</artifactId> 50 <version>1.2.58</version> 51 </dependency> 52 <dependency> 53 <groupId>org.apache.commons</groupId> 54 <artifactId>commons-pool2</artifactId> 55 <version>2.6.2</version> 56 </dependency> 57 58 <dependency> 59 <groupId>mysql</groupId> 60 <artifactId>mysql-connector-java</artifactId> 61 <scope>runtime</scope> 62 </dependency> 63 <dependency> 64 <groupId>org.projectlombok</groupId> 65 <artifactId>lombok</artifactId> 66 <optional>true</optional> 67 </dependency> 68 </dependencies> 69 70 <build> 71 <plugins> 72 <plugin> 73 <groupId>org.springframework.boot</groupId> 74 <artifactId>spring-boot-maven-plugin</artifactId> 75 </plugin> 76 </plugins> 77 </build> 78 79 </project>
application.yml
1 server: 2 port: 8080 3 spring: 4 application: 5 name: cjs-redisson-example 6 redis: 7 cluster: 8 nodes: 10.0.29.30:6379, 10.0.29.95:6379, 10.0.29.205:6379 9 lettuce: 10 pool: 11 min-idle: 0 12 max-idle: 8 13 max-active: 20 14 datasource: 15 url: jdbc:mysql://127.0.0.1:3306/test 16 username: root 17 password: 123456 18 driver-class-name: com.mysql.cj.jdbc.Driver 19 type: com.zaxxer.hikari.HikariDataSource
RedissonConfig.java
1 package com.cjs.example.lock.config; 2 3 import org.redisson.Redisson; 4 import org.redisson.api.RedissonClient; 5 import org.redisson.config.Config; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 9 /** 10 * @author ChengJianSheng 11 * @date 2019-07-26 12 */ 13 @Configuration 14 public class RedissonConfig { 15 16 @Bean 17 public RedissonClient redissonClient() { 18 Config config = new Config(); 19 config.useClusterServers() 20 .setScanInterval(2000) 21 .addNodeAddress("redis://10.0.29.30:6379", "redis://10.0.29.95:6379") 22 .addNodeAddress("redis://10.0.29.205:6379"); 23 24 RedissonClient redisson = Redisson.create(config); 25 26 return redisson; 27 } 28 29 }
CourseServiceImpl.java
1 package com.cjs.example.lock.service.impl; 2 3 import com.alibaba.fastjson.JSON; 4 import com.cjs.example.lock.constant.RedisKeyPrefixConstant; 5 import com.cjs.example.lock.model.CourseModel; 6 import com.cjs.example.lock.model.CourseRecordModel; 7 import com.cjs.example.lock.repository.CourseRecordRepository; 8 import com.cjs.example.lock.repository.CourseRepository; 9 import com.cjs.example.lock.service.CourseService; 10 import lombok.extern.slf4j.Slf4j; 11 import org.apache.commons.lang3.StringUtils; 12 import org.redisson.api.RLock; 13 import org.redisson.api.RedissonClient; 14 import org.springframework.beans.factory.annotation.Autowired; 15 import org.springframework.data.redis.core.HashOperations; 16 import org.springframework.data.redis.core.StringRedisTemplate; 17 import org.springframework.stereotype.Service; 18 19 import java.util.concurrent.TimeUnit; 20 21 /** 22 * @author ChengJianSheng 23 * @date 2019-07-26 24 */ 25 @Slf4j 26 @Service 27 public class CourseServiceImpl implements CourseService { 28 29 @Autowired 30 private CourseRepository courseRepository; 31 @Autowired 32 private CourseRecordRepository courseRecordRepository; 33 @Autowired 34 private StringRedisTemplate stringRedisTemplate; 35 @Autowired 36 private RedissonClient redissonClient; 37 38 @Override 39 public CourseModel getById(Integer courseId) { 40 41 CourseModel courseModel = null; 42 43 HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash(); 44 45 String value = hashOperations.get(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId)); 46 47 if (StringUtils.isBlank(value)) { 48 String lockKey = RedisKeyPrefixConstant.LOCK_COURSE + courseId; 49 RLock lock = redissonClient.getLock(lockKey); 50 try { 51 boolean res = lock.tryLock(10, TimeUnit.SECONDS); 52 if (res) { 53 value = hashOperations.get(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId)); 54 if (StringUtils.isBlank(value)) { 55 log.info("从数据库中读取"); 56 courseModel = courseRepository.findById(courseId).orElse(null); 57 hashOperations.put(RedisKeyPrefixConstant.COURSE, String.valueOf(courseId), JSON.toJSONString(courseModel)); 58 } 59 } 60 } catch (InterruptedException e) { 61 e.printStackTrace(); 62 } finally { 63 lock.unlock(); 64 } 65 } else { 66 log.info("从缓存中读取"); 67 courseModel = JSON.parseObject(value, CourseModel.class); 68 } 69 70 return courseModel; 71 } 72 73 @Override 74 public void upload(Integer userId, Integer courseId, Integer studyProcess) { 75 76 HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash(); 77 78 String cacheKey = RedisKeyPrefixConstant.COURSE_PROGRESS + ":" + userId; 79 String cacheValue = hashOperations.get(cacheKey, String.valueOf(courseId)); 80 if (StringUtils.isNotBlank(cacheValue) && studyProcess <= Integer.valueOf(cacheValue)) { 81 return; 82 } 83 84 String lockKey = "upload:" + userId + ":" + courseId; 85 86 RLock lock = redissonClient.getLock(lockKey); 87 88 try { 89 lock.lock(10, TimeUnit.SECONDS); 90 91 cacheValue = hashOperations.get(cacheKey, String.valueOf(courseId)); 92 if (StringUtils.isBlank(cacheValue) || studyProcess > Integer.valueOf(cacheValue)) { 93 CourseRecordModel model = new CourseRecordModel(); 94 model.setUserId(userId); 95 model.setCourseId(courseId); 96 model.setStudyProcess(studyProcess); 97 courseRecordRepository.save(model); 98 hashOperations.put(cacheKey, String.valueOf(courseId), String.valueOf(studyProcess)); 99 } 100 101 } catch (Exception ex) { 102 log.error("获取所超时!", ex); 103 } finally { 104 lock.unlock(); 105 } 106 107 } 108 }
StockServiceImpl.java
1 package com.cjs.example.lock.service.impl; 2 3 import com.cjs.example.lock.constant.RedisKeyPrefixConstant; 4 import com.cjs.example.lock.service.StockService; 5 import org.apache.commons.lang3.StringUtils; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.data.redis.core.HashOperations; 8 import org.springframework.data.redis.core.StringRedisTemplate; 9 import org.springframework.stereotype.Service; 10 11 /** 12 * @author ChengJianSheng 13 * @date 2019-07-26 14 */ 15 @Service 16 public class StockServiceImpl implements StockService { 17 18 @Autowired 19 private StringRedisTemplate stringRedisTemplate; 20 21 @Override 22 public int getByProduct(Integer productId) { 23 HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash(); 24 String value = hashOperations.get(RedisKeyPrefixConstant.STOCK, String.valueOf(productId)); 25 if (StringUtils.isBlank(value)) { 26 return 0; 27 } 28 return Integer.valueOf(value); 29 } 30 31 @Override 32 public boolean decrease(Integer productId) { 33 int stock = getByProduct(productId); 34 if (stock <= 0) { 35 return false; 36 } 37 HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash(); 38 hashOperations.put(RedisKeyPrefixConstant.STOCK, String.valueOf(productId), String.valueOf(stock - 1)); 39 return true; 40 } 41 }
OrderServiceImpl.java
1 package com.cjs.example.lock.service.impl; 2 3 import com.cjs.example.lock.model.OrderModel; 4 import com.cjs.example.lock.repository.OrderRepository; 5 import com.cjs.example.lock.service.OrderService; 6 import com.cjs.example.lock.service.StockService; 7 import lombok.extern.slf4j.Slf4j; 8 import org.redisson.api.RLock; 9 import org.redisson.api.RedissonClient; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Service; 12 13 import java.util.Date; 14 import java.util.UUID; 15 import java.util.concurrent.TimeUnit; 16 17 /** 18 * @author ChengJianSheng 19 * @date 2019-07-30 20 */ 21 @Slf4j 22 @Service 23 public class OrderServiceImpl implements OrderService { 24 25 @Autowired 26 private StockService stockService; 27 @Autowired 28 private OrderRepository orderRepository; 29 @Autowired 30 private RedissonClient redissonClient; 31 32 /** 33 * 乐观锁 34 */ 35 @Override 36 public String save(Integer userId, Integer productId) { 37 int stock = stockService.getByProduct(productId); 38 log.info("剩余库存:{}", stock); 39 if (stock <= 0) { 40 return null; 41 } 42 43 // 如果不加锁,必然超卖 44 45 RLock lock = redissonClient.getLock("stock:" + productId); 46 47 try { 48 lock.lock(10, TimeUnit.SECONDS); 49 50 String orderNo = UUID.randomUUID().toString().replace("-", "").toUpperCase(); 51 52 if (stockService.decrease(productId)) { 53 54 OrderModel orderModel = new OrderModel(); 55 orderModel.setUserId(userId); 56 orderModel.setProductId(productId); 57 orderModel.setOrderNo(orderNo); 58 Date now = new Date(); 59 orderModel.setCreateTime(now); 60 orderModel.setUpdateTime(now); 61 orderRepository.save(orderModel); 62 63 return orderNo; 64 } 65 66 } catch (Exception ex) { 67 log.error("下单失败", ex); 68 } finally { 69 lock.unlock(); 70 } 71 72 return null; 73 } 74 75 }
OrderModel.java
1 package com.cjs.example.lock.model; 2 3 import lombok.Data; 4 5 import javax.persistence.*; 6 import java.io.Serializable; 7 import java.util.Date; 8 9 /** 10 * @author ChengJianSheng 11 * @date 2019-07-30 12 */ 13 @Data 14 @Entity 15 @Table(name = "t_order") 16 public class OrderModel implements Serializable { 17 18 @Id 19 @GeneratedValue(strategy = GenerationType.IDENTITY) 20 private Integer id; 21 22 @Column(name = "order_no") 23 private String orderNo; 24 25 @Column(name = "product_id") 26 private Integer productId; 27 28 @Column(name = "user_id") 29 private Integer userId; 30 31 @Column(name = "create_time") 32 private Date createTime; 33 34 @Column(name = "update_time") 35 private Date updateTime; 36 }
数据库脚本.sql
1 SET NAMES utf8mb4; 2 SET FOREIGN_KEY_CHECKS = 0; 3 4 -- ---------------------------- 5 -- Table structure for t_course 6 -- ---------------------------- 7 DROP TABLE IF EXISTS `t_course`; 8 CREATE TABLE `t_course` ( 9 `id` int(11) NOT NULL AUTO_INCREMENT, 10 `course_name` varchar(64) NOT NULL, 11 `course_type` tinyint(4) NOT NULL DEFAULT '1', 12 `start_time` datetime NOT NULL, 13 PRIMARY KEY (`id`) 14 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; 15 16 -- ---------------------------- 17 -- Table structure for t_order 18 -- ---------------------------- 19 DROP TABLE IF EXISTS `t_order`; 20 CREATE TABLE `t_order` ( 21 `id` int(11) NOT NULL AUTO_INCREMENT, 22 `order_no` varchar(256) CHARACTER SET latin1 NOT NULL, 23 `user_id` int(11) NOT NULL, 24 `product_id` int(11) NOT NULL, 25 `create_time` datetime NOT NULL, 26 `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, 27 PRIMARY KEY (`id`) 28 ) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; 29 30 -- ---------------------------- 31 -- Table structure for t_user_course_record 32 -- ---------------------------- 33 DROP TABLE IF EXISTS `t_user_course_record`; 34 CREATE TABLE `t_user_course_record` ( 35 `id` int(11) NOT NULL AUTO_INCREMENT, 36 `user_id` int(11) NOT NULL, 37 `course_id` int(11) NOT NULL, 38 `study_process` int(11) NOT NULL, 39 PRIMARY KEY (`id`) 40 ) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4; 41 42 SET FOREIGN_KEY_CHECKS = 1;
1.4 工程结构
https://github.com/chengjiansheng/cjs-redisson-example
1.5 Redis集群创建
1.6 测试
测试/course/upload
测试/order/create
2. Spring Integration
用法与Redisson类似
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-integration</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.integration</groupId> 7 <artifactId>spring-integration-redis</artifactId> 8 </dependency>
1 package com.kaishustory.base.conf; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.data.redis.connection.RedisConnectionFactory; 6 import org.springframework.integration.redis.util.RedisLockRegistry; 7 8 /** 9 * @author ChengJianSheng 10 * @date 2019-07-30 11 */ 12 @Configuration 13 public class RedisLockConfig { 14 15 @Bean 16 public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { 17 return new RedisLockRegistry(redisConnectionFactory, "asdf") 18 } 19 20 }
1 @Autowired 2 private RedisLockRegistry redisLockRegistry; 3 4 public void save(Integer userId) { 5 6 String lockKey = "order:" + userId; 7 8 Lock lock = redisLockRegistry.obtain(lockKey); 9 try { 10 lock.lock(); 11 12 //todo 13 14 } finally { 15 lock.unlock(); 16 } 17 18 }
3. 其它
https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。