Thread Pool for Marble Principle

Posted by imamferianto on Fri, 14 Jun 2019 21:44:04 +0200

This chapter relies on Marble Use. Make sure you have a good understanding of Marble before reading this chapter

Overview of Thread Pools

Since Marble is a framework project, user access to Marble does not care about the implementation mechanism of Marble.Therefore, Marble's consumption of resources should be controlled when doing related processing, and applications accessed cannot be made unavailable (e.g., resource exhaustion) because of Marble.
In addition, each time Marble-Agent receives an RPC schedule, a new thread is opened for JOB execution without blocking, and threads are frequently used, so Marble's resource use receipt must be made using the same thread pool.

Thread pool Java is well encapsulated and can be overwritten in most scenarios, as enumerated below:
1. newCachedThreadPool creates a cacheable thread pool that allows for flexible recycling of idle threads if the length of the thread pool exceeds processing requirements, or new threads if none is available;
2. newFixedThreadPool creates a fixed-length thread pool that controls the maximum number of concurrent threads, and the threads that exceed it wait in the queue;
3. newScheduledThreadPool creates a fixed-length thread pool that supports both periodic and periodic task execution;
4. newSingleThreadExecutor creates a single-threaded thread pool that only uses a single worker thread to perform tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority);

Thread pool new thread process (network theft):

Marble Thread Pool

Thread pool definition

Since Marble thread pools have a big role in controlling resource usage and capping Marble resource usage, Java itself provides a thread pool with a maximum number of threads, but blocking queues is unbounded and not suitable for resource-limited use.Therefore, Marble customizes the java thread pool.

Use bounded blocking queue

 executor = new ThreadPoolExecutor(
                tpConfig.getMaxSize(),
                tpConfig.getCoreSize(), 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(tpConfig.getBlockQueueSize()),
                tpConfig.getRejectPolicy()
        );

Thread pool self-configuration support

To facilitate user self-configuration of thread pools, Marble provides configuration files to support user-defined thread pool configuration by creating a file marble-config.properties in the project root directory.Parameter assignments are made in the file as follows:

#Maximum Threads in Thread Pool
tpool_max_size=5
#Number of thread pool core threads
tpool_core_size=5
#Thread pool blocked bounded queue length
tpool_bq_size=3
#Processing policy after the thread pool is full.1-AbortPolicy (throwing RejectedExecutionException); 2-CallerRunsPolicy; 3-DiscardOldestPolicy 4-DiscardPolicy (throwing no exception)
tpool_reject_policy=1

Marble will first look for this configuration file in the root directory, and no default configuration will be found.
tpool_max_size=20
tpool_core_size=20
tpool_bq_size=5
tpool_reject_policy=1

Marble's configuration resolution classes are as follows:


/**
 * Marble Configuration Resolution
 *
 * @author <a href="dongjianxing@aliyun.com">jeff</a>
 * @version 2017/3/31 20:15
 */
public class MarbleConfigParser {
    private static ClogWrapper logger = ClogWrapperFactory.getClogWrapper(MarbleConfigParser.class);
    private static final String CONFIG = "marble-config.properties";
    private static Properties prop = new Properties();
    //Default Configuration
    private static final int TPOOL_MAX_SIZE = 20;//Maximum Threads in Thread Pool
    private static final int TPOOL_CORE_SIZE = 20;//Number of thread pool core threads
    private static final int TPOOL_BQ_SIZE = 5;//Thread pool blocking queue size
    private static final int TPOOL_REJECT_POLICY = 1;//Processing policy for thread pool full. 1-AbortPolicy (throwing RejectedExecutionException exception); 2-CallerRunsPolicy; 3-DiscardOldestPolicy 4-DiscardPolicy

    private MarbleConfigParser() {
        try {
            InputStream stream = PropertyUtils.class.getClassLoader().getResourceAsStream(CONFIG);
            if (stream == null) {
                logger.MARK("PARSE_CONFIG").warn("no marbleConfig.properties.xml is exist in the root directory of classpath, so default the config will be used.");
                return;
            }
            prop.load(stream);
        } catch (Exception e) {
            logger.MARK("PARSE_CONFIG").error("parse the marbleConfig.properties.xml in the root directory exception, detail: {}", Throwables.getStackTraceAsString(e));
        }
    }

    //Resolve thread pool configuration
    ThreadPoolConfig parseTPConfig() {
        ThreadPoolConfig tpConfig = null;
        try {
            Integer tpms = getInteger(prop, "tpool_max_size");
            Integer tpcs = getInteger(prop, "tpool_core_size");
            Integer tpqs = getInteger(prop, "tpool_bq_size");
            Integer tprp = getInteger(prop, "tpool_reject_policy");

            //Amendment Parameters
            tpcs = (tpcs == null || tpcs < 0 || tpcs > 500) ? TPOOL_CORE_SIZE : tpcs;
            tpms = (tpms == null || tpms < tpqs) ? tpcs : tpms;
            tpqs = (tpqs == null || tpqs < 0 || tpqs > 100) ? TPOOL_BQ_SIZE : tpqs;
            tprp = (tprp == null || tprp > 4) ? 1 : tprp;

            RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
            switch (tprp) {
                case 1:
                    handler = new ThreadPoolExecutor.AbortPolicy();
                    break;
                case 2:
                    handler = new ThreadPoolExecutor.CallerRunsPolicy();
                    break;
                case 3:
                    handler = new ThreadPoolExecutor.DiscardOldestPolicy();
                    break;
                case 4:
                    handler = new ThreadPoolExecutor.DiscardPolicy();
                    break;
            }
            tpConfig = new ThreadPoolConfig(tpms,tpcs,tpqs,handler);
        } catch (Exception e) {
            logger.MARK("PARSE_CONFIG").error("parse the thread-pool config from marbleConfig.properties.xml exception, detail: {}", Throwables.getStackTraceAsString(e));
        }
        if (tpConfig == null) {
            tpConfig = new ThreadPoolConfig(TPOOL_MAX_SIZE,TPOOL_CORE_SIZE, TPOOL_BQ_SIZE, new ThreadPoolExecutor.DiscardPolicy());
        }
        return tpConfig;
    }


    private Integer getInteger(Properties prop, String key) {
        Integer result = null;
        try {
            String value = prop.getProperty(key);
            if (value != null && value.trim().length() > 0) {
                result = Integer.parseInt(value);
            }
        } catch (Exception e) {
        }
        return result;
    }

    //Singleton
    private static class SingletonHolder {
        private static final MarbleConfigParser CONFIG_HELPER = new MarbleConfigParser();
    }

    public static MarbleConfigParser getInstance() {
        return MarbleConfigParser.SingletonHolder.CONFIG_HELPER;
    }


    //Thread pool configuration
    class ThreadPoolConfig {
        private int maxSize;//Maximum Threads in Thread Pool
        private int coreSize;//Number of thread pool core threads
        private int blockQueueSize;//Thread pool blocking queue size
        private RejectedExecutionHandler rejectPolicy;//Thread pool rejection policy

        ThreadPoolConfig(int maxSize, int coreSize, int blockQueueSize, RejectedExecutionHandler rejectPolicy) {
            this.maxSize = maxSize;
            this.coreSize = coreSize;
            this.blockQueueSize = blockQueueSize;
            this.rejectPolicy = rejectPolicy;
        }

        int getCoreSize() {
            return coreSize;
        }

        int getBlockQueueSize() {
            return blockQueueSize;
        }

        public int getMaxSize() {
            return maxSize;
        }

        RejectedExecutionHandler getRejectPolicy() {
            return rejectPolicy;
        }

        @Override
        public String toString() {
            return "ThreadPoolConfig{" +
                    "maxSize=" + StringUtils.safeString(maxSize) +
                    ", coreSize=" + StringUtils.safeString(coreSize) +
                    ", blockQueueSize=" + StringUtils.safeString(blockQueueSize) +
                    ", rejectPolicy=" + StringUtils.safeString(rejectPolicy.getClass().getSimpleName()) +
                    '}';
        }
    }
}

Thread pool usage example

Take the following thread pool configuration as an example:
tpool_max_size=5
tpool_core_size=5
tpool_bq_size=3
tpool_reject_policy=1

In the figure below, the same machine (10.2.37.137) received 11 consecutive Marble dispatches>

  • Marble-Agent successfully started five threads from the thread pool for execution on the first to fifth times.
  • 6-8 calls, the number of core threads is full, bounded blocking queue begins to fill;
  • The 9-10 call bounded blocking queues have been filled and the maximum number of threads has been filled. Due to the rejection policy Abort, 10-11 dispatch requests have been rejected directly.
  • A Thread Interrupt call was made manually;
  • Successfully executed for the 11th time;

Topics: Java xml network