将 App Engine 应用从 Go 1.9 迁移到 Go 1.11

了解如何在 App Engine 标准环境中将现有 App Engine 应用从 Go 1.9 运行时迁移到新的 Go 1.11 运行时。

继续使用 App Engine Go SDK

您可以将 App Engine API 与 Go 1.11 运行时搭配使用。 不过,我们建议您迁移到 Google Cloud 客户端库,这样,您的应用可以移植到任何 Google Cloud 计算服务。

对于需要 appengine 软件包的应用,您可以改为使用公开提供的 google.golang.org/appengine 软件包:

  1. 将现有 import "appengine" 语句更改为:

    import "google.golang.org/appengine"
    
  2. 通过运行以下命令升级 google.golang.org/appengine 软件包:

    如果您使用的是 GOPATH,请使用:

    go get -u google.golang.org/appengine
    

    如果您使用的是 Go 模块,请使用:

    go get google.golang.org/appengine@'>=v1.2.0'
    

对 App Engine Go 1.11 运行时的更改

要使用 App Engine Go 1.11 运行时,您可能必须对现有 App Engine Go 1.9 应用和部署流程进行一些更改。以下是新运行时的主要不同之处:

app.yaml 文件的更改

修改了 app.yaml 配置文件中某些元素的行为:

元素 更改类型 说明
api_version 已弃用
runtime 已修改 更改 runtime 元素以指定 runtime: go111
application_readable 已弃用
builtins 已弃用
includes 已弃用
login 已弃用 Go 1.11 运行时支持 login,但它在未来的 Go 运行时中将不受支持。您应使用其他方法对用户进行身份验证
libraries 已弃用
threadsafe 已弃用 假定所有应用的线程安全,这意味着实例可以同时处理多个请求。如果应用不是线程安全的,请将 max_concurrent_requests 设置为 1。请注意,这可能会显着增加您的费用。
skip_files 已弃用 请改为在 app.yaml 文件所在的根目录中创建 .gcloudignore 文件。如需了解详情,请参阅 .gcloudignore 参考文档

如需了解详情,请参阅 app.yaml 参考文档

创建 main 软件包

您的服务必须至少在一个源文件中包含 package main 语句。

编写 main 软件包

如果您的服务尚不包含 main 软件包,请添加 package main 语句并编写 main() 函数。main() 函数至少应该:

  • 读取 PORT 环境变量并调用 http.ListenAndServe() 函数:

    port := os.Getenv("PORT")
    if port == "" {
    	port = "8080"
    	log.Printf("Defaulting to port %s", port)
    }
    
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
    	log.Fatal(err)
    }
  • 或者,如果您的服务使用的是 google.golang.org/appengine 软件包,则调用 appengine.Main()

注册 HTTP 处理程序

您可以通过以下任一方案来注册 HTTP 处理程序:

  • 首选方法是手动将您的软件包中的所有 http.HandleFunc() 调用移入您的 main 软件包中的 main() 函数。
  • 另一种方法是将应用的软件包导入 main 软件包,并确保每个 init() 函数(包含对 http.HandleFunc() 的调用)在启动时运行。

    您可以通过以下 bash 脚本找到所有使用 http.HandleFunc() 调用的软件包,并将输出复制到 main 软件包的 import 块中:

    gp=$(go env GOPATH) && p=$(pwd) && pkg=${p#"$gp/src/"} && find . -name "*.go" | xargs grep "http.HandleFunc" --files-with-matches | grep -v vendor/ | grep -v '/main.go' | sed "s#\./\(.*\)/[^/]\+\.go#\t_ \"$pkg/\1\"#" | sort | uniq
    

构建文件

Go 要求每个软件包都位于自己的目录中,所以,如果您目前的文件结构类似于以下结构:

  • myapp/
    • app.yaml
    • bar.go
    • foo.go

您需要对结构进行更改,使 main 软件包与 app.yaml 文件位于不同的目录中。您的最终文件结构应如下所示:

  • myapp/
    • bar.go
    • foo.go
    • web/
      • app.yaml
      • main.go

将文件移动到 GOPATH

使用以下命令查找您的 GOPATH

go env GOPATH

将所有相关文件和导入内容移动到 GOPATH。如果使用相对导入(例如 import ./guestbook),请将您的导入命令更新为使用完整路径:import github.com/example/myapp/guestbook

从 App Engine Go SDK 迁移(可选)

强烈建议您使用 Google Cloud 客户端库或第三方库,不要使用旧版 App Engine 专属 API

并发设置

Go 1.11 运行时会忽略项目的 app.yaml 文件中的 threadsafe 值。您可以使用 app.yaml 文件中的 automatic_scaling: max_concurrent_requests 设置来指定并发设置。

Go 1.9 运行时存在一个已知问题,即它会忽略 max_concurrent_requests 设置。Go 1.11 和较新的运行时遵循项目的 app.yaml 文件中 max_concurrent_requests 设置的值。

如果将 max_concurrent_requests 的值设置为小于 10,则您的应用可能需要更多实例。我们建议您将 max_concurrent_requests 的值设置为 10 或更高。