Google Cloud 上的变量生命周期 运行 Python 是什么?
What are variables life time running Python on Google Cloud?
当我在 Google Function
上 运行 一个 Python 程序时,我想每天 一次 从数据库中读取一些数组,并确保它始终可以在下一个 http requests
.
中使用
目前 array
是硬编码的:
array = [a,b,c,d]
我想每天读一次,不是,每次 http request
都想读得更快:
array = read daily from server
问题是 - 假设我今天读了它,我是否保证此变量将可用于所有接下来的 1000 个 http
请求?基本上永远?它的生命周期是多少?
在服务器 restart/built 上为每个请求再次执行 python 程序 就像在我的计算机上一样?
这可能是您想要的:
data = None
def get_data():
global data
if not data:
data = get_data_from_somewhere_slow()
return data
在两次调用之间,实例可能保持活动状态,然后将使用“热”缓存为下一个请求提供服务,其中全局数据保持填充状态。每个实例一个请求并不是严格意义上的,它只是足够真实以至于不值得花太多精力在调用之间持久化数据。
您可以像这样从本地文件(或实际上是数据库)读取然后缓存数据,因此您从中获取数据的“慢”位置是您的本地文件(或数据库)。但是你也可以直接把它放在源代码本身中,并在需要时从那里“读取”它。
您还会遇到这样的问题,即如果不进行部署(因此是数据库)就无法更新本地文件,因为您需要直接更新数据。
因此,如果您乐于进行部署以更新数据,则无需调用数据库即可从本地文件中以超快的速度读取数据。
然而,考虑到世界上几乎每个站点都连接到一个数据库,并且性能可以非常快。那么究竟需要多快才算快?
您要解决什么问题?一次读取某个值(为什么?)或服务速度超过某个速度限制?决定了,你会得到更好的答案。
将 Cloud Functions 实例想象成一个服务器。启动它时,您可以加载所需的全局变量并将该数据保存在内存中。只要您的服务器启动并且 运行,全局范围内的数据就会保留。如果你启动一个新服务器,你必须重新加载那个全局变量,因为它是一个新环境。
Cloud Funcitons 实例的工作方式完全相同。 1 个请求一次只能由 1 个实例处理(对于 v1)。我的意思是,如果您有 2 个并发请求,则会创建 2 个实例,并且每个请求都在其专用实例上处理。
因为您有很多并行实例,所以您可以多次从源加载数据。此外,Cloud Functions 在一段时间后没有任何请求处理(10 到 30 分钟)后被卸载。它可以缩放到 0。
相反,如果您在 Cloud Functions 上有持续的流量,您可以在几小时和几天内保持同一实例和 运行。没有过期,您必须自己管理缓存失效。
最后,因为你在全局范围内加载东西,在启动时,你会减慢冷启动,我的意思是你的云函数启动和服务请求所花费的时间。
听起来你想要的是将这个 1000 键字典硬编码到你的源代码中。
另一种方法是创建一个日常构建过程
- 从数据库中读取您的字典并将其直接写入您的 python 源文件(或您的脚本在启动时读取的附近文件)
- 打包 python 源并部署它替换当前的云函数
现在您的云函数不必担心连接到数据库或使缓存失效。
如果发现您需要更频繁地更新,请将构建从每天更改为每 12 小时或任何最佳时间。
乔什
在对这个问题的不同答案写了十几条评论后,我决定将我的意见统一在一个答案中。
免责声明:很遗憾,我没有工作,而且我从未为 Google 工作过,所以下面的文字是基于阅读文档、白皮书、博客, 和个人经验。我可能错了!!!
免责声明:我尽量简化并省略了很多细节。真正的实现和行为 - 复杂得多。
免责声明:我不知道原问题的具体上下文、范围和要求。因此,我必须应用大量假设。
答案由 3 部分组成 - 云函数状态机的一般描述;一些常见场景的描述;以及一些评论和建议 - 与原始问题相关。
云函数状态机
请参阅图表及其下方的说明。
我建议考虑 3 个状态 - 第一个状态是 'global'(或在多个 run-time containers/environments 之间共享);其他两个 - 每 运行 时间 container/environment - 不是一个理想的描述,但无论如何:
已部署(冷)
- 这是第一个状态——带有各种参数的源代码被部署到GCP项目中。源码已解析,但没有run-time容器,云函数(实例)等待调用。从这个状态 - 云功能可以被初始化,或者完全删除。
已初始化(热)
- 云函数的运行时间环境是在容器中准备的。一次初始化完成。源代码被加载到内存中。该函数已准备好(热)调用。如果初始化包括获取一些外部数据——该数据被复制到云函数实例内存中,因此如果原始数据源稍后被修改,云函数实例副本具有该数据的陈旧版本。请记住,对于给定的云函数(源代码),可能有(几乎)任意数量的已初始化容器。该数字由 Cloud Functions 服务(由 Google)控制,而不是由客户端控制。根据我的经验,云功能可以在没有 'movements' 的情况下保持这种状态很长一段时间 - 可能长达半小时。从此状态可以调用或释放(终止)云函数。
运行
- 在将请求绑定到具有初始化云函数的容器后调用云函数实例。云函数实例可以保持此状态,直到执行完成、超时或 run-time 崩溃发生。从这个状态,云功能可以完成并 return 进入初始化(热)状态,或者可以完全崩溃(我知道在这种情况下整个容器都被杀死了)。
可能会有一些状态转换:
1 - deploy
- 第一个可能的状态转换发生在云功能部署过程中(即源代码、配置和环境解析和检查)。此状态转换导致已部署(冷)状态。
2 - initialize
- 当 Cloud Function 服务 (Google) 决定使用所选的云函数初始化新容器时发生。初始化总是从最新的可用源代码开始(加载到容器内存中并在那里可用)。在此过程中,执行 'global'(在云函数入口点函数之外)代码 - 例如,在 Go 语言中执行 init
函数;以及各种全局变量的初始化。此状态转换导致初始化(热)状态。
3 - invoke
- 当 Cloud Function 服务 (Google) 将传入请求与可用的(免费)初始化(热)云函数实例绑定并调用入口点函数时发生。此状态转换导致 运行 状态。
4 - finish
- 在云函数完成执行时发生。此状态转换导致初始化(热)状态。
5 - crash
- 当云函数代码内部出现完全错误时发生。我知道在这种情况下整个容器都被杀死了。此状态转换导致已部署(冷)状态。
6 - release (kill)
- 当 Cloud Function 服务 (Google) 决定从 运行 时间 container/environment 释放内存时发生。此状态转换导致已部署(冷)状态。
7 - delete
- 当客户端完全删除云功能时发生。
几个场景 - 非常简单的描述
云函数的部署。
我了解 - 最多发生 3 次状态转换 - 1 - deploy
; 2 - initialize
;如果没有立即调用云函数实例,则可选 6 - release (kill)
。请记住,部署过程不会影响云 f已经在 运行 时间容器中的功能 - INITIALIZED (HOT) 和 运行 状态。他们可以在这些状态('invocation' 和 'finish' 状态转换)中继续上下波动一段时间 - 几分钟,几小时。但是,下一次初始化(运行 时间容器 - 'initialism' 状态转换)发生在已部署源代码的最新版本中。请记住 - 在 'hot' 容器中用新代码替换旧代码 - 需要时间 - 几分钟,甚至几小时。
调用云函数。
Cloud Function 服务 (Google) 检查是否有具有 INITIALIZED (HOT) 云函数的容器。如果存在这样的云函数实例 - 调用它。否则检查可能的云函数实例的最大数量。如果阈值未存档 - 'initialize' 来自最新可用的 DEPLOYED (COLD) 状态(最新部署的代码)的云函数,并在初始化时调用它。
云函数容器终止。
Cloud Function 服务 (Google) 检查是否有一个具有 INITIALIZED (HOT) 云函数的容器,该容器有一段时间未被调用(从几个 seconds/minutes 可能最多半小时)。并杀死它,释放所有内存。如果该容器云功能实例在其伪目录 temp
中有任何内容 - 所有这些也都消失了。
关于原题的几点评论
云函数是无状态的。并且应该是幂等的。它们的行为完全依赖于外部数据(它要么随请求而来,要么在请求处理期间获取数据)。
随意使用伪文件系统(tmp
目录),但当云函数运行时,不要指望那里有任何东西(也不要使用在那里找到的任何数据)实例被调用。最佳实践 - 在完成云函数实例调用执行之前删除那里的所有内容。
随时用源代码打包任何东西,并在 运行 时使用它。但请做好准备 - 该数据与您的 运行ning 代码同龄。最近部署的代码(及其辅助数据)可以比正在执行的代码提前几个版本。
初始化越少 - 2 - initialize
状态转换完成得越快,云函数进入初始化(热)状态的速度就越快。只有不可变的东西值得初始化 - 全局常量,一些 API 客户端。普通初始化和“惰性”初始化之间没有 material 区别(在某些非常特殊的特殊情况下可能会有区别)。
配置和缓存 - 是外部的,在云功能之外。您必须在每次调用时从它们那里获取数据。也许也更新它们。所有这些 - 就像流程的状态 - 应该在云功能之外。
使用外部 API 是不可避免的。如果需要一些缓存并且延迟很关键 - 使用 Go 而不是 Python,找到一些延迟最小的存储(即 Firestore 可能是一个很好的选择),并将持久数据保存在那里。
一个云功能(或一个功能组件)- 一个服务帐户。具有最低 IAM 角色和权限。
等等……
当我在 Google Function
上 运行 一个 Python 程序时,我想每天 一次 从数据库中读取一些数组,并确保它始终可以在下一个 http requests
.
目前 array
是硬编码的:
array = [a,b,c,d]
我想每天读一次,不是,每次 http request
都想读得更快:
array = read daily from server
问题是 - 假设我今天读了它,我是否保证此变量将可用于所有接下来的 1000 个 http
请求?基本上永远?它的生命周期是多少?
在服务器 restart/built 上为每个请求再次执行 python 程序 就像在我的计算机上一样?
这可能是您想要的:
data = None
def get_data():
global data
if not data:
data = get_data_from_somewhere_slow()
return data
在两次调用之间,实例可能保持活动状态,然后将使用“热”缓存为下一个请求提供服务,其中全局数据保持填充状态。每个实例一个请求并不是严格意义上的,它只是足够真实以至于不值得花太多精力在调用之间持久化数据。
您可以像这样从本地文件(或实际上是数据库)读取然后缓存数据,因此您从中获取数据的“慢”位置是您的本地文件(或数据库)。但是你也可以直接把它放在源代码本身中,并在需要时从那里“读取”它。
您还会遇到这样的问题,即如果不进行部署(因此是数据库)就无法更新本地文件,因为您需要直接更新数据。
因此,如果您乐于进行部署以更新数据,则无需调用数据库即可从本地文件中以超快的速度读取数据。
然而,考虑到世界上几乎每个站点都连接到一个数据库,并且性能可以非常快。那么究竟需要多快才算快?
您要解决什么问题?一次读取某个值(为什么?)或服务速度超过某个速度限制?决定了,你会得到更好的答案。
将 Cloud Functions 实例想象成一个服务器。启动它时,您可以加载所需的全局变量并将该数据保存在内存中。只要您的服务器启动并且 运行,全局范围内的数据就会保留。如果你启动一个新服务器,你必须重新加载那个全局变量,因为它是一个新环境。
Cloud Funcitons 实例的工作方式完全相同。 1 个请求一次只能由 1 个实例处理(对于 v1)。我的意思是,如果您有 2 个并发请求,则会创建 2 个实例,并且每个请求都在其专用实例上处理。
因为您有很多并行实例,所以您可以多次从源加载数据。此外,Cloud Functions 在一段时间后没有任何请求处理(10 到 30 分钟)后被卸载。它可以缩放到 0。
相反,如果您在 Cloud Functions 上有持续的流量,您可以在几小时和几天内保持同一实例和 运行。没有过期,您必须自己管理缓存失效。
最后,因为你在全局范围内加载东西,在启动时,你会减慢冷启动,我的意思是你的云函数启动和服务请求所花费的时间。
听起来你想要的是将这个 1000 键字典硬编码到你的源代码中。
另一种方法是创建一个日常构建过程
- 从数据库中读取您的字典并将其直接写入您的 python 源文件(或您的脚本在启动时读取的附近文件)
- 打包 python 源并部署它替换当前的云函数
现在您的云函数不必担心连接到数据库或使缓存失效。
如果发现您需要更频繁地更新,请将构建从每天更改为每 12 小时或任何最佳时间。 乔什
在对这个问题的不同答案写了十几条评论后,我决定将我的意见统一在一个答案中。
免责声明:很遗憾,我没有工作,而且我从未为 Google 工作过,所以下面的文字是基于阅读文档、白皮书、博客, 和个人经验。我可能错了!!!
免责声明:我尽量简化并省略了很多细节。真正的实现和行为 - 复杂得多。
免责声明:我不知道原问题的具体上下文、范围和要求。因此,我必须应用大量假设。
答案由 3 部分组成 - 云函数状态机的一般描述;一些常见场景的描述;以及一些评论和建议 - 与原始问题相关。
云函数状态机
请参阅图表及其下方的说明。
我建议考虑 3 个状态 - 第一个状态是 'global'(或在多个 run-time containers/environments 之间共享);其他两个 - 每 运行 时间 container/environment - 不是一个理想的描述,但无论如何:
已部署(冷)
- 这是第一个状态——带有各种参数的源代码被部署到GCP项目中。源码已解析,但没有run-time容器,云函数(实例)等待调用。从这个状态 - 云功能可以被初始化,或者完全删除。
已初始化(热)
- 云函数的运行时间环境是在容器中准备的。一次初始化完成。源代码被加载到内存中。该函数已准备好(热)调用。如果初始化包括获取一些外部数据——该数据被复制到云函数实例内存中,因此如果原始数据源稍后被修改,云函数实例副本具有该数据的陈旧版本。请记住,对于给定的云函数(源代码),可能有(几乎)任意数量的已初始化容器。该数字由 Cloud Functions 服务(由 Google)控制,而不是由客户端控制。根据我的经验,云功能可以在没有 'movements' 的情况下保持这种状态很长一段时间 - 可能长达半小时。从此状态可以调用或释放(终止)云函数。
运行
- 在将请求绑定到具有初始化云函数的容器后调用云函数实例。云函数实例可以保持此状态,直到执行完成、超时或 run-time 崩溃发生。从这个状态,云功能可以完成并 return 进入初始化(热)状态,或者可以完全崩溃(我知道在这种情况下整个容器都被杀死了)。
可能会有一些状态转换:
1 - deploy
- 第一个可能的状态转换发生在云功能部署过程中(即源代码、配置和环境解析和检查)。此状态转换导致已部署(冷)状态。
2 - initialize
- 当 Cloud Function 服务 (Google) 决定使用所选的云函数初始化新容器时发生。初始化总是从最新的可用源代码开始(加载到容器内存中并在那里可用)。在此过程中,执行 'global'(在云函数入口点函数之外)代码 - 例如,在 Go 语言中执行 init
函数;以及各种全局变量的初始化。此状态转换导致初始化(热)状态。
3 - invoke
- 当 Cloud Function 服务 (Google) 将传入请求与可用的(免费)初始化(热)云函数实例绑定并调用入口点函数时发生。此状态转换导致 运行 状态。
4 - finish
- 在云函数完成执行时发生。此状态转换导致初始化(热)状态。
5 - crash
- 当云函数代码内部出现完全错误时发生。我知道在这种情况下整个容器都被杀死了。此状态转换导致已部署(冷)状态。
6 - release (kill)
- 当 Cloud Function 服务 (Google) 决定从 运行 时间 container/environment 释放内存时发生。此状态转换导致已部署(冷)状态。
7 - delete
- 当客户端完全删除云功能时发生。
几个场景 - 非常简单的描述
云函数的部署。
我了解 - 最多发生 3 次状态转换 - 1 - deploy
; 2 - initialize
;如果没有立即调用云函数实例,则可选 6 - release (kill)
。请记住,部署过程不会影响云 f已经在 运行 时间容器中的功能 - INITIALIZED (HOT) 和 运行 状态。他们可以在这些状态('invocation' 和 'finish' 状态转换)中继续上下波动一段时间 - 几分钟,几小时。但是,下一次初始化(运行 时间容器 - 'initialism' 状态转换)发生在已部署源代码的最新版本中。请记住 - 在 'hot' 容器中用新代码替换旧代码 - 需要时间 - 几分钟,甚至几小时。
调用云函数。
Cloud Function 服务 (Google) 检查是否有具有 INITIALIZED (HOT) 云函数的容器。如果存在这样的云函数实例 - 调用它。否则检查可能的云函数实例的最大数量。如果阈值未存档 - 'initialize' 来自最新可用的 DEPLOYED (COLD) 状态(最新部署的代码)的云函数,并在初始化时调用它。
云函数容器终止。
Cloud Function 服务 (Google) 检查是否有一个具有 INITIALIZED (HOT) 云函数的容器,该容器有一段时间未被调用(从几个 seconds/minutes 可能最多半小时)。并杀死它,释放所有内存。如果该容器云功能实例在其伪目录 temp
中有任何内容 - 所有这些也都消失了。
关于原题的几点评论
云函数是无状态的。并且应该是幂等的。它们的行为完全依赖于外部数据(它要么随请求而来,要么在请求处理期间获取数据)。
随意使用伪文件系统(
tmp
目录),但当云函数运行时,不要指望那里有任何东西(也不要使用在那里找到的任何数据)实例被调用。最佳实践 - 在完成云函数实例调用执行之前删除那里的所有内容。随时用源代码打包任何东西,并在 运行 时使用它。但请做好准备 - 该数据与您的 运行ning 代码同龄。最近部署的代码(及其辅助数据)可以比正在执行的代码提前几个版本。
初始化越少 -
2 - initialize
状态转换完成得越快,云函数进入初始化(热)状态的速度就越快。只有不可变的东西值得初始化 - 全局常量,一些 API 客户端。普通初始化和“惰性”初始化之间没有 material 区别(在某些非常特殊的特殊情况下可能会有区别)。配置和缓存 - 是外部的,在云功能之外。您必须在每次调用时从它们那里获取数据。也许也更新它们。所有这些 - 就像流程的状态 - 应该在云功能之外。
使用外部 API 是不可避免的。如果需要一些缓存并且延迟很关键 - 使用 Go 而不是 Python,找到一些延迟最小的存储(即 Firestore 可能是一个很好的选择),并将持久数据保存在那里。
一个云功能(或一个功能组件)- 一个服务帐户。具有最低 IAM 角色和权限。
等等……