Redisson入门(优惠券秒杀、分布式锁)

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>
@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        //配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1");
        //创建RedissonClient对象
        return Redisson.create(config);
    }
}

        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁,无参就失败不等待,直接返回
        boolean isLock = lock.tryLock();
        //判断是否获取锁成功
        if (!isLock) {
            //获取锁失败,返回错误或重试
            return Result.fail("一个人只允许下一单");
        }
        try {
            //获取代理对象(事务)
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            lock.unlock();
        }
 优惠券秒杀、分布式锁逻辑层代码(乐观锁、悲观锁)实现了一人一单
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Resource
    private SeckillVoucherMapper seckillVoucherMapper;
    @Resource
    private VoucherOrderMapper voucherOrderMapper;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1.查询秒杀优惠卷
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀尚未开始");
        }
        //3.判断秒杀是否结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀已经结束");
        }
        //判断库存是否充足
        if (voucher.getStock() <= 0) {
            return Result.fail("库存不足");
        }
        Long userId = UserHolder.getUser().getId();
        //确保当用户id值一样时,就用一把锁,不同的用户用不同的锁
        //确保获取锁,提交事务,释放锁,线程就安全了
        //创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁,无参就失败不等待,直接返回
        boolean isLock = lock.tryLock();
        //判断是否获取锁成功
        if (!isLock) {
            //获取锁失败,返回错误或重试
            return Result.fail("一个人只允许下一单");
        }
        try {
            //获取代理对象(事务)
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            lock.unlock();
        }
    }

    @Transactional
    public Result createVoucherOrder(Long voucherId) {
        //查询数据库有没有数据时,不能用乐观锁,需用悲观锁
        //6.一人一单 判断订单表中是否存在用户对应某优惠券的单条记录
        //6.1 查询订单
        Long userId = UserHolder.getUser().getId();
        QueryWrapper<VoucherOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",userId).eq("voucher_id", voucherId);
        Integer count = voucherOrderMapper.selectCount(queryWrapper);
        //6.2 判断是否存在
        if(count > 0){
            //已经购买过一次
            return Result.fail("用户已经购买过一次");
        }
        //扣减库存
//        boolean success = seckillVoucherService.update()
//                .setSql("stock = stock - 1")
//                .eq("voucher_id", voucherId).update();

        UpdateWrapper<SeckillVoucher> updateWrapper = new UpdateWrapper<>();
        /**
         * 使用乐观锁,乐观锁是在更新数据时使用
         * 为了防止超卖,应该设置查询时的票数等于自己准备修改时的票数,此时会出现少卖问题,
         *         所以直接改为当修改时票数大于0,即可修改
         */
        updateWrapper.eq("voucher_id", voucherId).gt("stock", 0);
        updateWrapper.setSql("stock = stock - 1");
        int success = seckillVoucherMapper.update(null, updateWrapper);
        if (success == 0) {
            return Result.fail("库存不足");
        }
        //没有买过就创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //6.1订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //6.2用户id

        voucherOrder.setUserId(userId);
        //6.3代金券id
        voucherOrder.setVoucherId(voucherId);
        voucherOrderMapper.insert(voucherOrder);
        //返回订单id
        return Result.ok(orderId);
    }
}

里面使用到了代理,需引入pom.xml 和 启动类上加注解

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>




@EnableAspectJAutoProxy(exposeProxy = true)
public interface IVoucherOrderService extends IService<VoucherOrder> {

    Result seckillVoucher(Long voucherId);

    Result createVoucherOrder(Long voucherId);
}
RedisIdWorker工具类(唯一ID生成器)
@Component
public class RedisIdWorker {

    //开始时间戳
    private static final long BEGIN_TIMESTAMP = 1704067200L;

    //序列号的位数
    private static final int COUNT_BITS = 32;
    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public long nextId(String keyPrefix){
        //1.生成当前时间的时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;
        //2.生成序列号
        //2.1 获取当前日期,精确到天,每天一个key,方便统计订单量
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        //2.2自增长
        Long count = stringRedisTemplate.opsForValue().increment("icr" + keyPrefix + ":" + date);
        //3.拼接并返回  时间戳放左边 序列号放右边  将时间戳右移
        // |count 时间戳右移32位后,右边32位全为0,或之后,原来是什么就是什么
        return timestamp << COUNT_BITS | count;
    }

    //用于生成开始时间的时间戳
//    public static void main(String[] args) {
//        LocalDateTime time = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
//        long second = time.toEpochSecond(ZoneOffset.UTC); //将当前时间转换为秒数
//        System.out.println(second);
//    }
}

<think>好的,我现在需要处理用户关于“黑马点评介绍”的查询。用户想了解黑马点评的相关信息,可能是一个项目或者课程案例。首先,我需要明确用户的具体需求是什么,是技术架构、功能模块,还是学习资源? 接下来,调用网络搜索工具,输入关键词“黑马点评介绍”、“黑马点评项目”、“黑马点评功能”。查看搜索结果,发现大部分资料来自培训机构,比如传智播客的黑马程序员课程。黑马点评可能是一个教学项目,用于教授分布式系统Redis应用。 根据搜索结果,黑马点评是一个模拟大众点评的实战项目,重点在于Redis的应用,比如缓存、分布式锁秒杀等。技术栈可能包括SpringBoot、Redis、MySQL、MyBatis-Plus。需要整合这些信息,分模块介绍,比如项目定位、技术架构、核心功能、适用场景。 用户可能需要详细的技术细节,比如如何实现缓存击穿解决方案,或者如何用Redis实现分布式锁。同时,要避免使用第一称,按照要求分点说明,每个部分换行。需要检查是否有代码或公式,如果有,要正确格式化。 最后,生成相关问题时要基于项目本身,考虑用户可能进一步想知道的技术点、学习资源或部署方式。比如项目是否开源,涉及哪些技术难点,适合的学习阶段等。确保问题相关且具体,帮助用户深入探索。</think>黑马点评是传智播客旗下黑马程序员课程中的实战项目案例,定位为本地生活服务平台,模拟大众点评的业务场景。该项目主要用于教学分布式系统开发Redis实战应用,帮助学习者掌握企业级项目开发流程高并发场景解决方案。 **技术架构** 技术栈包含SpringBoot+MySQL+MyBatis-Plus+Redis+Nginx,采用前后端分离架构。前端使用Vue.js实现页面交互,后端通过Restful API提供据接口。项目部署采用Docker容器化方案,支持环境配置。 **核心功能模块** - 商户查询模块:实现LBS地理定位查询,结合Redis GEO存储商户坐标据 - 优惠券秒杀模块:采用Redis+Lua脚本实现库存原子性操作,通过Redisson分布式锁解决超卖问题 - 签到统计模块:使用Redis BitMap实现每日签到功能,支持连续签到统计 - 缓存优化模块:实现级缓存架构,采用缓存穿透解决方案(布隆过滤器+空值缓存) - 好友关注模块:基于Redis ZSet实现Feed流推送,支持按时间排序的动态更新 **Redis实战应用** 1. 分布式锁实现:通过`SETNX key value`命令配合Lua脚本保证原子性 ```lua if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call('expire', KEYS[1], ARGV[2]) else return 0 end ``` 2. 秒杀库存扣减:使用Hash结构存储库存信息,扣减公式为 $$stock = redis.call('HINCRBY', KEYS[1], 'stock', -1)$$ **适用场景** 项目适合具备Java Web基础的学习者,作为分布式系统开发入门案例。通过该案例可掌握: - 基于Redis的缓存设计模式 - 高并发场景解决方案 - 分布式系统常见问题处理 - 微服务架构下的性能优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值