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

wordpress显示flash logo关键词排名优化官网

wordpress显示flash logo,关键词排名优化官网,怎样做公司的网站建设,宜春做网站Redis学习Day3——黑马点评项目工程开发-CSDN博客 问题发现及描述 在黑马点评项目中,进行到使用Redis提供的Stream消息队列优化异步秒杀问题时,我在进行jmeter测试时遇到了重大的错误 发现无论怎么测试,一定会进入到catch中,又由…

Redis学习Day3——黑马点评项目工程开发`-CSDN博客

问题发现及描述

        在黑马点评项目中,进行到使用Redis提供的Stream消息队列优化异步秒杀问题时,我在进行jmeter测试时遇到了重大的错误

发现无论怎么测试,一定会进入到catch中,又由于消息队列是个循环读的过程,所以ERROR 33016错误就会不断的发生。

观察一下报错信息

java.lang.NullPointerException: Cannot invoke "com.hmdp.service.IVoucherOrderService.createVoucherOrder(com.hmdp.entity.VoucherOrder)" because the return value of "com.hmdp.service.impl.VoucherOrderServiceImpl.access$400(com.hmdp.service.impl.VoucherOrderServiceImpl)" is null

意思是

java.lang.NullPointerException 错误表明你的代码中有一个地方尝试调用了 null 对象的方法或访问了其属性。在你的具体错误信息中,问题出现在尝试调用 com.hmdp.service.IVoucherOrderService.createVoucherOrder(com.hmdp.entity.VoucherOrder) 方法时,但这个方法的调用是通过 com.hmdp.service.impl.VoucherOrderServiceImpl.access$400(com.hmdp.service.impl.VoucherOrderServiceImpl) 返回的对象进行的,而这个返回值为 null。

问题排除

既然明白了问题缘由是空对象导致出来的,那我们就根据报错的栈信息去处理:

定位位置

at com.hmdp.service.impl.VoucherOrderServiceImpl$VoucherOrderHandler.handleVocherOrder(VoucherOrderServiceImpl.java:406) ~[classes/:na]

at com.hmdp.service.impl.VoucherOrderServiceImpl$VoucherOrderHandler.handlePendingList(VoucherOrderServiceImpl.java:438) ~[classes/:na]

at com.hmdp.service.impl.VoucherOrderServiceImpl$VoucherOrderHandler.run(VoucherOrderServiceImpl.java:385) ~[classes/:na]

发现定位出现问题的是 执行订单创建方法  handleVocherOrder()

 跟进去看看,proxy代理对象也是一个报错提示点

结论         

        哦,这么一来问题就解决啦!原来是由于handleVocherOrder()需要使用到代理对象进行订单创建,那他必须不能写在线程任务了,要不然是没有办法获取到代理对象的,也就是null。就是因为这个空,才导致了我们的程序一致在报错。

错误代码说明

        一开始,为了代码逻辑的顺畅可懂,我将方法进行编号,并统一写入了线程任务VoucherOrderHandler方法中,在我看来handleVocherOrder()创建订单方法 和 handlePendingList()执行异常方法 对应着两者情况,本身的地位是一致的,于是乎将其都写在了线程的内部。

        但是没注意到的是,handleVocherOrder()需要调用在主线程提供的代理对象,这样一来就没理由将它写在异步线程任务中了。

//3. 创建线程任务用于接收消息队列的信息private class VoucherOrderHandler implements Runnable{// 消息队列名称private String queueName = "stream.orders";@Overridepublic void run() {while (true) {try{//1. 获取队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.oredes >// 指定队列名称,组名称,消费者名称,读取模式,读取数量,阻塞时间,队列名称,读取位置List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),StreamOffset.create(queueName, ReadOffset.lastConsumed()));//2. 判断消息获取是否成功if( list == null || list.isEmpty()){//2.1 获取失败 说明没有消息 ---->继续循环continue;}// 解析消息中的订单信息MapRecord<String,Object,Object> record = list.get(0);//  获取键值对集合Map<Object,Object> values = record.getValue();// 获取订单信息VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);//3. 获取成功,执行订单创建handleVocherOrder(voucherOrder);//4. ACK确认  SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());}catch (Exception e) {// 消息没有被ACK确认 进入Pending Listlog.error("订单处理出现异常",e);handlePendingList();}}}// 4. 取到了订单—————创建订单private void handleVocherOrder(VoucherOrder voucherOrder){// 获取用户Long userId = voucherOrder.getUserId();// 1. 创建锁对象RLock lock =  redissonClient.getLock("lock:order:" + userId);//2. 尝试获取锁boolean isLock = lock.tryLock();// 3. 判断锁是否获取成功if(! isLock){log.error("不允许重复下单");}try {proxy.createVoucherOrder(voucherOrder);} finally {// 4. 释放锁lock.unlock();}}// 5.取不到订单————— 处理Pending List中的订单信息private void handlePendingList(){while (true) {try {//1. 获取Pending List中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1  STREAMS stream.oredes 0// 指定队列名称,组名称,消费者名称,读取模式,读取数量,阻塞时间,队列名称,读取位置List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1),StreamOffset.create(queueName, ReadOffset.from("0")));//2. 判断消息获取是否成功if (list == null || list.isEmpty()) {//2.1 获取失败 说明Pending List没有消息 ---->结束循环break;}// 解析消息中的订单信息MapRecord<String, Object, Object> record = list.get(0);//  获取键值对集合Map<Object, Object> values = record.getValue();// 获取订单信息VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);//3. 获取成功,执行订单创建handleVocherOrder(voucherOrder);//4. ACK确认  SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());} catch (Exception e) {log.error("Pending List订单处理出现异常", e);try {Thread.sleep(20);}catch (InterruptedException interruptedException){interruptedException.printStackTrace();}}}}}

     正确代码展示

/** 方案二、三公共代码* 预加载lua脚本*/private static DefaultRedisScript<Long> SECKILL_SCRIPT;static {SECKILL_SCRIPT = new DefaultRedisScript<>();// 这是第二种方案需要执行的lua脚本// SECKILL_SCRIPT.setLocation(new ClassPathResource("lua/seckill.lua"));// 这是第三种方案需要执行的lua脚本SECKILL_SCRIPT.setLocation(new ClassPathResource("lua/streamSeckill.lua"));SECKILL_SCRIPT.setResultType(Long.class);}/*-----------------------------第三种方案: 使用Redis的stream消息队列 + redis + lua脚本判断秒杀资格添加消息队列 的方案-------------------------------------------------------------*/// 1,创建-- 秒杀线程池private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();//2. 初始化方法  一初始化就执行@PostConstructpublic void init(){SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());}//3. 创建线程任务用于接收消息队列的信息private class VoucherOrderHandler implements Runnable{// 消息队列名称private String queueName = "stream.orders";@Overridepublic void run() {while (true) {try{//1. 获取队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.oredes >// 指定队列名称,组名称,消费者名称,读取模式,读取数量,阻塞时间,队列名称,读取位置List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),StreamOffset.create(queueName, ReadOffset.lastConsumed()));//2. 判断消息获取是否成功if( list == null || list.isEmpty()){//2.1 获取失败 说明没有消息 ---->继续循环continue;}// 解析消息中的订单信息MapRecord<String,Object,Object> record = list.get(0);//  获取键值对集合Map<Object,Object> values = record.getValue();// 获取订单信息VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);//3. 获取成功,执行订单创建handleVocherOrder(voucherOrder);//4. ACK确认  SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());}catch (Exception e) {// 消息没有被ACK确认 进入Pending Listlog.error("订单处理出现异常",e);handlePendingList();}}}// 5.取不到订单————— 处理Pending List中的订单信息private void handlePendingList(){while (true) {try {//1. 获取Pending List中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1  STREAMS stream.oredes 0// 指定队列名称,组名称,消费者名称,读取模式,读取数量,阻塞时间,队列名称,读取位置List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1),StreamOffset.create(queueName, ReadOffset.from("0")));//2. 判断消息获取是否成功if (list == null || list.isEmpty()) {//2.1 获取失败 说明Pending List没有消息 ---->结束循环break;}// 解析消息中的订单信息MapRecord<String, Object, Object> record = list.get(0);//  获取键值对集合Map<Object, Object> values = record.getValue();// 获取订单信息VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);//3. 获取成功,执行订单创建handleVocherOrder(voucherOrder);//4. ACK确认  SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());} catch (Exception e) {log.error("Pending List订单处理出现异常", e);try {Thread.sleep(20);}catch (InterruptedException interruptedException){interruptedException.printStackTrace();}}}}}// 4. 取到了订单—————创建订单private void handleVocherOrder(VoucherOrder voucherOrder){// 获取用户Long userId = voucherOrder.getUserId();// 1. 创建锁对象RLock lock =  redissonClient.getLock("lock:order:" + userId);//2. 尝试获取锁boolean isLock = lock.tryLock();// 3. 判断锁是否获取成功if(! isLock){log.error("不允许重复下单");}try {proxy.createVoucherOrder(voucherOrder);} finally {// 4. 释放锁lock.unlock();}}/***  秒杀优惠券下单------秒杀优化代码----lua脚本---主线程---使用Redis stream的消息队列完成的*/private IVoucherOrderService proxy;@Overridepublic Result seckillVoucher(Long voucherId) {// 获取用户Long userId = UserHolder.getUser().getId();// 获取订单idlong orderId =  redisIdWorker.nextId("order");//1.执行Lua脚本Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),userId.toString(),String.valueOf(orderId));//2.判断结果是否为0int r = result.intValue();if(r != 0){//3.不为0,代表没有购买资格return Result.fail(r == 1 ? "库存不足!" : "不能重复下单!");}//提前 获取代理对象proxy = (IVoucherOrderService) AopContext.currentProxy();//5.返回订单idreturn Result.ok(orderId);}/*** 秒杀优惠券下单------秒杀优化代码----创建订单* @param voucherOrder*/@Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder) {//4. 限制一人一单【悲观锁方案】Long userId = voucherOrder.getUserId();//4.1 查询订单int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();//4.2 判断订单是否存在// 是 -----> 返回异常信息---->结束if (count > 0) {log.error("用户已经购买了一次了");}//5. 扣减库存——解决超卖问题【乐观锁方案】boolean success = seckillVoucherService.update().setSql("stock = stock-1").eq("voucher_id", voucherOrder.getVoucherId()).gt("stock", 0) // 库存大于0就行了.update();if (!success) {log.error("库存不足");}//6. 创建订单save(voucherOrder);}
}

   总结

        以前在遇到bug时,我总喜欢做的事是将别人写的代码复制回来。但是随着学习的深入发现,其实调代码是一件正常不过的事情,为此,锻炼自己发现问题、定位问题、解决问题能力十分重要,不断地刨根问底,才能愈发印象深刻。

http://www.dinnco.com/news/41498.html

相关文章:

  • 网站怎么做跳转安全百度搜一搜
  • 青海住房和城乡建设厅网站首页如何线上推广引流
  • 做商城网站需要什么天津seo培训
  • 十个最好的网站帆软社区app
  • 个人网站备案做论坛怎么做推广和宣传平台
  • 科技 公司 响应式 网站常州百度关键词优化
  • 酷 网站模板江门seo网站推广
  • 微信会员卡小程序济南seo关键词优化方案
  • 网站建设发展站长网站工具
  • 网站搭建和网页设计新闻发布稿
  • 海南营业执照代办qq群排名优化
  • 辽阳网站建设seo培训学院官网
  • 专做定制网站建设成人教育培训机构排名
  • 北京朝阳做网站上海专业网络推广公司
  • 生活服务网站开发怎么让百度快速收录网站
  • 如何借用别人静态网站做模板百度移动点击排名软件
  • wordpress 找回密码邮件错误seo优化网站推广
  • 免费ppt模板下载免费版百度云漯河seo推广
  • 手机平台网站开发百度网站搜索排名
  • 佛山电商网站制作it教育培训机构
  • 花卉网站建设策划书企业官网
  • 电脑可以做网站主机么杭州产品推广服务公司
  • 做网站后台的叫什么恩城seo的网站
  • 浙江住房与城乡建设厅官方网站查询湖南网站seo推广
  • 做外贸的怎么建立自己的网站百度热搜榜怎么打开
  • 阜新市建设学校官方网站html做一个简单的网页
  • 免费做图片的网站有哪些市场调研报告模板
  • 村网站开设两学一做栏目衡阳seo优化
  • 做招聘求职网站厦门百度广告
  • wordpress_主题seo是什么工作