当前位置:   article > 正文

踩坑记录---openFeign高并发阻塞分析与解决_openfeign为什么不能高并发

openfeign为什么不能高并发

业务场景

公司某业务需要使用http协议去大量调用三方推送服务,因项目本身为Spring Cloud 服务,切通信框架为 OpenFeign,所以调用三方服务时,使用的是 OpenFeign

异常简述

某日上线一个新功能,从ID分发中心获取一个随机ID(openFeign服务化调用,QPS约1000左右),然后调用三方服务(openFeign调用,QPS约1000),上线之前的版本openFeign没有大并发,一切正常,上线之后,发现RabbitMQ存在大量阻塞积压,同时消费者利用率处于3%左右,严重影响业务处理能力

分析过程

  • 排查消费者业务处理线程池,通过编写ThreadPoolEndPoint,观察线程池运行状态,发现线程池队列处于积压状态
    在这里插入图片描述
  • 使用jstack命令,抓取线程运行状态
# 使用ps命令,获取运行进程编号,app.jar 为我的服务简化版名称
ps -ef|grep app.jar
# 假设获取的编号为 26599
jstack 26599 > 26599.jstack
# 下载并分析线程阻塞情况
sz 26599.jstack
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 使用IBM相关分析工具 JCA469,查看运行情况如下

在这里插入图片描述

大量线程处于阻塞情况,全部等待一个锁,疑似某些地方使用了同步处理导致无法高并发

  • 分析线程锁
    在这里插入图片描述
    发现大量线程openFeign卡在Jackson的编解码器这里,并且在执行Class.forName(),该方法是具有同步锁的,按道理来说,不应该存在并发调用的情况,按照堆栈进行分析发现是调用项目中的解码器配置导致,代码如下
	import feign.codec.Decoder;
 	/**
     * 补充 http 协议消息转换器
     * content-type:text/plan
     * content-type:text/json
     *
     * @param converter 自定义的消息转换器
     * @return openFeign 解码器
     */
    @Bean
    @ConditionalOnBean(TextMappingJackson2HttpMessageConverter.class)
    public Decoder feignDecoder(TextMappingJackson2HttpMessageConverter converter) {
        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(converter);
        logger.info("==================> 初始化openFeign text/json支持解析器");
        return new SpringDecoder(objectFactory);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 分析问题所在,按照堆栈执行流程分析,openFeign在处理解码时,关键源码如下
    在这里插入图片描述
    可以看到在 56行,是解码器自己new的处理逻辑,并调用我们配置的解码器,然后在不停的new HttpMessageConverters
    跟踪源码发现,在new HttpMessageConverters 过程中,会获取一个 default的转换器,而该default转换器的最终实现为 MappingJackson2HttpMessageConverter,就等于每次都在new MappingJackson2HttpMessageConverter ,每new 一次,都会执行一次jackson初始化配置
    在这里插入图片描述
    在这里插入图片描述
    每次执行jackson配置,都会去执行Class.forName去判断相关参数初始化
    在这里插入图片描述
    画重点,我的系统中,没有添加joda相关依赖,导致加载速度很慢,从而导致openFeign不支持高并发能力

解决问题

发现了问题所在,那么就能解决问题
不靠谱的方案一:
直接添加joda相关依赖

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4

优点:偷懒
缺点:鬼知道有什么其他影响,需要对每个业务进行通信测试,看看是否出现未知异常

靠谱的方案二:
问题的根本原因是在大量调用我们自己配置的解码器,导致不停初始化 jackson 配置
所以。。。。直接对解码器做个单例缓存,初始化一次就好了

	import feign.codec.Decoder;
	/**
	* 单例缓存
	*/
    private HttpMessageConverters messageConverters = null;
    /**
     * 补充 http 协议消息转换器
     * content-type:text/plan
     * content-type:text/json
     *
     * @param converter 自定义的消息转换器
     * @return openFeign 解码器
     */
    @Bean
    @ConditionalOnBean(TextMappingJackson2HttpMessageConverter.class)
    public Decoder feignDecoder(TextMappingJackson2HttpMessageConverter converter) {
        logger.info("==================> 初始化openFeign text/json支持解析器");
        if (messageConverters == null){
            messageConverters = new HttpMessageConverters(converter);
        }
        ObjectFactory<HttpMessageConverters> objectFactory = () -> messageConverters;
        return new SpringDecoder(objectFactory);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

结束

系统前后性能对比

机器 16c,64g
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/968466
推荐阅读
相关标签
  

闽ICP备14008679号