springboot+异步线程池使用

        今天在调试流量采集信息的时候, 发现异步线程池不管用,无论怎么修改都无法启自定义的线程池,后来发现,springboot注解的方法,必须还是需要注入对象的方式调用,标准方式如下

* 一、异步方法使用static修饰
* 二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
* 三、异步方法不能与被调用的异步方法在同一个类中
* 四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
* 五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
* 六,不能有返回值,方法是 void 类型

标红色特别坑,同一个类中调用异步方法无效,调用者和被调用者都在一个类中 

 System.out.println(Thread.currentThread().getName());  打印当前自定义线程名称是否正确

    @Autowired
    SNMPItem snmpitem;

第一步,配置线程池

@Configuration
@EnableAsync
class TaskPoolConfig{
		@Bean("taskExecutor")
		public Executor taskExecutor(){
			ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
			executor.setCorePoolSize(25);//核心线程数25:线程池创建时候初始化的线程数
			executor.setMaxPoolSize(50);//最大线程数50:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
			executor.setQueueCapacity(500);//缓冲队列200:用来缓冲执行任务的队列
			executor.setKeepAliveSeconds(60);//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
			executor.setThreadNamePrefix("taskExecutor-");// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
			//* 线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute
			//      方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务*//*
			executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
			executor.setWaitForTasksToCompleteOnShutdown(true);
			executor.setAwaitTerminationSeconds(60);
			executor.initialize();
			return executor;
		}

	}

说明:setWaitForTasksToCompleteOnShutdown(true)该方法就是这里的关键,用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁。同时,这里还设置了setAwaitTerminationSeconds(60),该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。事实我也碰过redis关闭时,报错

第二步,定时任务去启动多线程

重点在这个方法上 snmpitem.getFlow(snmpItemtask); 因为异步注入线程从这个方法开始

 @Scheduled(cron = "0/50 * * * * ?")//10秒执行 测试使用
   // @Scheduled(cron = "0 0/5 * * * ?")
    public void taskCollectinit() {
    	
     /*考虑网络不同和redis数据不一致总是,设置本地变量暂存数据*/
        
        String tasklist = redisTemplate.opsForValue().get(SNMPItem.localIp);
        if (null == tasklist || "".equals(tasklist)) {
            System.out.println("redis没有拿到任务列表");
            return;
        }
        // 每个节点的任务服务IP,通过后台项目distributed_traffic_center_management,分配获得
        JSONArray jsonarray = JSONArray.parseArray(JSON.parseObject(tasklist.toString(), Object.class).toString());
        System.out.println("tasklist : "+jsonarray.toString());
       // rdlist.add(redisObj);
       for (int i = 0; i < jsonarray.size(); i++) {
            JSONObject rd = jsonarray.getJSONObject(i);
            if (rd.get("port") != null && !"".equals(rd.get("port"))) {
                String ip = rd.getString("ip");
                int port = rd.getInteger("port");
                String community = rd.getString("community");
                String id = rd.getString("id");
                int poll_time = rd.getInteger("poll_time");

                SNMPItemUtil snmpItemtask = new SNMPItemUtil();
                snmpItemtask.setCommunity(community);
                snmpItemtask.setPort(port);
                snmpItemtask.setIP(ip);
                snmpItemtask.snmpListen();
                snmpitem.getFlow(snmpItemtask);
            }
       }

    }

第三步  这里跟业务相关的操作了,这一步涉及内容较多,仍然进可以使用多线程


    @Async("taskExecutor")
    public void getFlow(SNMPItemUtil snmputil) {
        List<Object[]> flowList = new Vector<Object[]>();
        boolean taskTry = true;
        boolean bRet = snmputil.getIfFlow(snmputil.getIP(), flowList, true); // 取流量信息.
        if (!bRet) {
           // System.out.println("取流量失败:" + ip);
            taskTry = false;
        }
       //尝试失败
        if (!taskTry){
            taskNodefail(snmputil.getIP()+"_"+snmputil.getCommunity()+"_"+port+"_"+localIp);
        } else {

            FlowRRDV2 rrd = new FlowRRDV2();  //flowRRDV2
            rrd.setRedisTemplate(redisTemplate);
            rrd.setKafkaProducer(kafkaProducer);

            rrd.setPoll_time_flag(this.poll_time_flag);
            if (this.poll_time == 1)
                rrd.setMin(this.poll_time);
            for (Object[] item : flowList) {
                if (bInsert)
                    rrd.setDeviceInfo(id, item[0].toString());
                // long inOctets = (Double) item[1];
                // long outOctets = (Double) item[2];
                long inOctets = Long.parseLong(item[1].toString());
                long outOctets = Long.parseLong(item[2].toString());
                // 导入数据 需要改装成批量导入

                try {
                    //  System.out.println("------------2.0--- MASTER--" + id + "--" + item[0].toString() + "---" + Util.getTimestamp(new Date()) + "----" + inOctets + "---" + outOctets);
                    if (bInsert && (inOctets >= 0 || outOctets >= 0))
                        rrd.insertAll(Util.getTimestamp(new Date()) - 10,
                                Util.getTimestamp(new Date()), inOctets, outOctets, ip);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

 

第四步,也是测试方法 必须注入的对象,然后调用才有效果

@Autowired
askSnmpFlow  taskSnmpFlow;


 @Test
  public  void taskAysc(){
    	//for(int i=0;i<2;i++) {
	       taskSnmpFlow.taskCollectinit();
    	//}
  }

配置类的方式使用

@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
    /**
     *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
     *	当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
     *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
     *
     *
     *  注意事项:
     * 如下方式会使@Async失效
     *
     *
     * 一、异步方法使用static修饰
     * 二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
     * 三、异步方法不能与被调用的异步方法在同一个类中
     * 四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
     * 五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
     *
     */

    /** 核心线程数(默认线程数) */
    private static final int    corePoolSize     = 20;
    /** 最大线程数 */
    private static final int    maxPoolSize      = 100;
    /** 允许线程空闲时间(单位:默认为秒) */
    private static final int    keepAliveTime    = 10;
    /** 缓冲队列大小 */
    private static final int    queueCapacity    = 200;
    /** 线程池名前缀 */
    private static final String threadNamePrefix = "Async-Service-";

    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);

        // 线程池对拒绝任务的处理策略
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }

}

最后还是有内存溢出问题, 总结来就是必须跟踪 执行的对象有没有回收 特别是new 对像的时候,

  对于资源的管理,必须有关闭  项目中就是snmp new出来后没有关闭回收 造成内存一直在升高。导致程序崩掉

另外在启动时调用任务 主线程会阻塞  必须开子线程


@Component
public class StartPingService implements CommandLineRunner{

	@Autowired
	Ping ping;
	
	@Override
	public void run(String... args) throws Exception {
		// TODO Auto-generated method stub
		 new Thread(()-> {
			 ping.pingStart();
		 }).start();;
	}

 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值