欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

大流量场景下统计问题整理

程序员文章站 2022-04-19 20:25:45
...

前言

电视剧《恰同学少年》中,给我印象最深的就是杨昌济先生晨读之前大声朗读的两句话:

“杨昌济,时光易逝,汝当惜之;先贤至理,汝当常忆。”

人生苦短,汝当惜之。向圣贤学习,时常警惕、自审,整理今日所获如下。

背景

流量分析过程中,有一个定时统计任务,对网络请求数据中的源IP、目的IP、访问总量进行统计,得到每个源IP,在一天中的各个小时访问的目的IP、及访问这些目的IP的总量。统计结果作为为一条记录,存入Sequoiadb数据库中。统计结果的实体定义如下:

 public class SourceIpVisitSum {
    private String _id;

    //统计时间
    private String date;

    //统计类型
    private String type;

    //源IP访问数据topN
    private List<SourceVisitInfo> topN;
}

public class SourceVisitInfo implements Comparable<SourceVisitInfo>{
    /**
     * 源IP
     */
    private String srcIp;

    /**
     * 访问总流量数
     */
    private Long trafficTotal;

    /**
     * 该源IP访问的目的IP集合:不存储xml,实时生成xml
     */
    private List<String> destIps;

    /**
     * 该源IP访问过的目的IP的统计结果
     */
    private List<BSONObject> visitServers;

    /**
     * 该该源IP户当天的访问趋势
     */
    private List<TrafficTrendData> trendData;

    /**
     * 日汇总的数据:每天的这个属性,汇集就是周、月的数据了
     */
    private TrafficTrendData dailyTotal;

}

public class TrafficTrendData {
    //统计时间:小时或者天
    private String time;

    //该时间段内的流量总数
    private Long trafficTotal;
}

抽象这三个实体,统计一天的流量数据中各个源IP的访问趋势情况,以JSON格式存储入库。业务需求实时展示访问情况,所以开启了一个每隔5分钟统计一次的Quartz定时任务,页面定时获取最新的统计结果进行展示。

大流量场景下统计异常

开发过程中进行过测试,随机插入700万条的记录,源IP,目的IP总量不过40个,做一次实时统计操作耗时一分钟,没有出现过定时任务积压的情况。

但是,在真实流量数据场景下,500万条数据统计过程,耗时40分钟。统计结果显示有一万多个源IP,一千多个目的IP,访问关系排列总数较多,统计得到的SourceIpVisitSum对象的JSON数据大小达到了34M。而Sequoiadb单条插入的BSON大小限制为16M。

最直接的结果就是,这个每5分钟一次的统计任务,真正耗时一个多小时,而且统计得到结果准备入库的时候,插入异常,log4j记录下大量的异常日志,其中主要的就是这个JSON参数信息。今天看昨天的log4j的日志文件,从18:02开始第一次定时任务,到今天凌晨,日志文件达到了348M。

而今天凌晨开始的产生的日志文件更是夸张,日志大小达到了2.7G。

大流量场景下统计问题整理

而今天的日志文件,EditPlus根本打不开,用了一个大文件工具查看,全是这个统计结果的JSON信息。再加上其他的异常信息,累计就到了这个级别了。还真吓倒我了,从来没有见到过这么大的日志量。

定时任务并行问题

由于开始用的是Quartz的并行定时任务,每隔5分钟一次,但是一次耗时40分钟的任务,导致Quartzs的工作线程很快就被这一个类型的定时任务占满了,其他正常任务就无法执行了。

解决办法,将这个定时任务设置成不能并行的,必须等上一次任务执行完成后再进行下一次任务。其实Quartz提供了StatefulJob接口,它被废弃了,被注解@DisallowConcurrentExecution替代。只要这样一行代码就可以让它们顺次执行了。

但是,我用了一个笨方法,用Java的线程池的scheduleAtFixedRate方法,多写了至少30行代码,这点,我不能释怀,脑子再灵活一点搜一下Quartz的非并行任务就好了。

统计伴随的高CPU消耗

再说说这个统计过程吧,由于Sequoiadb的聚集操作相当耗时,所以采用的是逐条遍历原始流量数据,然后用Java的Map和List进行统计处理方式。

现实场景下,源和目的IP量大且组合总量众多,统计操作里面的Map和List将需要频繁扩容,而我初始化的Map的容量是128,而真实的源IP个数为一万多个,那么这么Map的扩容会相当频繁;其次,就是每个源IP的目的IP情况,初始化为64,而真实的目的IP也有一千多个,那么也会频繁扩容。

这样就出现了一种现象:每次该任务执行时,过一会就开始出现好几个高CPU消耗的线程,而且线程ID相互挨着:
大流量场景下统计问题整理
打出堆栈日志,除了第一个是这个统计任务线程,其他高CPU消耗的线程都是JVM的GC线程。

降低GC消耗,采取的方式是:修改JVM参数,因为原来的tomcat的启动参数配置的xmx为512M,太小了,目标服务器的内存是32G,所以这可能是导致GC频繁的原因。稍微调大到2G后,这种现象消失。

JAVA_OPTS=”-server -Xms512m -Xmx2048m -XX:PermSize=512M -XX:MaxPermSize=512m”

启示录

那个2.7G的日志文件,VPN传送了一上午只传了76%就终止了。用打文件打开工具,逐行滚动,查看它的内容,结果发现,全是统计JSON,终于知道这个大日志是怎么回事了。

这样一个大文件,直接用vi打开,黑屏上全是闪烁的01形象,看得人眼晕。还好,我够有耐心,混迹于1048576的世界里,我依旧只有一个感受:编程路漫漫,我还在路上。

备注:偶然发现1048576,这个数字串是如此的有趣,1024*1024=1024K。

相关标签: 项目开发