All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.schedulerx.worker.batch.BaseReqHandler Maven / Gradle / Ivy

There is a newer version: 1.12.2
Show newest version
package com.alibaba.schedulerx.worker.batch;

import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.schedulerx.worker.container.ShutdownMode;
import com.alibaba.schedulerx.worker.log.LogFactory;
import com.alibaba.schedulerx.worker.log.Logger;
import com.alibaba.schedulerx.worker.master.TaskMasterPool;

/**
 * used for every parallel/Grid job instance
 * every parallel/Grid task master has a BaseReqHandler, a BaseReqHandler will
 * batchly retrieve reqs then merge these reqs into a batch request for hugely reducing network reqs number;
 * @author yanxun on 2019/1/5.
 */
public abstract class BaseReqHandler {
    private long jobInstanceId;
    private int coreBatchThreadNum;
    private int maxBatchThreadNum;
    private int batchSize;
    private String batchProcessThreadName;
    private String batchRetrieveThreadName;
    private ReqQueue reqsQueue;
    private Thread batchRetrieveThread;
    protected TaskMasterPool taskMasterPool = TaskMasterPool.INSTANCE;
    protected ThreadPoolExecutor batchProcessSvc;
    protected long defaultSleepMs = 500;
    protected long emptySleepMs = 1000;
    private volatile T latestRequest;
    private static final Logger LOGGER = LogFactory.getLogger(BaseReqHandler.class);

    protected Long dispatchDelay;

    private volatile AtomicBoolean running = new AtomicBoolean(true);
    /**
     *  onging runnable number, every subclass implement process method should decrement this value when a job was done.
     */
    protected AtomicInteger activeRunnableNum = new AtomicInteger(0);

    public BaseReqHandler(long jobInstanceId, int coreBatchThreadNum, int maxBatchThreadNum, int batchSize,
                          ReqQueue queue, String batchProcessThreadName, String batchRetrieveThreadName) {
        this.jobInstanceId = jobInstanceId;
        this.coreBatchThreadNum = coreBatchThreadNum;
        this.maxBatchThreadNum = maxBatchThreadNum > coreBatchThreadNum ? maxBatchThreadNum : coreBatchThreadNum;
        this.batchSize = batchSize;
        this.batchProcessThreadName = batchProcessThreadName;
        this.batchRetrieveThreadName = batchRetrieveThreadName;
        reqsQueue = queue;
    }

    public void start() {
        batchProcessSvc = new ThreadPoolExecutor(
            coreBatchThreadNum, maxBatchThreadNum, 30, TimeUnit.SECONDS,
            new LinkedBlockingQueue(10240), new ThreadFactory() {
            private final AtomicInteger nextId = new AtomicInteger(1);
            private final String namePrefix = batchProcessThreadName + jobInstanceId + "-";
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, namePrefix + nextId.getAndIncrement());
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());
        batchProcessSvc.allowCoreThreadTimeOut(true);
        batchRetrieveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!Thread.currentThread().isInterrupted()) {
                        List reqs = asyncHandleReqs();
                        LOGGER.debug("jobInstanceId={}, batch retrieve reqs, size:{}, remain size:{}, batchSize:{}",
                            jobInstanceId, reqs.size(), reqsQueue.size(), batchSize);
                        if (reqs.size() < batchSize*0.8f){
                            if (reqs.isEmpty()) {
                                // no element in reqs, sleep a while for aggregation
                                if (running.get()) {
                                    Thread.sleep(emptySleepMs);
                                } else {
                                    // handler状态已经停止则退出线程
                                    break;
                                }
                            } else {
                                // not reach expect batch size, sleep a while for aggregation
                                Thread.sleep(defaultSleepMs);
                            }
                        }
                        if (dispatchDelay != null) {
                            // 支持限速分发
                            Thread.sleep(dispatchDelay);
                        }
                    }
                } catch (InterruptedException ie) {
                    // ignore
                } catch (Throwable e) {
                    LOGGER.error(e);
                }
            }
        }, batchRetrieveThreadName + jobInstanceId);
        batchRetrieveThread.start();
    }

    /**
     * logic implemented by subclass for processing this batch of reqs
     * @param jobInstanceId id of job instance which these reqs belong to.
     * @param reqs batch of reqs
     * @param workerIdAddr of PullModel
     */
    public abstract void process(long jobInstanceId, List reqs, String workerIdAddr);

    public void  process(long jobInstanceId, List reqs) {
        process(jobInstanceId, reqs, null);
    }

    public void setWorkThreadNum(int workThreadNum) {
        this.coreBatchThreadNum = workThreadNum;
        this.maxBatchThreadNum = workThreadNum;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    public void stop() {
        try {
            stop(true);
        } catch (InterruptedException e) {
            LOGGER.error("handle stop failed.", e);
        }
    }

    public void stop(boolean immediate) throws InterruptedException {
        if (immediate) {
            if (batchRetrieveThread != null) {
                batchRetrieveThread.interrupt();
            }
            if (batchProcessSvc != null) {
                batchProcessSvc.shutdownNow();
            }
            if (reqsQueue != null) {
                reqsQueue.clear();
            }
        } else {
            this.running.set(false);
            // 等待分发线程结束
            batchRetrieveThread.join();
            if (batchProcessSvc != null) {
                batchProcessSvc.shutdown();
                batchProcessSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            }
        }
    }

    public void clear(){
        if (reqsQueue != null) {
            reqsQueue.clear();
        }
        activeRunnableNum.set(0);
        if (batchProcessSvc != null) {
            batchProcessSvc.purge();
        }
    }

    public void submitRequest(T request) throws Exception {
        if (this.running.get()) {
            latestRequest = request;
            this.reqsQueue.submitRequest(request);
        } else {
            throw new IllegalStateException("this handler is not running.");
        }
    }

    public T getLatestRequest() {
        return latestRequest;
    }

    protected int getBatchSize() {
        return batchSize;
    }

    private synchronized List asyncHandleReqs() {
        List reqs = reqsQueue.retrieveRequests(getBatchSize());
        if (!reqs.isEmpty()) {
            activeRunnableNum.incrementAndGet();
            process(jobInstanceId, reqs);
        }
        return reqs;
    }

    public synchronized List syncHandleReqs(int pageSize, String workerIdAddr) {
        List reqs = reqsQueue.retrieveRequests(pageSize);
        if (!reqs.isEmpty()) {
            activeRunnableNum.incrementAndGet();
            process(jobInstanceId, reqs, workerIdAddr);
            activeRunnableNum.decrementAndGet();
        }
        return reqs;
    }

    /**
     * queue has remaining or at least on runnable running, using this method with attention
     * because batch process may be async so activeRunnableNum should be decrement when job really down,
     * you can look #{TMStatusReqHandler} for example;
     * @return
     */
    public synchronized boolean isActive() {
        return reqsQueue.size() != 0 || activeRunnableNum.get() > 0;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy