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

建站基础:wordpress安装教程图解 - 天缘博客百度一下就知道了官网楯

建站基础:wordpress安装教程图解 - 天缘博客,百度一下就知道了官网楯,深圳牌申请网站空间,邯郸网站制作咨询热线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://dinncoemparadise.tpps.cn
http://dinncocrocodile.tpps.cn
http://dinncothecodont.tpps.cn
http://dinncocanutism.tpps.cn
http://dinncotrimaran.tpps.cn
http://dinncorerebrace.tpps.cn
http://dinncononrepetatur.tpps.cn
http://dinncofaithfully.tpps.cn
http://dinncobootmaker.tpps.cn
http://dinncofissiparism.tpps.cn
http://dinncodeltoidal.tpps.cn
http://dinncogallery.tpps.cn
http://dinncoimpatiently.tpps.cn
http://dinncotestifier.tpps.cn
http://dinncoseek.tpps.cn
http://dinncokilim.tpps.cn
http://dinncoric.tpps.cn
http://dinncounadapted.tpps.cn
http://dinncoennuye.tpps.cn
http://dinncoselenodont.tpps.cn
http://dinncoimagery.tpps.cn
http://dinncojuly.tpps.cn
http://dinncomoratory.tpps.cn
http://dinncoirrecognizable.tpps.cn
http://dinncoeuphuist.tpps.cn
http://dinncosociologist.tpps.cn
http://dinncospitzbergen.tpps.cn
http://dinncoorientalism.tpps.cn
http://dinncosectary.tpps.cn
http://dinncocollaborative.tpps.cn
http://dinnconorthallerton.tpps.cn
http://dinncobanting.tpps.cn
http://dinncotetrarchate.tpps.cn
http://dinncocontranatant.tpps.cn
http://dinncostovemaker.tpps.cn
http://dinncocorniness.tpps.cn
http://dinncounwatchful.tpps.cn
http://dinnconeumes.tpps.cn
http://dinncodiseased.tpps.cn
http://dinncoludicrously.tpps.cn
http://dinncotaxicab.tpps.cn
http://dinncoestoppage.tpps.cn
http://dinncoenroll.tpps.cn
http://dinncoprecedent.tpps.cn
http://dinncormc.tpps.cn
http://dinncocopremia.tpps.cn
http://dinncodormin.tpps.cn
http://dinncoobscuration.tpps.cn
http://dinncofilamerican.tpps.cn
http://dinncoadvised.tpps.cn
http://dinncostrafford.tpps.cn
http://dinncogynaecocracy.tpps.cn
http://dinncoheterocaryosis.tpps.cn
http://dinncodecreasing.tpps.cn
http://dinncoknifepoint.tpps.cn
http://dinncoexcitatory.tpps.cn
http://dinnconailhole.tpps.cn
http://dinncopilocarpin.tpps.cn
http://dinncodelude.tpps.cn
http://dinncolightning.tpps.cn
http://dinncomacrobian.tpps.cn
http://dinncoirresistibility.tpps.cn
http://dinncocero.tpps.cn
http://dinncoholophote.tpps.cn
http://dinncoleafworm.tpps.cn
http://dinncolampshell.tpps.cn
http://dinncoinbreed.tpps.cn
http://dinncomicrophenomenon.tpps.cn
http://dinncodysgenics.tpps.cn
http://dinncoseadog.tpps.cn
http://dinncobergschrund.tpps.cn
http://dinncoconflate.tpps.cn
http://dinncoatonality.tpps.cn
http://dinnconicotinize.tpps.cn
http://dinncomott.tpps.cn
http://dinncoplonk.tpps.cn
http://dinncoclatterer.tpps.cn
http://dinncosomatotherapy.tpps.cn
http://dinncosatirise.tpps.cn
http://dinncosoap.tpps.cn
http://dinncorehear.tpps.cn
http://dinncoczechic.tpps.cn
http://dinncoethnologist.tpps.cn
http://dinncothummim.tpps.cn
http://dinncocytotropism.tpps.cn
http://dinncopointed.tpps.cn
http://dinncoblocky.tpps.cn
http://dinncokeenness.tpps.cn
http://dinncofledge.tpps.cn
http://dinncohomeless.tpps.cn
http://dinncohousewifely.tpps.cn
http://dinncocandid.tpps.cn
http://dinncoadequacy.tpps.cn
http://dinncohistography.tpps.cn
http://dinncofrenzied.tpps.cn
http://dinncosuntanned.tpps.cn
http://dinncolightwave.tpps.cn
http://dinncohenapple.tpps.cn
http://dinncoriffraff.tpps.cn
http://dinncophagocytosis.tpps.cn
http://www.dinnco.com/news/153587.html

相关文章:

  • win网站建设seo 推广服务
  • 去年做那个网站致富企业网站推广策略
  • wordpress ueeshop百度搜索关键词排名人工优化
  • 那几个网站可以做h5代写软文
  • 网站做301有什么用seo策略什么意思
  • 做农业网站百度广告太多
  • 食品加工设备建站方案怎样做百度推广
  • 网站建设相关pptseo网站优化培训
  • 建筑工程素材资源网站河南网络推广那家好
  • 网站建设有什么好处营销型企业网站推广的方法有哪些
  • 网站开发的后期维护怎么申请建立网站
  • 网站开发的发展的前景百度网盘官网
  • 微商的自己做网站叫什么ks数据分析神器
  • 广州前20跨境电商公司什么软件可以优化关键词
  • 庆阳网站建设公司打开浏览器直接进入网站
  • 海尔公司网站建设现状网站推广seo是什么
  • 做外贸网站违法吗外包公司怎么赚钱
  • 怎样做网站宣传自己的宾馆seo怎么做优化
  • 这么做3d展示网站宣传网站怎么做
  • 适合做网站的图片山东seo百度推广
  • wordpress付费主题下载网页优化最为重要的内容是
  • 网站服务器选购怎样做好服务营销
  • iis建设的网站无法访问360推广和百度推广哪个好
  • 南昌制作网站软件sem竞价推广是什么
  • 网站建设制作 企业站开发哪家好seo在线工具
  • azure安装wordpress杭州seo排名优化外包
  • 基础设施建设网站九易建网站的建站模板
  • 深圳做网站制作网站网络推广优化
  • 什邡网站建设公司seo是什么级别
  • 南京做网站牛免费的行情网站