【发布时间】:2014-02-21 18:11:47
【问题描述】:
更新 4 - 为清楚起见重新表述问题
我正在使用拉取队列来为发送推送通知的后端工作人员任务提供反馈。我可以在日志中看到前端实例将任务排队。但是,该任务只是偶尔由后端处理。我看不到为什么任务在被处理并从队列中删除之前消失的迹象。
这可能与此有关:我在尝试从队列中租用任务时看到异常多的 TransientFailureExceptions - 尽管在尝试之间休眠。
在我的开发服务器上一切正常(早期版本已在生产中运行),但生产不再正常运行。一开始我以为是证书问题。但是,有时会在后端首次启动时发送通知。
当我在队列上调用leaseTasks 时,除了TransientFailureException 之外,没有任何迹象表明发生了错误。另外,我的日志似乎需要很长时间才能显示出来。
我可以根据需要提供更多信息和代码sn-ps。
感谢您的帮助。
更新 1:
应用程序使用 10 个拉取队列。它通常会使用 2,但队列标记仍被认为是实验性的。它们以标准方式声明:
<queue>
<name>gcm-henchdist</name>
<mode>pull</mode>
</queue>
租用任务函数是:
public boolean processBatchOfTasks()
{
List< TaskHandle > tasks = attemptLeaseTasks();
if( null == tasks || tasks.isEmpty() )
{
return false;
}
processLeasedTasks( tasks );
return true;
}
private List< TaskHandle > attemptLeaseTasks()
{
for( int attemptNnum = 1; !LifecycleManager.getInstance().isShuttingDown(); ++attemptNnum )
{
try
{
return m_taskQueue.leaseTasks( m_numLeaseTimeUnits, m_leaseTimeUnit, m_maxTasksPerLease );
} catch( TransientFailureException exc )
{
LOG.warn( "TransientFailureException when leasing tasks from queue '{}'", m_taskQueue.getQueueName(), exc );
ApiProxy.flushLogs();
} catch( ApiDeadlineExceededException exc )
{
LOG.warn( "ApiDeadlineExceededException when when leasing tasks from queue '{}'",
m_taskQueue.getQueueName(), exc );
ApiProxy.flushLogs();
}
if( !backOff( attemptNnum ) )
{
LOG.warn( "Failed to lease tasks." );
break;
}
}
return Collections.emptyList();
}
其中租赁变量分别为 30、TimeUnit.MINUTES、100
processBatchOfTasks 函数通过以下方式轮询:
private void startPollingForClient( EClientType clientType )
{
InterimApnsCertificateConfig config = InterimApnsCertificateConfigMgr.getConfig( clientType );
Queue notificationQueue = QueueFactory.getQueue( config.getQueueId().getName() );
ApplePushNotificationWorker worker = new ApplePushNotificationWorker(
notificationQueue,
m_messageConverter.getObjectMapper(),
config.getCertificateBytes(),
config.getPassword(),
config.isProduction() );
LOG.info( "Started worker for {} polling queue {}", clientType, notificationQueue.getQueueName() );
while ( !LifecycleManager.getInstance().isShuttingDown() )
{
boolean tasksProcessed = worker.processBatchOfTasks();
ApiProxy.flushLogs();
if ( !tasksProcessed )
{
// Wait before trying to lease tasks again.
try
{
//LOG.info( "Going to sleep" );
Thread.sleep( MILLISECONDS_TO_WAIT_WHEN_NO_TASKS_LEASED );
//LOG.info( "Waking up" );
} catch ( InterruptedException exc )
{
LOG.info( "Polling loop interrupted. Terminating loop.", exc );
return;
}
}
}
LOG.info( "Instance is shutting down" );
}
线程是通过以下方式创建的:
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;
}
更新 3
任务被添加到前端实例的事务中的队列中:
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 ) );
}
}
我知道事务成功是因为数据库更新正确。
在日志中,我看到“正在排队轮到你的通知...”条目,但我看不到后端日志中出现任何内容。
在管理面板中,我看到任务队列 API 调用增加 1 以及任务队列存储的任务计数增加 1。但是,写入的队列在队列中的任务和最后一分钟租用的任务中都显示为零字段。
【问题讨论】:
-
是的,请提供更多信息。
-
@MartinBerends 已更新!如果您有什么特别想看的,请告诉我。
-
@MartinBerends 我已将leaseTasks 函数的名称更改为attemptLeaseTasks,以便更容易按照您的建议评论代码。另外,我在 catch 语句和 backoff 调用之间放置了额外的间距,这样它就不会融入其中。