本文共 15445 字,大约阅读时间需要 51 分钟。
1. Redisson
Redisson是Redis官方推荐的Java版的Redis客户端。它提供的功能非常多,也非常强大,此处我们只用它的分布式锁功能。
1.1. 基本用法
12 org.redisson 3redisson 43.11.1 5
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 24 4.0.0 56 11org.springframework.boot 7spring-boot-starter-parent 82.1.6.RELEASE 910 com.cjs.example 12cjs-redisson-example 130.0.1-SNAPSHOT 14cjs-redisson-example 15 1617 19 201.8 1821 69 7022 25org.springframework.boot 23spring-boot-starter-data-jpa 2426 29org.springframework.boot 27spring-boot-starter-data-redis 2830 33 34 35org.springframework.boot 31spring-boot-starter-web 3236 40 41 42org.redisson 37redisson 383.11.1 3943 47org.apache.commons 44commons-lang3 453.9 4648 52com.alibaba 49fastjson 501.2.58 5153 57 58org.apache.commons 54commons-pool2 552.6.2 5659 63mysql 60mysql-connector-java 61runtime 6264 68org.projectlombok 65lombok 66true 6771 78 7972 7773 76org.springframework.boot 74spring-boot-maven-plugin 75
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: 012 max-idle: 813 max-active: 2014 datasource:15 url: jdbc:mysql://127.0.0.1:3306/test16 username: root17 password: 12345618 driver-class-name: com.mysql.cj.jdbc.Driver19 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 ChengJianSheng11 * @date 2019-07-2612 */13 @Configuration14 public class RedissonConfig {15 16 @Bean17 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 HashOperationshashOperations = 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 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 ChengJianSheng13 * @date 2019-07-2614 */15 @Service16 public class StockServiceImpl implements StockService {17 18 @Autowired19 private StringRedisTemplate stringRedisTemplate;20 21 @Override22 public int getByProduct(Integer productId) {23 HashOperationshashOperations = 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 @Override32 public boolean decrease(Integer productId) {33 int stock = getByProduct(productId);34 if (stock <= 0) {35 return false;36 }37 HashOperations 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 ChengJianSheng19 * @date 2019-07-3020 */21 @Slf4j22 @Service23 public class OrderServiceImpl implements OrderService {24 25 @Autowired26 private StockService stockService;27 @Autowired28 private OrderRepository orderRepository;29 @Autowired30 private RedissonClient redissonClient;31 32 /**33 * 乐观锁34 */35 @Override36 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 ChengJianSheng11 * @date 2019-07-3012 */13 @Data14 @Entity15 @Table(name = "t_order")16 public class OrderModel implements Serializable {17 18 @Id19 @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_order18 -- ----------------------------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_record32 -- ----------------------------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 工程结构
1.5 Redis集群创建
1.6 测试
测试/course/upload
测试/order/create
2. Spring Integration
用法与Redisson类似
12 5org.springframework.boot 3spring-boot-starter-integration 46 org.springframework.integration 7spring-integration-redis 8
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 ChengJianSheng10 * @date 2019-07-3011 */12 @Configuration13 public class RedisLockConfig {14 15 @Bean16 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 //todo13 14 } finally {15 lock.unlock();16 }17 18 }
3. 其它
来源:
转载地址:http://faqef.baihongyu.com/