Java 应用引擎拉队列任务在正确处理之前消失

Java 应用引擎拉队列任务在正确处理之前消失,java,google-app-engine,Java,Google App Engine,更新4-为清晰起见,重新表述问题 我使用拉队列来反馈发送推送通知的后端工作人员任务。我可以在日志中看到前端实例对任务的队列。但是,该任务只是偶尔由后端处理。我看不出任务在被处理和从队列中删除之前消失的原因 这可能与此有关:在尝试从队列中租用任务时,我看到异常多的s,尽管在尝试之间处于休眠状态 在我的开发服务器上,一切都正常工作(早期的版本已经在生产环境中运行),但生产环境不再正常工作。起初我认为这是一个证书问题。但是,有时会在后端首次启动时发送通知 除了在队列上调用leaseTasks时,没有任

更新4-为清晰起见,重新表述问题

我使用拉队列来反馈发送推送通知的后端工作人员任务。我可以在日志中看到前端实例对任务的队列。但是,该任务只是偶尔由后端处理。我看不出任务在被处理和从队列中删除之前消失的原因

这可能与此有关:在尝试从队列中租用任务时,我看到异常多的s,尽管在尝试之间处于休眠状态

在我的开发服务器上,一切都正常工作(早期的版本已经在生产环境中运行),但生产环境不再正常工作。起初我认为这是一个证书问题。但是,有时会在后端首次启动时发送通知

除了在队列上调用leaseTasks时,没有任何迹象表明发生了错误。而且,我的日志似乎需要很长时间才能显示出来

我可以根据需要提供更多信息和代码片段

谢谢你的帮助

更新1:

该应用程序使用10个拉队列。它通常使用2,但队列标记仍然被认为是实验性的。它们以标准方式声明:

<queue>
    <name>gcm-henchdist</name>
    <mode>pull</mode>
</queue>
线程是通过以下方式创建的:

Thread thread = ThreadManager.createBackgroundThread( new Runnable()
{
    @Override
    public void run()
    {
        startPollingForClient( clientType );
    }
} );

thread.start();
GCM通知以类似的方式处理

更新2

以下是退避功能。我已经在日志中(使用GAE和我自己的时间戳)验证了睡眠是否正确增加

private boolean backOff( int attemptNo )
{
    // Exponential back off between 2 seconds and 64 seconds with jitter
    // 0..1000 ms.
    attemptNo = Math.min( 6, attemptNo );
    int backOffTimeInSeconds = 1 << attemptNo;
    long backOffTimeInMilliseconds = backOffTimeInSeconds * 1000 + (int)( Math.random() * 1000 );

    LOG.info( "Backing off for {} milliseconds from queue '{}'", backOffTimeInMilliseconds, m_taskQueue.getQueueName() );
    ApiProxy.flushLogs();

    try
    {
        Thread.sleep( backOffTimeInMilliseconds );

    } catch( InterruptedException e )
    {
        return false;
    }

    LOG.info( "Waking up from {} milliseconds sleep for queue '{}'", backOffTimeInMilliseconds, m_taskQueue.getQueueName() );
    ApiProxy.flushLogs();

    return true;
}
private boolean退避(int attemptNo)
{
//2秒到64秒之间的指数后退,抖动
//0..1000毫秒。
attemptNo=Math.min(6,attemptNo);
int backofftimeunseconds=1表示“如果再次尝试,请求的操作可能会成功”(因为失败是暂时的)。因此,当引发此异常时,您的代码应循环并重复leaseTasks调用。此外,AppEngine不必重做请求本身,因为它通过异常通知您应该这样做

很遗憾,您将方法名称leaseTasks作为自己的方法重复,因为现在不清楚我在提到leaseTasks时指的是哪一个。不过,请将对m_taskQueue.leaseTasks的内部调用包装在while循环中,并添加一个try块,以仅捕获TransientFailureException。仅当出现异常时,才使用标志结束while循环离子不会被抛出


这就足够了吗,或者您需要一个完整的源代码列表吗?

似乎罪魁祸首可能是我在排队执行任务时调用了addAsync,而不是只调用add


我更换了电话,现在一切似乎都正常。我想知道为什么这会产生影响,并在找到原因后更新答案。

是的,请提供更多信息。@MartinBerends更新了!如果您想了解任何具体情况,请告诉我。@MartinBerends我已更改了n我的leaseTasks函数的ame用于尝试leaseTasks,以便按照您的建议更容易对代码进行注释。此外,我在catch语句和backoff调用之间放置了额外的间距,以便它不会混入。这就是backoff函数所做的,我在日志中验证了它在规定的时间内处于休眠状态然后再试一次。即使在两次尝试之间等待几秒钟,失败也会频繁发生。下面是Google创建的示例:我按enter键创建了一个空格,但提交了注释:令人沮丧的是,代码在生产中正常工作,但当我放大它并添加更多队列时,它停止了o函数。这尤其令人沮丧,因为它在dev服务器上正常工作。我还应该指出,我的日志从未显示任务被删除。因此,一旦租约到期,它应该返回队列,再次尝试。但是,任务似乎正在消失,而没有被删除。上述三条注释相当于rej这是我建议的一部分。您没有考虑过它或不愿意尝试它。目前的退避逻辑有缺陷。在它执行后,外部for循环将继续执行队列中的下一个任务,而上一个暂时失败的任务将被遗忘!如果您希望我继续尝试帮助,请添加内部while循环和内部catch中的逻辑存在相同的错误。使用addAsync应该没有什么区别,因为它只影响排队操作周围的流控制。可能是m_taskQueue.leaseTasks()引发的暂时故障异常现在已经不那么短暂了。日志可能会告诉我。@MartinBerends我也是这么想的。但是,异常仍然像以前一样普遍。考虑到错误的性质,我有一种感觉,一个无害的更改可以解决问题。我希望我有一个谷歌工程师可以解释为什么。当使用addAsync a时,异常仍然普遍吗是的,它们经常出现在添加和Addiasyc中。我认为错误不在于任务的检索和处理,而在于排队。当我们考虑更新3(上列)时,“队列中的任务”和“最后一分钟租用”是有意义的。尽管API调用号不断增加,字段仍保持为零。这也解释了与处理相关的日志不足以及找不到任务的原因(即使它们从未被删除)。所以可能它们没有消失,只是从来没有被正确创建过。哎哟。那么,你基于代码的链接示例在大量使用的情况下可能会出现同样的问题。我最近使用了一台iPhone进行测试,所以当我得到一个round tuit时,我会尝试部署原始项目。round tuit很少,所以没有关于什么时候的承诺,但是这个问题已经变成了一些问题
Thread thread = ThreadManager.createBackgroundThread( new Runnable()
{
    @Override
    public void run()
    {
        startPollingForClient( clientType );
    }
} );

thread.start();
private boolean backOff( int attemptNo )
{
    // Exponential back off between 2 seconds and 64 seconds with jitter
    // 0..1000 ms.
    attemptNo = Math.min( 6, attemptNo );
    int backOffTimeInSeconds = 1 << attemptNo;
    long backOffTimeInMilliseconds = backOffTimeInSeconds * 1000 + (int)( Math.random() * 1000 );

    LOG.info( "Backing off for {} milliseconds from queue '{}'", backOffTimeInMilliseconds, m_taskQueue.getQueueName() );
    ApiProxy.flushLogs();

    try
    {
        Thread.sleep( backOffTimeInMilliseconds );

    } catch( InterruptedException e )
    {
        return false;
    }

    LOG.info( "Waking up from {} milliseconds sleep for queue '{}'", backOffTimeInMilliseconds, m_taskQueue.getQueueName() );
    ApiProxy.flushLogs();

    return true;
}
if( null != queueType )
{
    String deviceName;
    int numDevices = deviceList.size();
    for ( int iDevice = 0; iDevice < numDevices; ++iDevice )
    {
        deviceName = deviceList.get( iDevice ).getName();
        LOG.info( "Queueing Your-Turn notification for user: {} device: {} queue: {}", user.getId(), deviceName, queueType.getName() );
        Queue queue = QueueFactory.getQueue( queueType.getName() );

        queue.addAsync( TaskOptions.Builder.withMethod( TaskOptions.Method.PULL )
                .param( "alertLocKey", "NOTIF_YOUR_TURN" ).param( "device", deviceName ) );
    }
}