GAE Python:进程已终止,因为后端已停止

GAE Python:进程已终止,因为后端已停止,python,google-app-engine,Python,Google App Engine,我已经在我的应用程序中创建了一个单独的模块,带有手动扩展和一个实例,以循环通过一个拉队列,一次租用并完成一个任务。为了实现这一点,我有一个映射到“/\u ah/start”的RequestHandler,它注册一个关闭钩子并启动一个后台线程。后台线程在队列中循环,租用任务,并使用该任务解析CSV文件。到目前为止,一切正常,除了关机挂钩。我认为它永远不会运行。这是我的密码: class FileParserWrapper(object): def __init__(self): ver

我已经在我的应用程序中创建了一个单独的模块,带有手动扩展和一个实例,以循环通过一个拉队列,一次租用并完成一个任务。为了实现这一点,我有一个映射到“/\u ah/start”的RequestHandler,它注册一个关闭钩子并启动一个后台线程。后台线程在队列中循环,租用任务,并使用该任务解析CSV文件。到目前为止,一切正常,除了关机挂钩。我认为它永远不会运行。这是我的密码:

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