package com.qiuguo.iot.box.websocket.api.runner;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * 基于Flux的动态批量运行任务（异步版）
 *
 * @param <T> the type parameter
 */
public class FluxBatchRunner<T> {
    private Scheduler scheduler;
    private int batchSize;
    private Consumer<Collection<T>> consumer;
    private Collection<T> records;

    public FluxBatchRunner(int batchSize, Consumer<Collection<T>> consumer) {
        if (batchSize <= 0) {
            throw new IllegalArgumentException();
        }
        Objects.requireNonNull(consumer);
        this.batchSize = batchSize;
        this.consumer = consumer;
        //scheduler = Schedulers.parallel();//创建CPU内核数量一样多线程池；
        //Schedulers.elastic() //无限制的弹性线程池,可以一直创建线程
        //Schedulers.boundedElastic() //有界的弹性线程池,它会回收闲置的线程，默认是60s;它对创建的线程数做了限制，默认值为CPU内核数x 10，达到上限后，最多可提交10万个任务；
        //Schedulers.fromExecutorService() 根据我们自定义线程池进行引用；
        //scheduler = Schedulers.single();//可重用单个线程
        scheduler = Schedulers.newSingle("webSendBox");//新的可重用单个线程
        records = new ArrayList<>(batchSize);
    }

    public FluxBatchRunner<T> setBatchSize(int batchSize) {
        this.batchSize = batchSize;
        return this;
    }

    public FluxBatchRunner<T> setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
        return this;
    }

    /**
     * 添加数据
     *
     * @param t the t
     */
    public void add(T t) {
        synchronized (records){
            records.add(t);
            if (records.size() == batchSize) {
                subscribeToMono(records);
                // reset
                records = new ArrayList<>(batchSize);
            }
        }


    }

    /**
     * Do final.
     */
    public void doFinal() {
        if (!records.isEmpty()) {
            subscribeToMono(this.records);
        }
    }

    private void subscribeToMono(Collection<T> records) {
        //subscribOn ： 指定指定调度器，它会让整个事件处理流程进入切换线程处理;需要注意的是：不管怎么切换调度器，他都是单线程执行
        Mono.just(records).subscribeOn(scheduler).subscribe(data -> consumer.accept(data));

        //需要多线程执行必须使用parallel+runOn 如下
        //Flux.just(records).parallel().runOn(scheduler).subscribe(data ->consumer.accept(data));
    }
}


