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

org.rx.core.Tasks Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
package org.rx.core;

import io.netty.util.internal.ThreadLocalRandom;
import lombok.Getter;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.rx.annotation.Subscribe;
import org.rx.bean.DateTime;
import org.rx.bean.FlagsEnum;
import org.rx.exception.TraceHandler;
import org.rx.util.function.Action;
import org.rx.util.function.Func;

import java.sql.Time;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.*;

import static org.rx.core.Extends.circuitContinue;

//Java 11 ForkJoinPool.commonPool() has class loading issue
@Slf4j
public final class Tasks {
    //Random load balance, if methodA wait methodA, methodA is executing wait and methodB is in ThreadPoolQueue, then there will be a false death.
    static final List nodes = new CopyOnWriteArrayList<>();
    static final ExecutorService executor;
    static final WheelTimer timer;
    static final Queue shutdownActions = new ConcurrentLinkedQueue<>();
    static int poolCount;

    static {
        onChanged(null);
        executor = new AbstractExecutorService() {
            @Getter
            boolean shutdown;

            @Override
            public Future submit(Runnable task) {
                return nextPool().submit(task);
            }

            @Override
            public  Future submit(Runnable task, T result) {
                return nextPool().submit(task, result);
            }

            @Override
            public  Future submit(Callable task) {
                return nextPool().submit(task);
            }

            @Override
            public void execute(Runnable command) {
                nextPool().execute(command);
            }

            @Override
            public void shutdown() {
                shutdown = true;
            }

            @Override
            public List shutdownNow() {
                shutdown = true;
                return Collections.emptyList();
            }

            @Override
            public boolean isTerminated() {
                return shutdown;
            }

            @Override
            public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
                return shutdown;
            }
        };
        timer = new WheelTimer(executor);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            Action fn;
            while ((fn = shutdownActions.poll()) != null) {
                try {
                    fn.invoke();
                } catch (Throwable e) {
                    TraceHandler.INSTANCE.log(e);
                }
            }
        }));

        try {
            Reflects.writeStaticField(CompletableFuture.class, "asyncPool", executor); //jdk8
//            ForkJoinPoolWrapper.transform();
        } catch (Throwable e) {
            try {
                Reflects.writeStaticField(CompletableFuture.class, "ASYNC_POOL", executor); //jdk11
            } catch (Throwable ie) {
                log.warn("setAsyncPool {}", e, ie);
            }
        }
    }

    @Subscribe(topicClass = RxConfig.class)
    static synchronized void onChanged(ObjectChangedEvent event) {
        int newCount = RxConfig.INSTANCE.threadPool.replicas;
        if (newCount == poolCount) {
            return;
        }

        log.info("RxMeta {} changed {} -> {}", RxConfig.ConfigNames.THREAD_POOL_REPLICAS, poolCount, newCount);
        for (int i = 0; i < newCount; i++) {
            nodes.add(0, new ThreadPool(String.format("N%s", i)));
        }
        poolCount = newCount;

        if (nodes.size() > poolCount) {
            timer.setTimeout(() -> {
                if (nodes.size() == poolCount) {
                    circuitContinue(false);
                    return;
                }
                for (int i = poolCount; i < nodes.size(); i++) {
                    if (nodes.get(i).getActiveCount() == 0) {
                        nodes.remove(i);
                    }
                }
            }, 60000, nodes, TimeoutFlag.PERIOD.flags(TimeoutFlag.REPLACE));
        }
    }

    public static ThreadPool nextPool() {
        return nodes.get(ThreadLocalRandom.current().nextInt(0, poolCount));
    }

    public static ExecutorService executor() {
        return executor;
    }

    public static WheelTimer timer() {
        return timer;
    }

    public static void addShutdownHook(Action fn) {
        shutdownActions.offer(fn);
    }

    @SneakyThrows
    @SafeVarargs
    public static  T sequentialRetry(Func... funcs) {
        Throwable last = null;
        for (Func func : funcs) {
            try {
                return func.invoke();
            } catch (Throwable e) {
                last = e;
            }
        }
        if (last != null) {
            throw last;
        }
        return null;
    }

    @SneakyThrows
    @SafeVarargs
    public static  T randomRetry(Func... funcs) {
        int mid = ThreadLocalRandom.current().nextInt(0, funcs.length);
        Throwable last = null;
        for (int i = 0; i < mid; i++) {
            try {
                return funcs[i].invoke();
            } catch (Throwable e) {
                last = e;
            }
        }
        for (int i = mid; i < funcs.length; i++) {
            try {
                return funcs[i].invoke();
            } catch (Throwable e) {
                last = e;
            }
        }
        if (last != null) {
            throw last;
        }
        return null;
    }

    @SneakyThrows
    public static  T await(Future future) {
        if (future instanceof CompletableFuture) {
            return ((CompletableFuture) future).join();
        }
        return future.get();
    }

    public static  T awaitQuietly(Func func, long millis) {
        return awaitQuietly(run(func), millis);
    }

    public static  T awaitQuietly(Future future, long millis) {
        try {
            return future.get(millis, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            //catch +1 ?
            TraceHandler.INSTANCE.log("awaitNow {} timeout", Reflects.CLASS_TRACER.getClassTrace(2).getName());
        } catch (Exception e) {
            TraceHandler.INSTANCE.log(e);
        }
        return null;
    }

    public static Future run(Action task) {
        return nextPool().run(task);
    }

    public static Future run(Action task, Object taskId, FlagsEnum flags) {
        return nextPool().run(task, taskId, flags);
    }

    public static  Future run(Func task) {
        return nextPool().run(task);
    }

    public static  Future run(Func task, Object taskId, FlagsEnum flags) {
        return nextPool().run(task, taskId, flags);
    }

    public static CompletableFuture runAsync(Action task) {
        return nextPool().runAsync(task);
    }

    public static CompletableFuture runAsync(Action task, Object taskId, FlagsEnum flags) {
        return nextPool().runAsync(task, taskId, flags);
    }

    public static  CompletableFuture runAsync(Func task) {
        return nextPool().runAsync(task);
    }

    public static  CompletableFuture runAsync(Func task, Object taskId, FlagsEnum flags) {
        return nextPool().runAsync(task, taskId, flags);
    }

    public static TimeoutFuture setTimeout(Action task, long delay) {
        return timer.setTimeout(task, delay);
    }

    public static TimeoutFuture setTimeout(Action task, long delay, Object taskId, FlagsEnum flags) {
        return timer.setTimeout(task, delay, taskId, flags);
    }

    public static List> scheduleDaily(Action task, String... timeArray) {
        return Linq.from(timeArray).select(p -> scheduleDaily(task, Time.valueOf(p))).toList();
    }

    /**
     * 每天按指定时间执行
     *
     * @param task Action
     * @param time "HH:mm:ss"
     * @return Future
     */
    public static ScheduledFuture scheduleDaily(@NonNull Action task, @NonNull Time time) {
        long oneDay = Constants.ONE_DAY_TOTAL_SECONDS * 1000;
        long initDelay = DateTime.now().setTimePart(time.toString()).getTime() - System.currentTimeMillis();
        initDelay = initDelay > 0 ? initDelay : oneDay + initDelay;

        return schedulePeriod(task, initDelay, oneDay);
    }

    public static ScheduledFuture schedulePeriod(Action task, long period) {
        return schedulePeriod(task, period, period);
    }

    public static ScheduledFuture schedulePeriod(@NonNull Action task, long initialDelay, long period) {
        return timer.setTimeout(task, d -> d == 0 ? initialDelay : period, null, Constants.TIMER_PERIOD_FLAG);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy