My favorites | 中文(简体) | Sign in

请求和应用程序缓存

Python 运行时环境会在单个网络服务器上的请求之间对导入的模块进行缓存,类似于独立 Python 应用程序仅加载一次模块的方式(即使模块由多个文件导入)。如果处理程序脚本提供 main() 例行程序,则运行时环境也会缓存脚本。否则,就会对每个请求加载处理程序脚本。

应用程序缓存在响应时间方面有明显的优势。我们建议所有的应用程序都使用 main() 例行程序,如下所述。

缓存导入的内容

为了提高效率,网络服务器会将导入的模块保存在内存中,并且对于同一服务器上的相同应用程序的后续请求,就不再重新加载或重新评估这些模块。大多数模块不会初始化任何全局数据,或在导入时没有其他副作用,所以缓存它们不会更改应用程序的行为。

如果您的应用程序导入的模块取决于针对每个请求进行评估的模块,则应用程序必须调整该缓存行为。

以下示例演示了缓存导入的模块的方式。由于 mymodule 只对一个网络服务器导入一次,所以全局 mymodule.counter 只会对服务器提出的第一次请求初始化为 0。后续请求则使用来自前一个请求的值。

### mymodule.py
counter = 0
def increment():
  global counter
  counter += 1
  return counter


### myhandler.py
import mymodule

print "Content-Type: text/plain"
print ""
print "My number: " + str(mymodule.increment())

这会输出 My number: #,其中 # 是处理请求的网络服务器调用该处理程序的次数。

处理程序脚本也可以进行缓存

您可以让 App Engine 除缓存导入模块之外,还对处理程序脚本本身进行缓存。如果处理程序脚本定义了一个名为 main() 的函数,则会缓存脚本及其全局环境,就像缓存导入的模块一样。指定网络服务器上脚本的第一个请求会正常评估脚本。对于后续请求,App Engine 则会调用缓存的环境中的 main() 函数。

要缓存处理程序脚本,App Engine 必须能够调用不带参数的 main()。如果处理程序脚本没有定义 main() 函数,或 main() 函数需要参数(没有默认值),则 App Engine 将针对每个请求加载和评估整个脚本。

将解析的 Python 代码保留在内存中可节省时间并加快响应的速度。缓存全局环境也有其他潜在作用:

  • 编译的正则表达式。所有正则表达式都以编译的形式进行解析和存储。您可以在全局变量中存储编译的正则表达式,然后使用应用程序缓存以在请求之间重复使用编译的对象。
  • GqlQuery 对象。创建 GqlQuery 对象时,会解析 GQL 查询字符串。重复使用具有参数绑定和 bind() 方法的 GqlQuery 对象要比每次都重新构建对象更快。您可为全局变量中的值存储具有参数绑定的 GqlQuery 对象,然后通过对每个请求绑定新参数值来重复使用该对象。
  • 配置和数据文件。如果您的应用程序加载和解析来自文件的配置数据,它可以在内存中保留解析的数据,以免对每个请求都重新加载文件。

以下示例使用处理程序脚本的全局环境的缓存,可实现与前面的示例相同的操作:

### myhandler.py

# A global variable, cached between requests on this web server.
counter = 0

def main():
  global counter
  counter += 1
  print "Content-Type: text/plain"
  print ""
  print "My number: " + str(counter)

if __name__ == "__main__":
  main()

注意:请勿在请求之间 [泄漏] 用户特定的信息。除非需要缓存,否则请避免使用全局变量,且一律在 main() 例行程序内初始化请求特定的数据。

具有 main() 的应用程序缓存在应用程序的响应时间方面有明显改善。我们建议将其用于所有应用程序。