原先逻辑链接:号源预约加锁思路_java 预约 接口加锁-CSDN博客
一、进行治疗项目和号源数据缓存
1.新建一个定时任务,主要在凌晨时缓存治疗项目和号源数据
1.1.类中使用redission获取锁(用于分布式系统获取数据,保证原子性,单体架构可以忽略)
1.2.获取接下来几天开放的数据库治疗项目、号源信息和获取redis中的治疗项目信息
// 获取号源数据
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(CACHE_PREFIX);
// 号源唯一id,用于判断是否存在数据
ops.hasKey(sessionId);
// 如果没有数据则塞入数据
ops.put(sessionId,json);
1.3.遍历数据库治疗项目,并通过唯一键获取redis的治疗项目,看下否存在,不存在就将数据库治疗项目塞入redis中
1.4.获取号源的数量,根据号源数量生成信号量
// 生成信号量 token:号源唯一号
RSemaphore semaphore = redissonClient.getSemaphore(SEMAPHORE + token);
// 号源有多少就放多少的信号量,只要抢到一个号就减去一个信号
semaphore.trySetPermits(num);
// 设置过期时间。
semaphore.expireAt(endTime);
二、用户抢号核心
1、使用volatile变量定义好共享变量:队列序号、预约队列、入参参数Hashmap、队列结果Hashmap
2、患者开始预约时,根据队列序号+"saveReservation"成为唯一键,将唯一键放入预约队列和将入参放进参数Hashmap
3、使用 多线程new Thread 开始预约任务
3.1、使用redission获取锁(redis执行lua脚本获取锁,如果获取锁成功则结束等待,反之则继续等待或者获取锁超时,同时还有看门狗机制)
3.2、得到锁之后,从队列中取出唯一键,根据唯一键拿出入参,开始处理预约业务,
3.3、从redis拿出治疗项目数据和号源信息,通过入参数据,判断是否能正常的业务逻辑进行抢号
BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(CACHE_PREFIX);
// 通过id获取治疗项目
String json = hashOps.get(sessionId);
3.4、如果能正常抢号则判断是否还有号源和是否抢过号
//考虑到只要抢一次就行,用redis设置占位符,防止重复抢号
//设置自动过期
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);// token:号源唯一号
RSemaphore semaphore = redissonClient.getSemaphore(SEMAPHORE + token);
//从信号量中取出一个,等同于从号源取出一个
boolean b = semaphore.tryAcquire(1);
3.5、取到号后,开始处理更新治疗项目和号源信息,同时将处理结果放到队列结果中
3.6、如果预约超时或者程序报错,则将错误信息放到队列结果中
3.5、最后释放锁,移除hashmap中的参数
4、在线程外,循环等待获取当前队列结果的数据并返回给患者,同时移除hashmap中的返回结果