Go 部落格

App Engine SDK 和工作區 (GOPATH)

Andrew Gerrand
2013 年 1 月 9 日

引言

釋出 Go 1 時,我們引入了 go tool,並隨之引入了工作區的概念。工作區(由 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 系統(沒有符號連結)上構建:

// +build !windows

App Engine SDK 引入了一個新的構建約束項:“appengine”。指定以下內容的檔案:

// +build appengine

將由 App Engine SDK 構建,並被 go tool 忽略。相反,指定以下內容的檔案:

// +build !appengine

將被 App Engine SDK 忽略,而 go tool 會很樂意構建它們。

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 tool 構建它,您將獲得一個獨立的 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 tool

結論

我們希望這些更改能讓您更容易地開發具有外部依賴項的應用程式,以及維護包含獨立程式和 App Engine 應用程式的程式碼庫。

下一篇文章:併發不是並行
上一篇文章:近期兩場 Go 講座
部落格目錄