女娲补天 <1> -- Executors导致的内存溢出

一些介绍

本人负责广告项目的相关开发工作,很遗憾没有加入到广告项目最初的开发,现在只是在前人的基础上做一些优化和维护的工作,但是随着微信功能的调整,相信后面会有很多的开发任务。

灾难发生

正如文章标题所示,本人最近在修复一个粉丝采样的BUG,问题在于当粉丝采样数量达到两千万以上之后会导致采样任务的失败,经过反复的排查,最后确定是因为堆内存溢出导致的服务挂掉。

项目相关

项目相关技术:

  1. SpringBoot:1.5.16

  2. MySQL:5.6

  3. ElasticSearch:5.3.3

服务器配置:

  1. 系统:Ubuntu16.04

  2. CPU数量:8核

  3. 内存:32G

具体

项目的流程很简单,就是运营提交采样任务后,根据筛选条件从ElasticSearch中分页查询相关的粉丝数据,然后分批次调用微信推送接口给用户推送相关的文章。实现细节中有一条:我每次取ES中的一万条数据进行推送,然后将这一万个粉丝打上标签来避免重复推送。由于微信推送接口和ES更新的操作都是异步执行的,而程序中用了Executors创建的线程池来执行任务:


private Executor updateFansBatchExecutor = Executors.newFixedThreadPool(10);

Executors有一个很坑的地方就是当线程池满了之后,后续的任务会进入一个无限制的阻塞队列,所以当任务越来越多的时候,Executors就将内存撑爆了导致服务挂掉。

所以需要认为的进行一些限制,并使用ThreadPoolExecutor:


public static ThreadPoolExecutor getThreadPoolExecutor(

            int corePoolSize,

            int maxiumPoolSize,

            String name,

            RejectedExecutionHandler handler) { // 当maxiumPoolSize也满了之后的策略

        corePoolSize = corePoolSize < 1 ? 1 : corePoolSize;

        maxiumPoolSize = maxiumPoolSize < 1 ? 1 : maxiumPoolSize;

        return new ThreadPoolExecutor(

                corePoolSize,

                maxiumPoolSize,

                0L, TimeUnit.MILLISECONDS,

                new LinkedBlockingQueue<>(1024),

                new ThreadFactoryBuilder().setNameFormat(name).build(),

                handler

        );

    }

  • corePoolSize: 表示线程池的大小

  • maxiumPoolSize: 表示当线程池和阻塞队列都满了之后能够继续放的任务数量

  • new LinkedBlockingQueue<>(1024): 表示阻塞队列的大小,这里我设置成1024个

  • RejectedExecutionHandler:表示当maxiumPoolSize满了之后处理任务的策略,有以下几种策略:

  1. CallerRunsPolicy: 在当前线程中执行任务(当前线程指将任务提交到线程池的那个线程)

  2. AbsortPolicy:拒绝执行任务并抛出RejectedExecutionException异常

  3. DiscardPolicy:不执行任务,和AbsortPolicy的区别在于不抛异常

  4. DiscardOldestPolicy:接收任务并且抛弃掉最老的没有执行的任务

这样就保证了线程池的大小不会内存溢出。

你可能感兴趣的:(女娲补天 <1> -- Executors导致的内存溢出)