如何重启 Google App Engine 标准服务
How to restart Google App Engine Standard Service
上下文:我有一个提供交互式图表和数据分析的应用程序。为了计算图表和数据摘要,它使用通过查询 google BigQuery 在 App 初始化时加载的数据集。然后将数据保存为全局变量(在内存中),并用于可能由不同用户 运行 进行的所有数据计算和绘图(每个用户在其会话中保存自己的 filters/mask)。
此数据集在 BigQuery 中每天晚上更改一次(我知道刷新的确切日期时间)。在 BigQuery 中刷新数据后,我希望刷新数据集的全局变量。
我知道正确的解决方案是针对每个用户请求调用一个数据库,但是 BigQuery 对请求的高延迟并不能使它成为一个好的解决方案,而且我不能使用另一个数据库。
到目前为止,我遇到的唯一解决方案是在 BigQuery 数据刷新后重新启动 Google App Engine 服务(所有实例)。请注意,这应该是计划的操作,以编程方式完成。
我的问题:
- 如果重启服务是最好的解决方案,我应该如何重启服务?
- 如果有其他方法可以完成我想要的,请告诉我
在您执行操作时缓存您的数据集可能是个好习惯;如果你知道数据没有改变。那么就不需要为它重新查询 BigQuery。
但是,您的数据集确实会发生变化,每天仅发生一次。
因此,我认为您的方法应该是修改您的应用,以便它每天刷新 BigQuery 数据集的缓存副本和停止|阻止您的用户查询数据集随着它的变化。
您实际上只需要在用户请求时更改数据集(无需在用户不需要的日子刷新数据集),因此,取决于刷新所需的时间和用户对延迟的期望,您可以通过用户请求触发刷新:数据集是否已更改?如果是,则屏蔽此请求,刷新数据后再响应用户。
我假设您已经解决了用户的数据图和计算因数据集不同而不同的问题。
一种可能的方法是在 BQ 数据集更新后触发 运行 个实例退出(自行退出,即自杀)并离开 GAE 启动 new/replacement 个实例,这将加载更新数据集。
触发器可以基于内存缓存、数据存储或云 storage/GCS(都比 BQ 快 - 在每个请求中检查它们的惩罚更少)。您希望确定触发器不会影响新启动的实例:
- 让触发器成为例如最近一次BQ数据集更新的时间戳
- 添加一个全局变量,其中包含加载到内存中的数据集的时间戳
- 当 memcache/datastore 时间戳比内存中的时间更新 ~24 小时(或 "a lot")时触发触发器
对于导致退出的操作,我会尝试:
- 常规
sys.exit(0)
调用(不太确定 if/how 这是否适用于 GAE)
引发异常(不太好,它会在日志中留下令人讨厌的痕迹)。如果你使用它,尽量让它尽可能清楚,以尽量减少被意外解释为真正失败的机会。可能是这样的:
assert False, "Intentional crash to force an instance restart"
另一种可能的方法是强制实例从外部重启 - 通过使用相同的版本字符串重新部署应用程序。与重新部署相同版本导致的实例重启相关的中断实际上是我不喜欢使用基于服务版本的环境实现的原因,请参阅
但要实现这一点,您需要一些其他环境来触发和执行部署。它可能是其他一些 GAE 服务,甚至 Cloud Function (in which case using a Storage event trigger 将消除显式轮询数据集更新条件的需要)。
我终于找到了一种通过使用 Python API 发现客户端和服务帐户以编程方式重新启动所有实例的方法。
它首先获取活动实例列表并删除所有实例。然后,执行一个简单的请求来启动其中之一。
import requests
from apiclient.discovery import build
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('credentials.json')
scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/appengine.admin',"https://www.googleapis.com/auth/cloud-platform"])
appengine = build(serviceName="appengine",version="v1",credentials=scoped_credentials)
VERSION_ID = "version_id"
PROJECT_ID = "project_id"
SERVICE_ID = "appengine_service_name"
APP_URL = "http://some_url.com"
active_instances_dict = appengine.apps().services().versions().instances().list(servicesId=SERVICE_ID,appsId=PROJECT_ID,versionsId=VERSION_ID).execute()
list_of_instances = active_instances_dict["instances"]
for instance in list_of_instances:
appengine.apps().services().versions().instances().delete(servicesId=SERVICE_ID,appsId=PROJECT_ID,
versionsId=VERSION_ID,instancesId=instance["id"]).execute()
requests.get(url=APP_URL)
上下文:我有一个提供交互式图表和数据分析的应用程序。为了计算图表和数据摘要,它使用通过查询 google BigQuery 在 App 初始化时加载的数据集。然后将数据保存为全局变量(在内存中),并用于可能由不同用户 运行 进行的所有数据计算和绘图(每个用户在其会话中保存自己的 filters/mask)。
此数据集在 BigQuery 中每天晚上更改一次(我知道刷新的确切日期时间)。在 BigQuery 中刷新数据后,我希望刷新数据集的全局变量。
我知道正确的解决方案是针对每个用户请求调用一个数据库,但是 BigQuery 对请求的高延迟并不能使它成为一个好的解决方案,而且我不能使用另一个数据库。
到目前为止,我遇到的唯一解决方案是在 BigQuery 数据刷新后重新启动 Google App Engine 服务(所有实例)。请注意,这应该是计划的操作,以编程方式完成。
我的问题:
- 如果重启服务是最好的解决方案,我应该如何重启服务?
- 如果有其他方法可以完成我想要的,请告诉我
在您执行操作时缓存您的数据集可能是个好习惯;如果你知道数据没有改变。那么就不需要为它重新查询 BigQuery。
但是,您的数据集确实会发生变化,每天仅发生一次。
因此,我认为您的方法应该是修改您的应用,以便它每天刷新 BigQuery 数据集的缓存副本和停止|阻止您的用户查询数据集随着它的变化。
您实际上只需要在用户请求时更改数据集(无需在用户不需要的日子刷新数据集),因此,取决于刷新所需的时间和用户对延迟的期望,您可以通过用户请求触发刷新:数据集是否已更改?如果是,则屏蔽此请求,刷新数据后再响应用户。
我假设您已经解决了用户的数据图和计算因数据集不同而不同的问题。
一种可能的方法是在 BQ 数据集更新后触发 运行 个实例退出(自行退出,即自杀)并离开 GAE 启动 new/replacement 个实例,这将加载更新数据集。
触发器可以基于内存缓存、数据存储或云 storage/GCS(都比 BQ 快 - 在每个请求中检查它们的惩罚更少)。您希望确定触发器不会影响新启动的实例:
- 让触发器成为例如最近一次BQ数据集更新的时间戳
- 添加一个全局变量,其中包含加载到内存中的数据集的时间戳
- 当 memcache/datastore 时间戳比内存中的时间更新 ~24 小时(或 "a lot")时触发触发器
对于导致退出的操作,我会尝试:
- 常规
sys.exit(0)
调用(不太确定 if/how 这是否适用于 GAE) 引发异常(不太好,它会在日志中留下令人讨厌的痕迹)。如果你使用它,尽量让它尽可能清楚,以尽量减少被意外解释为真正失败的机会。可能是这样的:
assert False, "Intentional crash to force an instance restart"
另一种可能的方法是强制实例从外部重启 - 通过使用相同的版本字符串重新部署应用程序。与重新部署相同版本导致的实例重启相关的中断实际上是我不喜欢使用基于服务版本的环境实现的原因,请参阅
但要实现这一点,您需要一些其他环境来触发和执行部署。它可能是其他一些 GAE 服务,甚至 Cloud Function (in which case using a Storage event trigger 将消除显式轮询数据集更新条件的需要)。
我终于找到了一种通过使用 Python API 发现客户端和服务帐户以编程方式重新启动所有实例的方法。 它首先获取活动实例列表并删除所有实例。然后,执行一个简单的请求来启动其中之一。
import requests
from apiclient.discovery import build
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('credentials.json')
scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/appengine.admin',"https://www.googleapis.com/auth/cloud-platform"])
appengine = build(serviceName="appengine",version="v1",credentials=scoped_credentials)
VERSION_ID = "version_id"
PROJECT_ID = "project_id"
SERVICE_ID = "appengine_service_name"
APP_URL = "http://some_url.com"
active_instances_dict = appengine.apps().services().versions().instances().list(servicesId=SERVICE_ID,appsId=PROJECT_ID,versionsId=VERSION_ID).execute()
list_of_instances = active_instances_dict["instances"]
for instance in list_of_instances:
appengine.apps().services().versions().instances().delete(servicesId=SERVICE_ID,appsId=PROJECT_ID,
versionsId=VERSION_ID,instancesId=instance["id"]).execute()
requests.get(url=APP_URL)