Go 博客
App Engine SDK 和工作区 (GOPATH)
引言
当我们发布 Go 1 时,我们引入了 go 工具,以及工作区的概念。工作区(由 GOPATH 环境变量指定)是组织代码的约定,可以简化 Go 包的获取、构建和安装。如果您不熟悉工作区,请在继续阅读之前先阅读 这篇文章 或观看 这个屏幕录像。
直到最近,App Engine SDK 中的工具都无法感知工作区。如果没有工作区,“go get” 命令无法正常工作,因此应用作者不得不手动安装和更新其应用依赖项。这很麻烦。
随着 App Engine SDK 1.7.4 版本的发布,这一切都改变了。dev_appserver 和 appcfg 工具现在都支持工作区。在本地运行或上传应用时,这些工具现在会在 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。
让我们来看一个简单的 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 讲座
博客索引