Go 博客

App Engine SDK 和工作区 (GOPATH)

Andrew Gerrand
2013 年 1 月 9 日

简介

当我们发布 Go 1 时,我们引入了 go 工具,以及工作区的概念。工作区(由 GOPATH 环境变量指定)是组织代码的一种约定,它简化了 Go 包的获取、构建和安装。如果您不熟悉工作区,请阅读 本文 或观看 此屏幕截图,然后再继续阅读。

直到最近,App Engine SDK 中的工具才没有意识到工作区的存在。如果没有工作区,“go get”命令将无法工作,因此应用程序作者必须手动安装和更新其应用程序依赖项。这很麻烦。

App Engine SDK 的 1.7.4 版本改变了这一切。 dev_appserverappcfg 工具现在都支持工作区。在本地运行或上传应用程序时,这些工具现在会在 GOPATH 环境变量指定的工作区中搜索依赖项。这意味着您现在可以在构建 App Engine 应用程序时使用“go get”,并且可以在普通 Go 程序和 App Engine 应用程序之间切换,而无需更改您的环境或习惯。

例如,假设您想构建一个使用 OAuth 2.0 对远程服务进行身份验证的应用程序。一个流行的 Go OAuth 2.0 库是 oauth2 包,您可以使用以下命令将其安装到您的工作区

go get golang.org/x/oauth2

在编写您的 App Engine 应用程序时,就像在常规 Go 程序中一样导入 oauth 包

import "golang.org/x/oauth2"

现在,无论使用 dev_appserver 运行应用程序还是使用 appcfg 部署应用程序,这些工具都将在您的工作区中找到 oauth 包。它可以正常工作。

混合独立/App Engine 应用程序

Go App Engine SDK 构建在 Go 的标准 net/http 包的基础上,用于提供 Web 请求服务,因此许多 Go Web 服务器只需进行少量更改即可在 App Engine 上运行。例如,godoc 作为独立程序包含在 Go 发行版中,但它也可以作为 App Engine 应用程序运行(godoc 从 App Engine 提供 golang.org 的服务)。

但是,如果您能够编写一个既是独立 Web 服务器又是 App Engine 应用程序的程序,那不是很好吗?通过使用 构建约束,您可以做到这一点。

构建约束是确定是否应将文件包含在包中的行注释。它们最常用于处理各种操作系统或处理器架构的代码。例如,path/filepath 包包含文件 symlink.go,该文件指定了一个构建约束,以确保它不会在 Windows 系统上构建(Windows 系统没有符号链接)

// +build !windows

App Engine SDK 引入了一个新的构建约束术语:“appengine”。指定以下内容的文件

// +build appengine

将由 App Engine SDK 构建,而 go 工具将忽略它们。相反,指定以下内容的文件

// +build !appengine

会被 App Engine SDK 忽略,而 go 工具将愉快地构建它们。

goprotobuf 库使用此机制为其编码/解码机制的关键部分提供了两种实现: pointer_unsafe.go 是速度更快的版本,不能在 App Engine 上使用,因为它使用了 unsafe 包,而 pointer_reflect.go 是速度较慢的版本,它通过使用 reflect 包 而不是 unsafe 来避免 unsafe。

让我们以一个简单的 Go Web 服务器为例,并将其转换为混合应用程序。这是 main.go

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe("localhost:8080", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

使用 go 工具构建它,您将获得一个独立的 Web 服务器可执行文件。

App Engine 基础设施提供了自己的 main 函数,该函数运行其等效于 ListenAndServe 的功能。要将 main.go 转换为 App Engine 应用程序,请删除对 ListenAndServe 的调用,并在 init 函数(在 main 之前运行)中注册处理程序。这是 app.go

package main

import (
    "fmt"
    "net/http"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

要将其转换为混合应用程序,我们需要将其拆分为 App Engine 特定的部分、独立二进制文件特定的部分以及两个版本共有的部分。在本例中,没有 App Engine 特定的部分,因此我们将其拆分为两个文件

app.go 指定并注册了处理程序函数。它与上面的代码清单相同,并且不需要构建约束,因为它应该包含在程序的所有版本中。

main.go 运行 Web 服务器。它包含“!appengine”构建约束,因为它必须仅在构建独立二进制文件时包含。

// +build !appengine

package main

import "net/http"

func main() {
    http.ListenAndServe("localhost:8080", nil)
}

要查看更复杂的混合应用程序,请查看 present 工具

结论

我们希望这些更改将使使用外部依赖项的应用程序更容易开发,并更容易维护包含独立程序和 App Engine 应用程序的代码库。

下一篇文章:并发不是并行
上一篇文章:最近两次 Go 演讲
博客索引