美文网首页
调用链服务端优化

调用链服务端优化

作者: 千牛卫中郎将 | 来源:发表于2020-01-15 21:03 被阅读0次

    一 背景

    1 调用链吞吐能力低,200w/min

    2 压测时,调用链kafka堆积上亿条消息,导致调用链延迟度达到小时级别

    3 压测试调用链服务full gc严重,每分钟30-50次

    4 随着公司业务增长,线上调用链消息在日益增长,线上调用链为每天1T左右


    二 优化前调用链架构

    image

    优化前的项目主要具备以下功能:

    1. 暴露日志收集dubbo接口,接收调用链日志上报

    2. 消费kafka日志,多线程处理后写入到es


    三 问题分析

    优化前服务存在如下性能问题:

    1 IO性能瓶颈

    多个IO密集型功能点(kafka拉取,es写入,dubbo请求处理)耦合在一个服务中,导致IO性能成为系统性能的主要瓶颈点之一

    2 Synchronized代码性能问题

    代码在kafka拉取线程和写入es的线程之间交换数据的集合是传统的非线程安全的LinkedList,为了保证线程安全使用了synchronized修饰,导致方法的吞吐量上限很低

    3 GC问题

    在处理峰值时期,full gc次数为30-50/min,导致full gc频繁主要有如下原因:

    3.1 Kafka版本过低,不能指定拉取条数,只能指定拉取大小,由于调用链日志每条日志上限为10M,所以每个partition拉取值为10M,每个topic有12个partition,导致每次会拉取上万条日志到内存中,超过jvm 大对象分配2M的阈值,直接分配到老年代,导致老年代频繁回收,出发full GC

    3.2 Kafka消费服务,java对象有大量的被创建,需要被快速回收,不需要晋升等特点,jvm默认的Young,Old分配比例,及Young中的Eden Survior默认分配比例不能满足本服务对GC的要求

    3.3 代码实现上,为了实现异步处理,为每个日志对象创建了一个Runnable对象,导致内存的对象翻倍,加重了GC的压力


    四 解决方案

    针对上面问题分析的结果,提出一下解决方案:

    1 代码优化,引入disruptor代替Synchronized

    2 Jvm优化,解决GC问题

    代码优化

    1. 为什么要使用disruptor?

     调用链服务消费kafka写入ES,天然符合生产消费者模型

     Disruptor高性能的异步处理框架,无锁(CAS)环形队列

     Disruptor的生产消费线程完全相互透明,生产消费代码解耦

     生产消费线程个数,等待策略,RingBuffer大小均由参数指定,极易扩展

    2. Disruptor和ArrayBlockQueue的性能对比

    主要生产消费模型:

    image

    各个生产消费模型下性能对比:

    image

    3. disruptor的性能为什么那么高?

     生产消费线程竞争基于CAS,无锁竞争

     解决CPU缓存行的伪共享问题,使部分变量的读取远高于内存读取

    4. 调用链中disruptor主要参数的指定

    Jvm 优化

    | Tables | Are | Cool || ------------- |:-------------:| -----:|| col 3 is | right-aligned | 1600 || col 2 is | centered |12 || zebra stripes | are neat | $1 |

    全部参数对比

    image

    差异参数对比:


    五 优化效果

    吞吐能力

    image

    服务器配置(下降一半)

    image

    相关文章

      网友评论

          本文标题:调用链服务端优化

          本文链接:https://www.haomeiwen.com/subject/cllxzctx.html