文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot之使用Redis实现分布式锁(秒杀系统)

2024-04-02 19:55

关注

一、Redis分布式锁概念篇

建议直接采用Redis的官方推荐的Redisson作为redis的分布式锁

1.1、为什么要使用分布式锁

    我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug!

    注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间!

    后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均衡,大致如下图:

    上图可以看到,变量A存在JVM1、JVM2、JVM3三个JVM内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象,例如:UserController控制器中的一个整形类型的成员变量),如果不加任何控制的话,变量A同时都会在JVM分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的!即使不是同时发过来,三个请求分别操作三个不同JVM内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的!

    如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题!

    为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLock或Synchronized)进行互斥控制。在单机环境中,Java中提供了很多并发处理相关的API。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

1.2、分布式锁应具备哪些条件

    在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:

    1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;

    2、高可用的获取锁与释放锁;

    3、高性能的获取锁与释放锁;

    4、具备可重入特性;

    5、具备锁失效机制,防止死锁;

    6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

1.3、分布式锁的三种实现方式

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。

    1、基于数据库实现分布式锁;

    2、基于缓存(Redis等)实现分布式锁;

    3、基于Zookeeper实现分布式锁;

    尽管有这三种方案,但是不同的业务也要根据自己的情况进行选型,他们之间没有最好只有更适合!

二、Redis分布式锁实战篇

2.1、导入依赖


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

2.2、配置Redis配置信息


spring
    redis:
      port: 6379
      host: 127.0.0.1
      password:
      database: 0

2.3、配置RedisConfig属性、如果需要使用FastJSON来序列化你的对象可以看看我前面写的一篇文章


@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate initRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws Exception {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

2.4、写一个RedisLock工具类



 
@Component
public class RedisLockCommon {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
 
    
    public Boolean tryLock(String key, String value) {
        if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isNotEmpty(currentValue) && Long.valueOf(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间 如果高并发的情况可能会出现已经被修改的问题  所以多一次判断保证线程的安全
            String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);
            if (StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }
        return false;
    }
 
 
    
    public void unlock(String key, String value) {
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        try {
            if (StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)) {
                stringRedisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
        }
    }
}

2.5、减库存操作


@Override
    public boolean decrementProductStore(Long productId, Integer productQuantity) {
        String key = "dec_store_lock_" + productId;
        long time = System.currentTimeMillis();
        try {
            //如果加锁失败
            if (!redisLock.tryLock(key, String.valueOf(time))) {
                return false;
            }
            ProductInfo productInfo = productInfoMapper.selectByPrimaryKey(productId);
            //如果库存为空
            if (productInfo.getProductStock() == 0) {
                return false;
            }
            //减库存操作
            productInfo.setProductStock(productInfo.getProductStock() - 1);
            productInfoMapper.updateByPrimaryKey(productInfo);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //解锁
            redisLock.unlock(key, String.valueOf(time));
        }
        return true;
 
    }

2.6、测试接口


@GetMapping("test")
public String createOrderTest() {
    if (!productInfoService.decrementProductStore(1L, 1)) {
        return "库存不足";
    }
    OrderMaster orderMaster = new OrderMaster();
    //未支付
    orderMaster.setOrderStatus(0);
    //未支付
    orderMaster.setPayStatus(0);
    orderMaster.setBuyerName("张三");
    orderMaster.setBuyerAddress("湖南长沙");
    orderMaster.setBuyerPhone("18692794847");
    orderMaster.setOrderAmount(BigDecimal.ZERO);
    orderMaster.setCreateTime(DateUtils.getCurrentDate());
    orderMaster.setOrderId(UUID.randomUUID().toString().replaceAll("-", ""));
    orderMasterService.insert(orderMaster);
}

2.7、可以使用使用Apache ab进行http接口测试

    具体文章可以看看这篇文章 https://www.jb51.net/article/177250.htm

三、结尾

到此这篇关于SpringBoot之使用Redis实现分布式锁(秒杀系统)的文章就介绍到这了,更多相关SpringBoot Redis分布式锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

    文章概览篇来源于 https://blog.csdn.net/hxpjava1/article/details/81068355

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯