当前位置: 首页 > 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.zqtcl.cn/news/468814/

相关文章:

  • 怎么通过域名访问网站elision wordpress
  • 做邮轮的网站做游戏的软件app
  • 做网站用php还是python家装十大品牌排行榜
  • 湛江网站建设招聘创作者服务平台
  • 衡阳建网站高中制作网站怎么做
  • 上海网站排名团队推广链接跳转
  • 寻找郑州网站优化公司上海高端网站定制
  • 网站关键词排名优化长城建设投资有限公司网站
  • 网站专题优化电子商务网站运营方案
  • 唐山建网站公司湖南网站制作电话
  • 做神马网站优化合肥城乡建设局官网
  • 网站开发与管理心得体会建设高流量网站
  • 网站安全建设的重要性减粘装置设备设计要点
  • 建设一个网站的所有代码Django和wordpress速度
  • 临沂市建设局网站公示php建站系统
  • 有哪些好的做问卷调查的网站好学的专业是编课 网站开发英语翻译
  • 个人网站免费推广广饶网站制作
  • 怎么检测网站是否安全拍卖网站开发
  • 沂源网站制作自建网站的流程
  • 网站关键词收录查询网站最好服务器
  • 做百度移动网站优网站建设类论文选题
  • 自己做的网站怎样让百度搜到长沙专业外贸建站公司
  • 上海缔客网站建设公司网站策划书内容不包括什么
  • 找团队做网站网站建设 通知
  • 网站标题上的小图标怎么做的霞浦建设局网站
  • 国外那些网站做展厅比较好vp代理商网站管理系统
  • 广州最大网站建设wordpress数字超市
  • 怎么提高网站seo优化关键字排名wordpress媒体库搜索
  • 伊春网站制作怎么做视频网站赚钱吗
  • 前端网站开发邹城住房城乡建设部网站