时间间隔与条数优先触发的批量数据处理

 高性能批处理代码

 当数据量达到一定数值或者时间达到一定间隔,任何一个满足条件,都将触发任务,任务完成之后再进行下一次条件判断,(能满足海量数据的批量传输和少量数据的较低延迟传输)

 条件1: 当时间达到了一定间隔 称为时间触发(自定义名称,方便理解后续代码)

 条件2: 当条数达到了一定数量,称为条数触发(自定义名称,方便理解后续代码)

 每一次触发计算后都会清空条件1的计时(重新计时),同时也会清空条件2的数量(重新计数)

重新计时有两种方式(1.强制精确计时,2.有一定误差计时,最大时间误差为一个时间触发的间隔)

方式1:  强制精确计时

    时间间隔的触发,一定是间隔指定时间而且指定间隔之间没有条数触发,每一次条数触发,一定是达到了最大条数, 对于时间触发,需要开启一个休眠时间任务,条数达到之后stop休眠任务,需要强制停止休眠的线程(强制停止线程可能会造成应该回收的连接没有回收,一些清理性的工作得不到完成),条数未达到,休眠结束,执行休眠后的任务处理代码,代码如下

  代码如下:

import org.apache.log4j.Logger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.function.Function;

/**
 *
 */

public class BatchExec<T> {

    private Logger logger = Logger.getLogger(this.getClass());
    public int maxCount;//批处理触发最大条数
    public int waitMilliSecond;//批处理触发最大等待时间
    public List<T> dataList;//批处理缓存数据
    public Thread timeJob;//时间触发任务
    public Function<List<T>, Object> function;//jdk1.8中的函数式编程,将方法当成参数
    public SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");//时间格式化

    /**
     * 默认初始化
     */
    public BatchExec() {
        this.dataList = new ArrayList<>(100);//保存数据使用
        this.function = result -> {
            logger.info(String.format("触发默认方法,数据长度:%s", result.size()));
            return result;
        };
        this.maxCount = 100;
        this.waitMilliSecond = 3000;
        this.timeJob = this.initThread();
        this.timeJob.start();
    }

    /**
     * 指定参数初始化
     *
     * @param maxCount
     * @param waitMilliSecond
     */
    public BatchExec(int maxCount, int waitMilliSecond, Function<List<T>, Object> function) {
        this.dataList = new ArrayList<>(maxCount);//使用连表
        this.function = function;
        this.maxCount = maxCount;
        this.waitMilliSecond = waitMilliSecond;
        this.timeJob = this.initThread();
        this.timeJob.start();
    }

    /**
     * 重置时间间隔任务任务
     *
     * @return
     */
    public Thread initThread() {
        Thread job = new Thread(() -> {//定义
            try {
                Thread.sleep(waitMilliSecond);
                waitMilliSecond();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        return job;
    }

    /**
     * 往集合中添加需要处理的数据
     *
     * @param data
     */
    public synchronized void add(T data) {
        this.dataList.add(data);
        if (this.dataList.size() == this.maxCount) {//触发
            timeJob.stop();
            exec("maxCount");
            //重置时间与数量
            this.timeJob = this.initThread();
            this.timeJob.start();
        }
    }

    /**
     * 达到一定时间触发任务
     * 达到时间执行与达到数量执行两个方法需要加锁,即waitMilliSecond 与 add
     */
    public synchronized void waitMilliSecond() {
        if (this.dataList.size() != 0) {//空数据
            exec("timeType");
        }
        //重置时间任务
        this.timeJob = this.initThread();
        this.timeJob.start();
    }

    /**
     * 任务执行并清空已经处理的数据
     *
     * @param type
     */
    public void exec(String type) {
        logger.debug(String.format("time :[%s], trigger type [%s]", getNowFormatDay(), type));
        function.apply(this.dataList);
        dataList.clear();//重置数据量
    }


    /**
     * 格式化获取当前时间
     *
     * @return
     */
    public String getNowFormatDay() {
        String dateString;
        Calendar cal = Calendar.getInstance();
        dateString = this.sdf.format(cal.getTime());
        return dateString;
    }

}

方式2: 有一定误差计时

    时间间隔的触发,并非严格的时间间隔,即时间间隔触发是在一到两个间隔之间肯定触发,每一次条数触发,一定是达到了最大条数, 对于非严格时间触发,需要开启一个单线程的循环时间休眠任务,休眠任务会判断两次休眠之间之间是否有条数达到的情况如果达到,不需要强制停止休眠的线程,只需要加一个if判断代码中记录两次休眠之间是否有数量触发的情况,如果有,即跳过任务处理逻辑,否则任务处理,代码如下

import org.apache.log4j.Logger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.function.Consumer;

/**
 * 批处理类
 * 时间间隔或数据量两个条件满足触发任务
 * 暂时没有用上这个类
 */
public class BatchCommon<T> {

    private Logger logger = Logger.getLogger(this.getClass());
    public int maxCount;//批处理触发最大条数
    public int waitMilliSecond;//批处理触发最大等待时间
    public List<T> dataList;//批处理缓存数据,有锁
    public Thread timeJob;//时间触发任务
    public Consumer<List<T>> function;//函数式编程,将方法当成参数,这个函数可以不用返回值,Function必须有返回值
    public SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");//时间格式化
    public boolean status = true;//两次休眠之间是否有达到条数触发,修改变量有锁,一定误差计时的误差范围是[0,最大等待时间]

    /**
     * 默认初始化
     */
    public BatchCommon() {
        this.dataList = new ArrayList<>(100);//保存数据使用
        this.function = result -> {
            logger.info(String.format("触发默认方法,数据长度:%s", result.size()));
        };
        this.maxCount = 100;
        this.waitMilliSecond = 3000;
        this.timeJob = this.initThread();
        this.timeJob.start();
    }

    /**
     * 指定参数初始化
     *
     * @param maxCount
     * @param waitMilliSecond
     */
    public BatchCommon(int maxCount, int waitMilliSecond, Consumer<List<T>> function) {
        this.dataList = new ArrayList<>(maxCount);//使用数组
        this.function = function;
        this.maxCount = maxCount;
        this.waitMilliSecond = waitMilliSecond;
        this.timeJob = this.initThread();
        this.timeJob.start();
    }

    /**
     * 重置时间间隔任务任务
     *
     * @return
     */
    public Thread initThread() {
        Thread job = new Thread(() -> {//定义
            try {
                while (true) {
                    Thread.sleep(waitMilliSecond);
                    if (this.isTimeExec()) waitMilliSecond();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        return job;
    }

    /**
     * 是否需要触发时间执行
     *
     * @return
     */
    public synchronized boolean isTimeExec() {
        if (this.status) {
            this.status = false;//更新当前时间时间检查没有出现条数触发
            return false;//上一个时间检查存在条数触发,不可以更新任务
        } else {
            return true;//没有条数触发,可以执行任务
        }
    }

    /**
     * 往集合中添加需要处理的数据
     *
     * @param data
     */
    public synchronized void add(T data) {
        this.dataList.add(data);
        if (this.dataList.size() == this.maxCount) {//触发
            this.status = true;//记录已经发生条数触发
            exec("maxCount");//条数触发
        }
    }

    /**
     * 达到一定时间触发任务
     * 达到时间执行与达到数量执行两个方法需要加锁,即waitMilliSecond 与 add
     */
    public synchronized void waitMilliSecond() {
        if (this.dataList.size() != 0) {//空数据
            exec("timeType");//时间触发
        }
    }

    /**
     * 任务执行并清空已经处理的数据
     *
     * @param type 触发任务类型
     */
    public void exec(String type) {
        logger.warn(String.format("time :[%s], trigger type [%s]", getNowFormatDay(), type));
        function.accept(this.dataList);
        dataList.clear();//重置数据量
    }

    /**
     * 格式化获取当前时间
     *
     * @return
     */
    public String getNowFormatDay() {
        String dateString;
        Calendar cal = Calendar.getInstance();
        dateString = this.sdf.format(cal.getTime());
        return dateString;
    }
}

主方法入口代码:

    public static void main(String[] args) throws InterruptedException {
        Consumer<List<String>> consumer = list -> {
            System.out.println(list.size());
        };
        BatchCommon<String> stringBatchCommon = new BatchCommon<>(2, 1000, consumer);
        while (true) {
            Thread.sleep(1000);
            stringBatchCommon.add("");
        }
    }

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小钻风巡山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值