预定执行器:以固定速率轮询结果,如果超时或结果有效则退出

Posted

技术标签:

【中文标题】预定执行器:以固定速率轮询结果,如果超时或结果有效则退出【英文标题】:Scheduled executor: poll for result at fix rate and exit if timeout or result valid 【发布时间】:2014-06-07 05:44:39 【问题描述】:

问题: 我需要每隔 10 秒以固定速率调用一个 dao 方法,然后我需要检查结果是否有效,如果是退出,否则继续每 10 秒调用一次该方法,直到我得到有效结果或定义超时(比如说 2 分钟)结束了。

方法: 我想将任务和调度程序逻辑分开,并以这样的方式编写任务,以便具有相似要求的不同类可以使用它。

我能想到的一种方法是定义一个新的轮询任务

public abstract class PollerTask<T> implements Runnable 

    abstract public boolean isValid(T result);

    abstract public T task();

    private T result;

    private volatile boolean complete;

    public boolean isComplete() 
        return complete;
    

    public T getResult() 
        return result;
    

    @Override
    final public void run() 
        result = task();
        if (complete = isValid(result)) 
            //may be stop scheduler ??
        

    

用户只需提供task的实现和isValid;

然后我们可以定义一个单独的类,它接受池频率和超时,并创建一个调度执行器并提交这个任务

public class PollerTaskExecutor 

    private int pollingFreq;
    private int timeout;
    private ScheduledExecutorService executor;
    private ScheduledExecutorService terminator;
    private ExecutorService condition;
    private volatile boolean done;
    private ScheduledFuture future;

    public PollerTaskExecutor(int pollingFreq, int timeout) 
        this.pollingFreq = pollingFreq;
        this.timeout = timeout;
        executor = Executors.newSingleThreadScheduledExecutor();
        terminator = Executors.newSingleThreadScheduledExecutor();
        condition = Executors.newSingleThreadExecutor();
    

    public void submitTaskForPolling(final PollerTask pollerTask) 
        future = executor.scheduleAtFixedRate(pollerTask, 0, pollingFreq, TimeUnit.SECONDS);
        terminator.schedule(new Runnable() 
            @Override
            public void run() 
                complete();
            
        , timeout, TimeUnit.SECONDS);
        condition.execute(new Runnable() 
            @Override
            public void run() 
                if (pollerTask.isComplete()) 
                    complete();
                
            
        );

    

    public boolean isDone() 
        return done;
    

    public void complete() 
        future.cancel(false);
        executor.shutdown();
        terminator.shutdown();
        condition.shutdown();
        done = true;

    

现在用户可以等到 pollerExecutor.isDone 返回 true 并获取结果。 为了以下目的,我不得不使用三个执行器:

    执行程序以固定间隔运行任务 执行程序在超时结束时停止所有操作 如果在超时之前获得有效结果,执行程序将停止所有操作。

有人可以建议一个更好的方法吗,对于如此微不足道的任务来说这似乎很复杂?

【问题讨论】:

【参考方案1】:

让它成为一个自调度的任务。在伪代码中:

public class PollingTaskRunner 

...
CountDownLatch doneWait = new CountDownLatch(1);
volatile boolean done;

PollingTaskRunner(Runnable pollingTask, int frequency, int period) 
    ...
    endTime = now + period;
    executor.schedule(this, 0);


run() 

    try 
        pollingTask.run();
     catch (Exception e) 
        ...
    
    if (pollingTask.isComplete() || now + frequency > endTime) 
        done = true;
        doneWait.countDown();
        executor.shutdown();
     else 
        executor.schedule(this, frequency);
    


await() 
    doneWait.await();


isDone() 
    return done;


这并不复杂,但在您第一次运行/测试时添加大量调试语句,这样您就知道发生了什么。一旦它按预期运行,就很容易重复使用该模式。

【讨论】:

【参考方案2】:

一个稍微简单的方法,你不需要为终结者单独的执行器服务,你可以简单地将终结者任务推送到同一个执行器中。

更简单。让PollerTask 将其结果放在BlockingQueue 中。然后让PollingTaskRunnerBlockingQueue 上执行定时poll。每当从poll 调用ScheduledFuture.cancel 返回控制权时,因为任务成功或超时。

【讨论】:

以上是关于预定执行器:以固定速率轮询结果,如果超时或结果有效则退出的主要内容,如果未能解决你的问题,请参考以下文章

基于HTTP的长轮询简单实现

quartz单实例任务超时处理

i/o 设备轮询间隔是不是经常一致?

jQuery AJAX 轮询 JSON 响应,基于 AJAX 结果或 JSON 内容进行处理

API 轮询和超时

nginx异步非阻塞理解