java executorservice persist queue

纵饮孤独 提交于 2019-12-08 03:45:18

问题


I implemented the Spring-TaskExecutor (which is the equivalent of the JDK 1.5's Executor.) to process notifications notifications receiving from external systems.

Interface with only one method:

 public interface AsynchronousService {
    void executeAsynchronously(Runnable task);
}

and the corresponding implementation:

public class AsynchronousServiceImpl implements AsynchronousService {

    private Executor taskExecutor;

    @Override
    public void executeAsynchronously(Runnable task) {
        taskExecutor.execute(task);
    }

    @Required
    public void setTaskExecutor(Executor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }
}

Xml-configuration of the task executor (legacy application):

<bean id="taskExecutor" class="org.example.impl.NotificationPool">
        <property name="corePoolSize" value="1"/>
        <property name="maxPoolSize" value="1"/>
        <property name="queueCapacity" value="100"/>
        <property name="WaitForTasksToCompleteOnShutdown" value="true"/>
</bean>

1 is set for both, corePoolSize and maxPoolSize, since I want the tasks to execute sequentially (only 1 thread is created by the pool which process the tasks).

I want to order my task based on the date when i received the notification, therefore I need to override this function to allow priority ordering:

public class NotificationPool extends ThreadPoolExecutorFactoryBean{
     @Override
     protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
          return new PriorityBlockingQueue<>(queueCapacity);
        }
    }

The ThreadPoolExecutorFactoryBean exposes the ´createExecutor´ method, which I did override to create our own Executor:

@Override
protected ThreadPoolExecutor createExecutor(int corePoolSize, int maxPoolSize, int keepAliveSeconds,
                                            BlockingQueue<Runnable> queue, ThreadFactory threadFactory,
                                            RejectedExecutionHandler rejectedExecutionHandler) {
    return new YourCustomThreadPoolExecutor (corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, queue,
            threadFactory, rejectedExecutionHandler);

Now, in my custom thread pool executor I can override the default afterExecute callback:

public class YourCustomThreadPoolExecutor extends ThreadPoolExecutor {

    public YourCustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        // Here do something with your exception
    }
}

Here is the Notification task class:

public class NotificationTask implements Runnable, Comparable<NotificationTask> {

    private final NotificationService notificationService;
    private final Notification notification;

    public NotificationService(NotificationService notificationService, 
                               Notification notification) {
        this.notificationService = notificationService;
        this.notification = notification;
    }

    @Override
    public int compareTo(NotificationTask task) {
        return notification.getTimestamp().compareTo(task.getTimestamp());
    }

    @Override
    public void run() {
        notificationService.processNotification(notification);
    }
}

And this is how I execute it:

asynchronousService.executeAsynchronously(new NotificationTask (notificationService, notification));

I don't want only to have the queue in the memory but also persist the tasks in the database, so I have them even if the application crashes.

My first solution which come to my mind is to override the default beforeExecute callback in the YourCustomThreadPoolExecutor class:

public class YourCustomThreadPoolExecutor extends ThreadPoolExecutor {

    public YourCustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

@Override
protected void beforeExecute(Thread t, Runnable r) {
    // 1) serialize runnable object
    // 2) write in the database (with a flag if it is executed true/false)  
    super.beforeExecute(t, r);
}

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);

    }
}

The idea is now, when I execute the task, I will check the database first, if it exists a task which is not executed (by checking a boolean flag for example). If yes, then I get this task first and execute it and update the flag. Then I continue executing the other task. So in this case, if the application crashes after executing a task, then I still make sure to execute this task after my application runs again, it doesn't get lost. What do you think of this solution?

First I tried to find a hook-method which is called before putting the tasks in the queue, so I can do the presistence of the tasks there. (Because I would prefer peristing the tasks before putting them in the queue) But I didn't find any way to do it, that's why my only chance is to override the beforeExecutue method. This is of course not so ideal, since I only start persisting the tasks when I dequeu them, so if the application crashes all my tasks in the queue are lost.

来源:https://stackoverflow.com/questions/51022412/java-executorservice-persist-queue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!