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

益阳房地产网站建设艺术设计作品

益阳房地产网站建设,艺术设计作品,沧县官厅网站建设,沭阳建设局网站#x1f468;‍#x1f393;作者简介#xff1a;一位大四、研0学生#xff0c;正在努力准备大四暑假的实习 #x1f30c;上期文章#xff1a;Redis#xff1a;原理速成项目实战——Redis实战8#xff08;基于Redis的分布式锁及优化#xff09; #x1f4da;订阅专栏‍作者简介一位大四、研0学生正在努力准备大四暑假的实习 上期文章Redis原理速成项目实战——Redis实战8基于Redis的分布式锁及优化 订阅专栏Redis原理速成项目实战 希望文章对你们有所帮助 简单回顾一下之前实现秒杀的思路其实无非就是2点 1、库存够不够该用户有没有买过 2、操作数据库扣库存、创建订单 之前的业务由于涉及了大量数据库的操作所以性能并不是太好。 秒杀优化 异步秒杀思路Redis实现秒杀资格的判断分析实现 基于阻塞队列实现秒杀异步下单总结及发现问题 异步秒杀思路 之前的业务可以用下图来表示 可以发现Tomcat中顺序执行的操作里面有4个需要对数据库进行查询或修改操作而MySQL本身的并发能力就是很差的时间是所有时间之和这种解决方式并不好。 因此我们需要将操作数据库的步骤分给多个线程来做而库存量判断、是否具有购买资格的判断我们也可以将其交给Redis从而提高效率。 既然是将步骤分给多个线程来做我们要开辟线程并且要使得开辟的线程能够正确执行业务 整个业务的流程被分开了所需要的时间不再是所有的过程时间之和完成下单的操作是异步执行的而且整个业务基于Redis将会大大提升业务的性能。 但这需要考虑2个难点问题如何在Redis里面完成判断库存以及一人一单的校验如何基于阻塞队列实现秒杀异步下单 Redis实现秒杀资格的判断 分析 要在Redis里面判断库存量以及一人一单我们肯定需要将优惠券的库存信息以及相关订单信息缓存到Redis中去我们需要选取合适的数据结构来保存这两个信息。 库存很容易因为只包含了库存量这个信息直接使用String类型进行存储即可。 但要实现一人一单我们要判断这个优惠券被哪些用户购买过。所以这个数据结构需要能够保存多个值而又因为一人一单所以我们要保存的用户的id显然是不能重复的。所以set是很适合的。 因此业务流程可以很容易知道 可以发现业务的步骤还是有很多步的因此我们要保证步骤的原子性因此上述的内容应当用Lua脚本来写。 实现 需求 1、新增秒杀优惠券的同时将优惠券的信息保存到Redis中 2、基于Lua脚本判断秒杀库存、一人一单、决定用户是否抢购成功 3、若成功将优惠券id与用户id进行封装再存入阻塞队列 4、开启线程任务不断从阻塞队列中获取信息实现异步下单 1、在VoucherServiceImpl类中新增优惠券同时保存到Redis中 Resourceprivate StringRedisTemplate stringRedisTemplate;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);//保存秒杀库存到Redis中, SECKILL_STOCK_KEY seckill:stock:stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY voucher.getId(), voucher.getStock().toString());}打开postman进行测试 添加成功 2、在resources下创建seckill.lua决定用户能否抢券成功 -- 1 参数列表 -- 1.1 优惠券id local voucherId ARGV[1] -- 1.2 用户id local userId ARGV[2]-- 2 数据key -- 2.1 库存key local stockKey seckill:stock: .. voucherId -- 2.2 订单key local orderKey seckill:order: .. voucherId-- 3 脚本业务 -- 3.1 判断库存是够充足 if(tonumber(redis.call(get, stockKey)) 0) then-- 3.2 库存不足返回1return 1 end --3.2 判断用户是否下单即判断用户id是不是这个set集合的成员 if(redis.call(sismember, orderKey, userId) 1) then-- 3.2 存在说明重复下单return 2 end -- 3.4 扣库存 redis.call(incrby, stockKey, -1) -- 3.5 下单保存用户 redis.call(sadd, orderKey, userId) return 03、在VoucherOrderServiceImpl中修改seckillVoucher修改业务先调用Lua脚本执行返回0即可将下单信息存入阻塞队列存入阻塞队列的代码编写较为麻烦暂时放着 //秒杀优化调用Lua的代码Overridepublic Result seckillVoucher(Long voucherId) {//获取用户Long userId UserHolder.getUser().getId();//执行Lua脚本这里使用了静态代码块Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(), //这里我们没有key传只需要传送一个空集合即可voucherId.toString(), userId.toString()//传其他类型的参数);//判断结果是否为0int r result.intValue();if(r ! 0){//不为0没有购买资格return Result.fail(r 1 ? 库存不足 : 不能重复下单);}//为0有购买资格将下单信息保存到阻塞队列long orderId redisIdWorker.nextId(order);//TODO 保存阻塞队列这边先空着下一部分单独编写//返回订单idreturn Result.ok(orderId);}打开postman进行测试连续下单两次证明下单资格判断的可行性 当然数据库中的数据还是没有改变的 异步下单还没做。 如果要进行压力测试的话大家要自己构建出几百个用户然后这些用户分别占一个线程进行下单用jmeter进行测试完成这些测试还是很繁琐的但是因为这些操作都是基于Redis的容易知道吞吐率肯定是变大了不少的。 基于阻塞队列实现秒杀异步下单 对于用户资格的判断已经完成了假设用户具有秒杀的资格这时候我们只需要独立开辟一个线程去异步实现下单。因为用户只要具有这个资格直接返回订单的id从而让用户去执行付款即可而将订单的信息存入数据库并不需要严格的时效性因此业务可行 接下来开启线程任务不断从阻塞队列中获取信息实现下单这是整体性能变高的关键。 代码的实现我们之所以选择阻塞队列是因为阻塞队列具有一个重要的性质当线程尝试获取阻塞队列的元素时若队列中没有元素该线程就会被阻塞直到队列中获取到这个元素了。 另外代码中因为涉及了开辟独立线程去实现异步下单因此我们需要准备好线程池与线程任务。 最终业务的全部代码实现如下 Slf4j Service public class VoucherOrderServiceImpl extends ServiceImplVoucherOrderMapper, VoucherOrder implements IVoucherOrderService {//注入秒杀优惠券的serviceResourceprivate ISeckillVoucherService seckillVoucherService;Resourceprivate RedisIdWorker redisIdWorker;Resourceprivate StringRedisTemplate stringRedisTemplate;Resourceprivate RedissonClient redissonClient;public static final DefaultRedisScriptLong SECKILL_SCRIPT;static {SECKILL_SCRIPT new DefaultRedisScript();SECKILL_SCRIPT.setLocation(new ClassPathResource(seckill.lua));//设置脚本位置SECKILL_SCRIPT.setResultType(Long.class);//配置返回值}//阻塞队列当线程尝试从这个队列中获取元素如果没有元素那么该线程就会被阻塞直到队列中获取到元素private BlockingQueueVoucherOrder orderTasks new ArrayBlockingQueue(1024 * 1024);//线程池private static final ExecutorService SECKILL_ORDER_EXECUTOR Executors.newSingleThreadExecutor();//线程任务用户随时都能抢单所以应该要在这个类被初始化的时候马上开始执行PostConstruct //该注解表示在当前类初始化完毕以后立即执行private void init(){SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());}private class VoucherOrderHandler implements Runnable{Overridepublic void run() {//不断从队列中取订单信息while (true){try {VoucherOrder voucherOrder orderTasks.take();//创建订单流程里面无须再加锁加个锁就是做个兜底handleVoucherOrder(voucherOrder);} catch (Exception e) {//e.printStackTrace();log.error(创建订单异常, e);}}}}IVoucherOrderService proxy;//秒杀优化调用Lua的代码Overridepublic Result seckillVoucher(Long voucherId) {//获取用户Long userId UserHolder.getUser().getId();//执行Lua脚本这里使用了静态代码块Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(), //这里我们没有key传只需要传送一个空集合即可voucherId.toString(), userId.toString()//传其他类型的参数);//判断结果是否为0int r result.intValue();if(r ! 0){//不为0没有购买资格return Result.fail(r 1 ? 库存不足 : 不能重复下单);}//为0有购买资格将下单信息保存到阻塞队列long orderId redisIdWorker.nextId(order);//TODO 保存阻塞队列//先将用户id与订单id封装起来VoucherOrder voucherOrder new VoucherOrder();voucherOrder.setId(orderId);voucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);//放入阻塞队列orderTasks.add(voucherOrder);//获取代理对象proxy (IVoucherOrderService) AopContext.currentProxy();//返回订单idreturn Result.ok(orderId);}private void handleVoucherOrder(VoucherOrder voucherOrder) {//获取用户用户id不能再从UserHolder中取了因为现在是从线程池获取的全新线程不是主线程Long userId voucherOrder.getUserId();//创建锁对象RLock lock redissonClient.getLock(lock:order: userId);//获取锁boolean isLock lock.tryLock();//判断是否获取锁成功if(!isLock){log.error(不允许重复下单);//理论上不会发生return;}try {//这是主线程的一个子线程无法直接获得代理对象代理对象需要在主线程中获取并设置成成员变量使得该子线程能够获取//createVoucherOrder的方法体直接修改不再需要重新根据id创建订单而是直接将订单传进去proxy.createVoucherOrder(voucherOrder);} finally {lock.unlock();}}Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder) {Long userId voucherOrder.getUserId();//查询订单int count query().eq(user_id, userId).eq(voucher_id, voucherOrder.getVoucherId()).count();//判断是否存在if (count 0) {log.error(不可重复购买);return;}//扣减库存boolean success seckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id, voucherOrder.getVoucherId()).gt(stock, 0).update();if (!success) {log.error(库存不足);return;}//不需要再创建订单直接savesave(voucherOrder);} } 总结及发现问题 1、秒杀业务的优化思路 1先用Redis完成库存量以及一人一单判断完成抢单 2下单的业务操作数据库的业务放入阻塞队列并用独立线程完成异步下单 2、基于阻塞队列的异步秒杀存在的问题 1内存限制问题我们使用了JDK的阻塞队列占用的是JVM内存若不加以限制会有很多的订单对象创建线程并且将大量信息放入阻塞队列可能会内存溢出 2数据安全问题要用于下单的业务信息都存到了内存中万一服务宕机那么用户完成了抢单但是数据库却没有做出相应的修改将会导致数据不一致 解决的方法将在下一节进行分析
http://www.dnsts.com.cn/news/72508.html

相关文章:

  • 湖北网站建站系统哪家好wordpress类似于mdx主题
  • 锡山建设局网站该网站是恶意网站
  • 环保网站建设模板免费下载绍兴网站关键词优化
  • 临湘市建设局网站wordpress 自建页面
  • 怎么在本地搭建网站百度云主机上装网站
  • 网站的建设与规划网络的营销方法有哪些
  • 游戏网站开发实验报告网站伪静态作用
  • 网站建设费记在什么科目下杭州模板开发建站
  • 乒乓球网站建设目标二维码生成器官网
  • 庞各庄网站建设网站赚钱平台
  • 口碑好的定制网站建设提供商汉中专业网站建设公司
  • 网上有哪些购物网站为什么建设的网站有时候访问慢
  • cdn网站加速做淘宝浏览单的网站
  • 2016年建设网站赚钱吗进行网站开发 如何搭建环境
  • 延安网站建设做详情页比较好的网站
  • 网站创建想法品牌创意网站建设徕卡e
  • 新野网站建设网站是指什么
  • 深圳网站建设学习网站响应式图片切换代码
  • 网站开发排行榜设计师网址大全
  • 建站费用参考网站开发常用的数据库
  • 营销网站建设网站开发无锡网站建设方式
  • 曲靖住房和城乡建设局网站网站栏目词
  • 北京大兴网站建设公司咨询wordpress 腾讯视频
  • 网站建站建设多少钱2021最新新闻热点事件
  • 建设部造价咨询企业网站上海注册公司买新能源车
  • 青白江区城乡和建设局网站交易系统开发
  • 手机做炫光头像图的网站网络营销方法
  • 顺德网站建设7starry开发一个网站需要多久
  • 深圳企业网站制作报价电子商务网站建设的准备工作有哪些
  • 网站设计报价是多少钱网站项目流程表