GAE Python:进程已终止,因为后端已停止
我已经在我的应用程序中创建了一个单独的模块,带有手动扩展和一个实例,以循环通过一个拉队列,一次租用并完成一个任务。为了实现这一点,我有一个映射到“/\u ah/start”的RequestHandler,它注册一个关闭钩子并启动一个后台线程。后台线程在队列中循环,租用任务,并使用该任务解析CSV文件。到目前为止,一切正常,除了关机挂钩。我认为它永远不会运行。这是我的密码:GAE Python:进程已终止,因为后端已停止,python,google-app-engine,Python,Google App Engine,我已经在我的应用程序中创建了一个单独的模块,带有手动扩展和一个实例,以循环通过一个拉队列,一次租用并完成一个任务。为了实现这一点,我有一个映射到“/\u ah/start”的RequestHandler,它注册一个关闭钩子并启动一个后台线程。后台线程在队列中循环,租用任务,并使用该任务解析CSV文件。到目前为止,一切正常,除了关机挂钩。我认为它永远不会运行。这是我的密码: class FileParserWrapper(object): def __init__(self): ver
class FileParserWrapper(object):
def __init__(self):
version = os.getenv('CURRENT_VERSION_ID').split('.')
if version[0] == 'test':
self.instance = 'development'
else:
self.instance = 'production'
if (os.getenv('SERVER_SOFTWARE') and
os.getenv('SERVER_SOFTWARE').startswith('Google App Engine/')):
if self.instance == 'development':
self.staging_db = MySQLdb.connect(unix_socket='/cloudsql/' + _INSTANCE_NAME, db='xxxxxx', user='xxxxxx')
else:
self.staging_db = MySQLdb.connect(unix_socket='/cloudsql/' + _INSTANCE_NAME, db=STAGING_DB_NAME, user='xxxxxx')
else:
self.staging_db = MySQLdb.connect(host='xxxxxx', db='xxxxxx', user='xxxxxx')
self.staging_cursor = self.staging_db.cursor()
self.current_task = None
self.current_state = 'inactive'
self.current_task_complete = False
self.last_task_complete = False
self.retries = 0
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
logging.info('FileParserWrapper.__exit__()')
self.staging_cursor.close()
if self.staging_db.open:
self.staging_db.close()
def shutdown(self):
logging.info('FileParserWrapper.shutdown()')
apiproxy_stub_map.apiproxy.CancelApiCalls()
# save_state()
self.__exit__(None, None, None)
# May want to raise an exception
def generate_payload_dict(self, p):
...
def reset_tables(self, upload_id, format):
...
def run_parser(self):
if self.instance == 'development':
q = taskqueue.Queue('test-queue')
else:
q = taskqueue.Queue('ir-upload-queue')
t = q.lease_tasks(3600, 1)
while t:
self.current_state = 'active'
self.current_task = t[0].name
payload = self.generate_payload_dict(t[0].payload)
logging.info('*** Processing task ' + self.current_task + ' ***')
logging.debug(repr(payload))
with FileParser() as fp:
try:
logging.info('Beginning parse...')
result = fp.ParseFile(payload['new_file_name_full'], payload['format'], payload['upload_id'], payload)
if payload['name_file_temp'] != None and result == True:
fp.ParseFile(payload['new_csu_name_file_name_full'], "name", payload['upload_id'], payload)
except:
logging.error('Unknown error occured!')
self.last_task_complete = False
# Release the task lease
q.modify_task_lease(t[0], 0)
self.reset_tables(payload['upload_id'], payload['format'])
raise
else:
self.last_task_complete = True
q.delete_tasks_by_name(self.current_task)
if self.last_task_complete == True:
# Sleep for 5 seconds, then check for new tasks
time.sleep(5)
t = q.lease_tasks(3600, 1)
logging.info('Loop complete, shutting down')
# Shutdown the instance
modules.stop_version()
class BatchProcessorHandler(webapp2.RequestHandler):
def get(self):
proc = FileParserWrapper()
def run(arg):
proc.run_parser()
def shutdown():
logging.info('BatchProcessorHandler().get().shutdown()')
self.response.set_status(200)
proc.shutdown()
# Register shutdown hook
hook = runtime.set_shutdown_hook(shutdown)
# Start background thread
tid = background_thread.start_new_background_thread(run, [True])
application = webapp2.WSGIApplication([
('/_ah/start', BatchProcessorHandler),
], debug=True)
正如您所看到的,我在处理关闭挂钩时应该调用的每个方法中都放置了日志消息,但我从未在日志中看到它们。我看到对“/\u-ah/start”和“/\u-ah/background”的请求,或者看到对“/\u-ah/start”、“/\u-ah/background”和“/\u-ah/stop”的请求。在后一个版本中,“/_ah/stop”接收HTTP 500代码,日志中的最后一条消息是“进程终止,因为后端已停止”。这种行为似乎是随机发生的,谷歌上很少有帖子提到这一特定的错误消息
为什么会发生这种行为?我在本地搜索了谷歌应用程序引擎代码,没有找到这条消息。该代码还指出,运行时API和后台线程已被弃用,即使模块文档(替换后端)中有使用相同代码的代码示例。runtime.set\u shutdown\u hook
函数是否有错误
**为了可读性,省略了一些代码,因此这不是一个直接的答案,但我就是这样解决了这个问题的: 我从手动缩放切换到基本缩放 一旦我切换到基本缩放,关闭挂钩运行得非常完美,但你不能直接调用它。通过基本的扩展,服务器自己处理关闭,您只需在模块的YAML文件中设置一个变量(我认为是空闲超时)。我将时间设置为5分钟,这对我的项目有效。我必须改变一些RequestHandler的位置,以确保所有调用都正确。这是我目前的代码: YAML文件
application: xxxxxxxxx
version: 1
runtime: python27
api_version: 1
threadsafe: yes
module: batch-processor
instance_class: B4_1G
basic_scaling:
max_instances: 1
idle_timeout: 5m
Python(节略)
我的大部分代码都是相同的。不过,我所做的是将我的BatchProcessorHandler
映射到/\u ah/queue/default
,它映射到默认任务队列。然后,在我的另一个脚本中,在我将任务添加到Pull队列之后,我创建了一个“虚拟”任务,给它一个版本/模块对的target
,并将其分配给默认推送队列。这将调用BatchProcessorHandler
。以下是代码的这一部分:
...
other code here
...
# Start up a batch-processor instance
logging.info('Starting instance of batch-processor')
try:
the_retry_options = taskqueue.TaskRetryOptions(task_retry_limit=1)
taskqueue.add(target='1.batch-processor', retry_options=the_retry_options)
except modules.UnexpectedStateError:
logging.warning('Instance already started!')
except:
logging.error('Could not start module!')
raise
1.批处理器
就是我刚才提到的版本/模块对。我正在向队列中添加一个空任务,其目的基本上是“ping”端点/\u ah/queue/default
,它在这里启动BatchProcessorHandler
。你找到解决办法了吗?经过短暂的调查,我发现在我们部署时会发生这种情况。如果你简单地从GAE面板关闭实例,它就像一个魅力,但这对我们来说仍然是一个问题。
...
other code here
...
# Start up a batch-processor instance
logging.info('Starting instance of batch-processor')
try:
the_retry_options = taskqueue.TaskRetryOptions(task_retry_limit=1)
taskqueue.add(target='1.batch-processor', retry_options=the_retry_options)
except modules.UnexpectedStateError:
logging.warning('Instance already started!')
except:
logging.error('Could not start module!')
raise