SpringCloud电商秒杀微服务-Redisson分布式锁方案

一、引言:

前两篇文章,介绍了:

《学会Zookeeper分布式锁--让面试官对你刮目相看》

《SpringBoot电商项目实战-Curator分布式锁实现》

细心的读者可能发现,号主在分布式锁这条路上“越走越黑”,已经发表了好几篇相关文章了,莫慌,再学完本章,你就可以在分布式锁场景下拿着倚天剑与屠龙刀叱咤分风云了

因为实际项目可能面临各种业务场景需求,需要不同的分布式锁方案,而这些文章并不是类似网上很多的教你ctrl c  + ctrl +v,关键掌握真谛。还不会的建议先去看下第一篇哦,从实际高并发场景深入浅出,层层剖析...

1、分布式锁思路分析

锁特点:

  • 排他性:同一时间,只有一个线程能获得;

  • 阻塞性:其它未抢到的线程阻塞等待,直到锁被释放,再继续抢;

  • 可重入性:线程获得锁后,后续是否可重复获取该锁(避免死锁)。

当然,还要考虑性能开销等问题。

2、常规的分布式锁解决方案有哪几种:

  • 文件系统:同一个目录下,不能存在同名文件

  • 数据库锁:主键 、 唯一约束  、for  update

  • 基于Redis的分布式锁:setnx、set、Redisson

  • 基于ZooKeeper的分布式锁:类似文件系统

直接进入今天正题,就不多介绍分布式锁的背景、区别之类了,可以参考前两篇文章哦。


二、redis分布式锁简介

1、基于redis分布式锁

(1)基本锁

  • 原理:利用redis的setnx,如果不存在某个key则设置值,设置成功则表示取得锁成功。

  • 缺点:如果获取锁后的进程在没有执行完就挂了,则锁永远不会释放,产生死锁。

(2)改进型

  • 改进:在基本锁上setnx后设置expire,保证超时后也能自动释放锁。

  • 缺点:setnx与expire不是一个原子操作,可能执行完setnx,还没来得及执行expire设置过期时间,该进程就挂了。

(3)增强版

  • 改进:利用Lua脚本,将setnx与expire变成一个原子操作,可解决一部分问题。

  • 缺点:还是锁过期问题。

注意: Redis 从2.6.12版本开始 set 命令支持 NX 、 PX 这些参数来达到 setnx 、 setex 、 psetex 命令的效果。

2、Redisson简介

所以直接使用redis自己实现分布式锁,相对麻烦,还存在上面的问题。接下来主要分析Redisson的分布式锁的实现,一般提及到Redis的分布式锁我们更多的使用的是Redisson的分布式锁,Redis的官方也是建议我们这样去做的。Redisson点我可以直接跳转到Redisson的官方文档。


注:redisson 更新注意点

  • 2018-02-26更新:增加tryLock方法,建议后面去掉DistributedLocker接口和其实现类,直接在RedissLockUtil中注入RedissonClient实现类(简单但会丢失接口带来的灵活性)。

  • 2019-07-11更新:redisson官方发布了 redisson-spring-boot-starter

本文将引入最新的redisson和springboot的集成包:redisson-spring-boot-starter,网上的很多教程可能还是使用redisson老版本,互相copy。redisson介绍可参考官网,有中英文wiki 。

 

参考官网:

https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter#spring-boot-starter


注:本示例为方便演示,仅提供如何实现redisson分布式锁方案,库存直接读取数据库,自己可以改造放到redis等缓存中,这个不会的自己去脑补。

三、项目实战

1、添加依赖:

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

2、application.yml配置

3、redissson.yml 配置

4、新建一个redisson配置,读取配置文件redisson.yml,并将RedissonClient注册到spring ioc容器

@Configurationpublic class RedissonConfig {    @Bean(destroyMethod = "shutdown")    public RedissonClient redisson() throws IOException {        // 1. 创建Config对象,读取配置属性        // 2. 创建Redisson对象,传入Config对象        RedissonClient redisson = Redisson.create(Config.fromYAML(                    new ClassPathResource("redisson.yml").getInputStream()));        return redisson;    }}

5、新建一个商品服务实现类

@Servicepublic class ProductServiceImpl implements IProductService {    @Autowired    private ProductDao productDao;    @Autowired    private RedissonClient redissonClient;    /**     * 秒杀商品     * @param productId 商品ID     * @param number 数量     * @return     */    @Override    public Boolean seckillProduct(Long productId, Integer number) {        String key = "seckill_stock_lock_" + productId;        RLock lock = redissonClient.getLock(key);        try {            //获取分布式锁            lock.lock();            Product product = productDao.selectById(productId);            if ( product!=null && product.getStocks() == 0) {                return false;            }            Integer stocks = product.getStocks() - 1;            productDao.updateStocksById(productId,stocks);        } catch (Exception e) {            e.printStackTrace();        } finally {            lock.unlock();        }        return true;    }}

6、新建一个controller,并提供一个http接口:秒杀商品

@RestController@RequestMapping("/product")public class ProductController {    @Autowired    private IProductService productService;    /**     * 秒杀商品测试     * @return     */    @GetMapping("seckillTest")    public String seckillProductTest() {        Boolean flag = productService.seckillProduct(1L, 1);        if(flag ==true){            return "创建订单成功";        } else{            return "库存不足";        }    }}

7、数据库product商品表数据如下,id=1的库存为30个,如下:

8、使用并发测试工具类,如jmeter ,新建一个测试任务,并发线程设为50或其他,模拟多人同时并发秒杀某个商品,启动项目,然后测试:

9、全部执行成功后,查看数据库中数据,库存已经为0,没有出现负数,超卖的情况。

完结,分布式锁专题终于告一段落,你学会了吗

注:本示例为方便演示,仅提供如何实现redisson分布式锁方案,库存直接读取数据库,自己可以改造放到redis等缓存中,这个不会的自己去脑补。

SpringBoot电商项目实战-Curator分布式锁实现

学会Zookeeper分布式锁,让面试官对你刮目相看

SpringBoot+Token实现接口幂等性|防止表单重复提交

一只 有深度 有灵魂 的公众号0.0  "阿甘正专"

来都来了,点个在看再走吧~~~

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值