当前位置: 首页 > news >正文

酒店预定类网站建设网站建设 中企动力 东莞

酒店预定类网站建设,网站建设 中企动力 东莞,网站建设的设备,做个企业网站大概多少费用秒杀优化-异步秒杀思路 未优化的思路 当用户发起请求#xff0c;此时会请求nginx#xff0c;nginx会访问到tomcat#xff0c;而tomcat中的程序#xff0c;会进行串行操作#xff0c;分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一…秒杀优化-异步秒杀思路 未优化的思路 当用户发起请求此时会请求nginxnginx会访问到tomcat而tomcat中的程序会进行串行操作分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一人一单 5、扣减库存 6、创建订单 在这六步操作中又有很多操作是要去操作数据库的而且还是一个线程串行执行 这样就会导致我们的程序执行的很慢 优化方案 我们将耗时比较短的逻辑判断放入到redis中比如是否库存足够比如是否一人一单这样的操作只要这种逻辑可以完成就意味着我们是一定可以下单完成的我们只需要进行快速的逻辑判断根本就不用等下单逻辑走完我们直接给用户返回成功 再在后台开一个线程后台线程慢慢的去执行queue里边的消息即不追求时效性让用户先成功下单后续再完善数据库数据 整体思路 用户下单之后判断库存是否充足只需要到redis中去根据key找对应的value是否大于0即可如果不充足则直接结束如果充足继续在redis中判断用户是否可以下单如果set集合中没有这条数据说明他可以下单如果set集合中没有这条记录则将userId和优惠卷存入到redis中并且返回0整个过程需要保证是原子性的我们可以使用lua来操作 当以上判断逻辑走完之后我们可以判断当前redis中返回的结果是否是0 如果是0则表示可以下单则将之前说的信息存入到到queue中去然后返回然后再来个线程异步的下单前端可以通过返回的订单id来判断是否下单成功。 难点 怎么在redis中去快速校验一人一单还有库存判断由于我们校验和tomct下单是两个线程那么我们如何知道到底哪个单他最后是否成功或者是下单完成为了完成这件事我们在redis操作完之后我们会将一些信息返回给前端同时也会把这些信息丢到异步queue中去后续操作中可以通过这个id来查询我们tomcat中的下单逻辑是否完成了。 代码实现 需求 新增秒杀优惠券的同时将优惠券信息优惠券id和库存信息保存到Redis中 基于Lua脚本判断秒杀库存、一人一单决定用户是否抢购成功 如果抢购成功将优惠券id和用户id封装后存入阻塞队列 开启线程任务不断从阻塞队列中获取信息实现异步下单功能 新增优惠券将优惠券信息入库并写入redis OverrideTransactionalpublic void addSeckillVoucher(Voucher voucher) {// 保存优惠券save(voucher);// 保存秒杀信息SeckillVoucher seckillVoucher new SeckillVoucher();seckillVoucher.setVoucherId(voucher.getId());seckillVoucher.setStock(voucher.getStock());seckillVoucher.setBeginTime(voucher.getBeginTime());seckillVoucher.setEndTime(voucher.getEndTime());seckillVoucherService.save(seckillVoucher); //存入redisstringRedisTemplate.opsForValue().setIfAbsent(SECKILL_STOCK_KEY voucher.getId(), voucher.getStock().toString());} 判断秒杀库存、一人一单决定用户是否抢购成功考虑到操作的原子性采用lua脚本完成这一连串的操作 --- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by Lenovo. --- DateTime: 2023/9/5 20:57 --- -- 1.参数列表 -- 1.1.优惠券id local voucherId ARGV[1] -- 1.2.用户id local userId ARGV[2] ---- 1.3.订单id local orderId ARGV[3]-- 2.数据key -- 2.1.库存key local stockKey seckill:stock: .. voucherId ---- 2.2.订单key local orderKey seckill:order: .. voucherId-- 3.脚本业务 -- 3.1.判断库存是否充足 get stockKey if(tonumber(redis.call(get, stockKey)) 0) then-- 3.2.库存不足返回1return 1 end -- 3.2.判断用户是否下单 SISMEMBER orderKey userId if(redis.call(sismember, orderKey, userId) 1) then-- 3.3.存在说明是重复下单返回2return 2 end -- 3.4.扣库存 incrby stockKey -1 redis.call(incrby, stockKey, -1) -- 3.5.下单保存用户sadd orderKey userId redis.call(sadd, orderKey, userId) ---- 3.6.发送消息到队列中 XADD stream.orders * k1 v1 k2 v2 ... redis.call(xadd, stream.orders, *, userId, userId, voucherId, voucherId, id, orderId) return 0 执行lua脚本判断是否抢购成功如果抢购成功要放入堵塞队列中 Overridepublic Result seckillVoucher(Long voucherId) {SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);//判断是否开始开始时间如果在当前时间之后就是尚未开始if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(秒杀尚未开始);}//判断是否结束结束时间如果在当前时间之前就是已经结束if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(秒杀已经结束);}Long userId UserHolder.getUser().getId();long orderId new RedisIdWorker(stringRedisTemplate).nextId(order);Long execute stringRedisTemplate.execute(SILLL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString(), String.valueOf(orderId));int r execute.intValue();if (r ! 0) {return Result.fail(r 1 ? 库存不足 : 不能重复下单);}VoucherOrder voucherOrder new VoucherOrder();//订单idvoucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);//将订单信息放入阻塞队列orderTakes.add(voucherOrder);return Result.ok(orderId);} 定义线程内部类不断从堵塞队列中读取订单 //从阻塞队列里面取订单信息private class voucherOrderHander implements Runnable {Overridepublic void run() {while (true) {try {VoucherOrder take orderTakes.take();handleVoucherOrder(take);} catch (Exception e) {log.error(异常信息如下, e);}}} 获取订单信息的具体方法,这里依然加了分布式锁是为了保险起见 private void handleVoucherOrder(VoucherOrder take) {Long userId take.getId();//创建锁对象RLock lock redissonClient.getLock(lock:order: userId);//尝试获取锁boolean isLock lock.tryLock();//获取锁失败if (!isLock) {log.error(不允许重复下单);return;}try {voucherOrderService.createVoucherOrder(take);} finally {//释放锁lock.unlock();}}} 这里又有一个问题就是我们订单信息入库应该是在该类对象被创建的时候就要开启线程在堵塞队列等待读取是否有订单信息然后顺利入库所以我们用了aop的PostConstruct保证该对象被创建时线程也能顺利创建这里用了线程池来提交线程任务 PostConstructpublic void init() {SECKILL_ORDER_EXECUTOR.execute(new voucherOrderHander());} 完整代码实现 Service public class VoucherOrderServiceImpl extends ServiceImplVoucherOrderMapper, VoucherOrder implements IVoucherOrderService {Autowiredprivate ISeckillVoucherService seckillVoucherService;Autowiredprivate RedisIdWorker redisIdWorker;Autowiredprivate IVoucherOrderService voucherOrderService;Autowiredprivate StringRedisTemplate stringRedisTemplate;Autowiredprivate RedissonClient redissonClient;private static final DefaultRedisScriptLong SILLL_SCRIPT;BlockingQueueVoucherOrder orderTakes new ArrayBlockingQueue(1024 * 1024);//异步处理线程池private static final ExecutorService SECKILL_ORDER_EXECUTOR Executors.newSingleThreadExecutor();static {SILLL_SCRIPT new DefaultRedisScript();SILLL_SCRIPT.setLocation(new ClassPathResource(skill.lua));SILLL_SCRIPT.setResultType(Long.class);}PostConstructpublic void init() {SECKILL_ORDER_EXECUTOR.execute(new voucherOrderHander());}//从阻塞队列里面取用户信息private class voucherOrderHander implements Runnable {Overridepublic void run() {while (true) {try {VoucherOrder take orderTakes.take();handleVoucherOrder(take);} catch (Exception e) {log.error(异常信息如下, e);}}}private void handleVoucherOrder(VoucherOrder take) {Long userId take.getId();//创建锁对象RLock lock redissonClient.getLock(lock:order: userId);//尝试获取锁boolean isLock lock.tryLock();//获取锁失败if (!isLock) {log.error(不允许重复下单);return;}try {voucherOrderService.createVoucherOrder(take);} finally {//释放锁lock.unlock();}}}Overridepublic Result seckillVoucher(Long voucherId) {SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);//判断是否开始开始时间如果在当前时间之后就是尚未开始if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(秒杀尚未开始);}//判断是否结束结束时间如果在当前时间之前就是已经结束if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(秒杀已经结束);}Long userId UserHolder.getUser().getId();long orderId new RedisIdWorker(stringRedisTemplate).nextId(order);Long execute stringRedisTemplate.execute(SILLL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString(), String.valueOf(orderId));int r execute.intValue();if (r ! 0) {return Result.fail(r 1 ? 库存不足 : 不能重复下单);}VoucherOrder voucherOrder new VoucherOrder();//订单idvoucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);//将订单信息放入阻塞队列orderTakes.add(voucherOrder);return Result.ok(orderId);} Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder) {Long userId voucherOrder.getUserId();// 5.1.查询订单int count query().eq(user_id, userId).eq(voucher_id, voucherOrder.getVoucherId()).count();// 5.2.判断是否存在if (count 0) {// 用户已经购买过了log.error(用户已经购买过了);return;}// 6.扣减库存boolean success seckillVoucherService.update().setSql(stock stock - 1) // set stock stock - 1.eq(voucher_id, voucherOrder.getVoucherId()).gt(stock, 0) // where id ? and stock 0.update();if (!success) {// 扣减失败log.error(库存不足);return;}save(voucherOrder);}
http://www.pierceye.com/news/95164/

相关文章:

  • 做推广最好的网站是哪个菜鸟网站建设
  • 首钢建设公司网站微信网站怎么做的好处
  • 西安网站开发费用网站即将 模板
  • 个人做商业网站需要什么热门网站建设代理
  • 企业网站手机端和pc端一个后台吗企业网站管理系统的运维服务
  • 北京官网开发优化游戏性能的软件
  • 网站开发选asp还是hph集约化网站群建设情况
  • 做网站域名重要吗10000ip网站怎么做
  • 途牛的旅游网站是谁做的wordpress 注册用户列表
  • 如何编辑网站新吁网站建设
  • 网站开发采集工具免费引流在线推广
  • 全面的锦州网站建设西安建筑工程有限公司
  • 做网站 郑州公司哪家好哪个购物网站最便宜
  • dedecms网站后台免费网页小游戏
  • 如何查网站外链wordpress火车头采集免费版
  • 四川住房建设和城乡建设厅新网站wordpress 采集 api
  • 企业所得税怎么交南昌seo实用技巧
  • 深圳英文网站开发企业网站和展板建设
  • 国内网站设计制作网页游戏传奇盛世开服表
  • 网站图片放大特效怎么做网站建设的后期服务要包括什么软件
  • 网站降权投诉商标注册证书电子版怎么查询
  • 济南网站制作公司哪家好网站建设搞笑广告词
  • 建设主管部门门户网站摄影网站源码 免费下载
  • js 曲线 网站营销型网站方案书
  • 如何盗取网站软件开发的自学教程
  • 傻瓜建站家庭网络搭建网站
  • 扬中做网站的公司静态网页生成器
  • 襄阳做公司网站的软件公司wordpress网站好做排名吗
  • 电商网站功能介绍太原市做网站公司
  • 网站开发融资计划网站响应式和电脑手机