使用Feign出现空指针异常

说明:本文记录一次偶然出现的空指针异常,在微服务架构中,一个服务在调用另一个服务时,出现了空指针异常。

业务描述:在做订单超时功能时,大家都知道,可以使用RabbitMQ延迟队列,下单的同时给队列发送一个延迟消息(消息的内容是订单号),比如延迟10分钟。10分钟之后,该消息被消费者监听到,会根据该订单ID查询数据库,看该订单的状态是否为已支付,是则忽略,否则取消该订单,恢复商品库存等等其他操作,然而此时出现了空指针异常,消息未被消费,被路由到死信队列中。

(微服务调用报空指针异常)

在这里插入图片描述

(消息被路由到死信队列)

在这里插入图片描述

如下图的第三步:

在这里插入图片描述

分析

首先排除FeignClient的问题,因为下单减少库存,取消订单恢复库存,我使用的是同一个接口,只是修改了商品的正负数,不可能出现下单时可以,取消订单时再使用就报错。

(controller层代码)

    /**
     * 根据ID更新商品库存
     * @param id
     * @param num
     */
    @PutMapping("/update/{id}/{num}")
    public void updateStockById(@PathVariable("id") Long id, @PathVariable("num") Integer num){
        itemService.updateStockById(id,num);
    }

(service层代码)

    @Override
    public void updateStockById(Long id, Integer num) {
        if (!ObjectUtil.isAllNotEmpty(id, num)) {
            System.out.println("参数不能为空");
        }

        if (id < 0 || num < 0) {
            System.out.println("参数非法");
        }

        update().setSql("stock = stock + " + num).eq("id", id).update();
    }

其次,再思考会不会不是因为Feign的调用报错,而是微服务之间有什业务产生的报错。于是,我找到了拦截器

为了保证用户登录后,经过Gateway(网关)后,信息可以被下游服务获取到,我的代码中是使用MVC拦截器+Feign拦截器实现的,如下图:

在这里插入图片描述

每个服务会有两个拦截器,分别把服务接收到的请求,发出的请求拦截到,然后分别解析用户信息,添加用户信息到请求头,以此达到参数透传,用户信息可在微服务之间流传。

MVC拦截器代码(获取请求头中用户的ID,存到ThreadLocal中)

public class AuthorizationInterceptor implements HandlerInterceptor {
    /**
     * 收到请求会执行的方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String id = request.getHeader("authorization");

        if (id != null && id != ""){
            long l = Long.parseLong(id);
            TokenThreadLocal.set(l);
        }else {
            responseHandler(response);
            return false;
        }

        // 放行
        return true;
    }
    ……
}

Feign拦截器(将本服务中的ThreadLocal中的用户ID再设置到请求头上)

/**
 * 发送请求拦截器
 */
@Slf4j
public class AuthorizationRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("authorization",TokenThreadLocal.get().toString());
    }
}

排查

给这两个地方分别打上断点,等订单超时后进入拦截器的代码,排查一下;

断点来到Feign拦截器,选中这行代码一看,原来是这里报了空指针异常

在这里插入图片描述

再一看,原来是TokenThreadLocal.get().toString()这里是空的;

在这里插入图片描述

然后恍然大悟,MQ发送消息是异步请求,ThreadLocal本地线程池对象,自然为空;

解决

很自然的想到一种很简单的解决方法,发送消息的时候把ThreadLocal中的值(用户ID)也给发到延迟队列中,然后在消费者监听的代码里面,再使用ThreadLocal的set()方法,把用户ID设置到线程池中;

把订单ID、用户ID封装成一个Map,转为json格式发送到延迟队列里;

在这里插入图片描述

消费者代码这边,使用ThreadLocal的set()方法,把用户ID再设置进去;

在这里插入图片描述

启动,测试下单,等待订单超时,清理超时订单,进入断点,问题解决!

在这里插入图片描述

总结

这是一个非常隐蔽的异常,因为设置了死信队列,未被成功消费的消息会被路由到死信队列中,程序并不会报错,并且因为订单表的内容大部分是在订单服务中,此异常仅仅会影响订单被取消后,调用商品服务恢复商品库存数量这一个很小的功能未能执行,要排除出来是非常困难的。

而问题原因,概括来说,是因为ThreadLocal的值不能在RabbitMQ的消息中传递,导致在使用拦截器获取ThreadLocal值的时候报了空指针异常

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/49118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二、SQL-6.DCL-2).权限控制

*是数据库和表的通配符&#xff0c;出现在数据库位置上表示所有数据库&#xff0c;出现在表名位置上&#xff0c;表示所有表 %是主机名的通配符&#xff0c;表示所有主机。 e.g.所有数据库&#xff08;*&#xff09;的所有表&#xff08;*&#xff09;的所有权限&#xff08;a…

关于Docker的基本概念和使用

关于Docker的基本概念和使用 一、Docker 概述1、Dcker的概念2、容器的优势3、Docker与虚拟机的区别4、容器在内核中支持2种重要技术5、Docker核心概念 二、安装 Docker1、安装依赖包2、设置阿里云镜像源3、安装 Docker-CE并设置为开机自动启动3、查看 docker 版本信息4、docker…

程序员,必须要知道的热门开源项目!

&#x1f3c6; 文章目标&#xff1a;了解热门开源项目 &#x1f340; 入门篇&#xff1a;程序员,必须要知道的热门开源项目! ✅ 创作者&#xff1a;熊猫Jay ✨ 个人公众号: 熊猫Jay字节之旅 (文末有链接) &#x1f341; 展望&#xff1a;若本篇讲解内容帮助到您&#xff0c;请帮…

Java并发编程面试题

Author 郑金维 并发编程面试题1 一、原子性高频问题&#xff1a; 1.1 Java中如何实现线程安全? 多线程操作共享数据出现的问题。 锁&#xff1a; 悲观锁&#xff1a;synchronized&#xff0c;lock乐观锁&#xff1a;CAS 可以根据业务情况&#xff0c;选择ThreadLocal&am…

PHP 3des加解密新旧方法可对接加密

一、旧3des加解密方法 <?php class Encrypt_3DES {//加密秘钥&#xff0c;private $_key;private $_iv;public function __construct($key, $iv){$this->_key $key;$this->_iv $iv;}/*** 对字符串进行3DES加密* param string 要加密的字符串* return mixed 加密成…

配置IPv4 over IPv6隧道示例

IPv4 over IPv6隧道&#xff1a; 在IPv4 Internet向IPv6 Internet过渡后期&#xff0c;IPv6网络被大量部署后&#xff0c;而IPv4网络只是散布在世界各地的一些孤岛。利用隧道技术可以在IPv6网络上创建隧道&#xff0c;从而实现IPv4孤岛的互联&#xff0c;IPv4孤岛能通过IPv6公…

基于STM32CubeMX和keil采用STM32F407的基本定时器中断实现LED闪烁

文章目录 前言1. 电路原理图理解2. 基本定时器2.1 STM32定时器中断的流程&#xff1a;2.2 部分参数详解2.2.1 时钟源2.2.2 预分频系数2.2.3 自动重装载值 3. STM32CubeMX参数配置3.1GPIO配置3.2 时钟配置3.2 配置定时器相关参数3.3 Debug配置3.4 中断配置3.5 代码生成 4. keil代…

关于Java中的Lambda变量捕获

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 目录 一、Lambda表达式语法 二、Lambda中变量捕获 一、Lambda表达式语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } Lambda表达式由三部分组成&a…

小米新专利曝光:解决升降摄像头痛点,隐藏式摄像头指日可待

根据国家知识产权局官方网站的最新消息&#xff0c;小米移动软件公司的“摄像头隐藏式电子设备及隐藏式摄像头”的专利申请在今天获得了授权。 这款电子设备的主要组成部分包括壳体、摄像模组和可伸缩的反射组件。壳体上设有一个开口&#xff0c;可以让反射组件向外伸出。反射组…

美容店预约小程序制作教程详解

现在&#xff0c;制作一个专属于美容店的预约小程序不再需要编程经验&#xff0c;通过乔拓云网提供的后台管理系统&#xff0c;你可以轻松地完成整个制作过程。下面&#xff0c;我将为你详细介绍如何DIY一个美容店预约小程序。 首先&#xff0c;登录乔拓云网的后台管理系统&…

redis到底几个线程?

通常我们说redis是单线程指的是从接收客户端请求->解析请求->读写->响应客户端这整个过程是由一个线程来完成的。这并不意味着redis在任何场景、任何版本下都只有一个线程 为何用单线程处理数据读写&#xff1f; 内存数据储存已经很快了 redis相比于mysql等数据库是…

Qt完成文本转换为语音播报与保存(系统内置语音引擎)(二)

一、前言 随着人工智能技术的不断发展,语音技术也逐渐成为人们关注的焦点之一。语音技术在很多领域都有着广泛的应用,例如智能家居、智能客服、语音识别等等。其中,语音转文字技术是语音技术中的一个重要分支,它可以将语音转换成可编辑的文本,为人们的生活和工作带来了更…

中缀表达式转后缀表达式,使用逆波兰计算。可以计算小数

1、使用方法 传递一个分开保存符号与数字的List即可&#xff1a;List SumNumber; 获取参数的构造方法如下&#xff1a; public ReversePolish(List<String> sumNumber) {SumNumber sumNumber;}要求的List保存数据的方式如下&#xff1a; 例如&#xff1a;123 然后使用…

MFC表格控件CListCtrl的改造及用法

1、目的 简单描述MFC的表格控件使用方法。Qt适用习惯了以后MFC用的比较别扭&#xff0c;因此记录一下以备后续复制代码使用。由于MFC原生的CListCtrl比较局限&#xff0c;比如无法改变表格的背景色、文字颜色等设定&#xff0c;因此先对CListCtrl类进行重写&#xff0c;以便满足…

Jenkins 拉取 GitHub 私有仓库失败问题

添加仓库的时候提示 stderr: fatal: Cannot prompt because user interactivity has been disabled. 把在 GitHub账户设置中生成的个人访问令牌填到地址里

第54步 深度学习图像识别:MLP-Mixer建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;MLP-Mixer MLP-Mixer&#xff08;Multilayer Perceptron Mixer&#xff09;是Google在2021年提出的一种新型的视觉模型结构。它的主要特点是完全使用多层感知机&#xff08;MLP&#xff09;来处理图像&#…

Docker Compose(九)

一、背景&#xff1a; 对于现代应用来说&#xff0c;大多数都是通过很多的微服务互相协同组成一个完整的应用。例如&#xff0c;订单管理、用户管理、品类管理、缓存服务、数据库服务等&#xff0c;他们构成了一个电商平台的应用。而部署和管理大量的服务容器是一件非常繁琐的事…

Sentinel Dashboard集成Nacos

1.前言 当项目上Sentinel Dashboard做流量监控的时候&#xff0c;我们可以通过Sentinel控制台修改限流配置&#xff0c;但当我们使用Nacos作为配置中心动态配置流控规则的时候&#xff0c;问题就来了。 首先我们要明白&#xff0c;Sentinel Dashboard的配置是从机器的内存中加…

【kubernetes系列】flannel之vxlan模式原理

概述 在Kubernetes中要保证容器之间网络互通&#xff0c;网络至关重要。而Kubernetes本身并没有自己实现容器网络&#xff0c;而是而是借助CNI标准&#xff0c;通过插件化的方式自由接入进来。在容器网络接入进来需要满足如下基本原则&#xff1a; Pod无论运行在任何节点都可…

运算放大器--------加减运算电路

反向求和运算电路 电路 公式 同向加法运算电路 电路 公式 加减运算电路 分别求正向输入的输出和反相输入的输出&#xff0c;然后求和就可以得到到最终的输出。 切记&#xff0c;虚短虚断不是真正的断路和短路。