The Go Blog

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パッケージには、Windows システム (シンボリックリンクがない) でビルドされないようにビルド制約を指定するsymlink.goファイルが含まれています。

// +build !windows

App Engine SDK は、新しいビルド制約用語「appengine」を導入します。以下の指定があるファイルは

// +build appengine

App Engine SDK によってビルドされ、go ツールによって無視されます。逆に、以下の指定があるファイルは

// +build !appengine

App Engine SDK によって無視されますが、go ツールは喜んでそれらをビルドします。

goprotobufライブラリは、このメカニズムを使用して、エンコード/デコードメカニズムの主要部分の2つの実装を提供します。pointer_unsafe.gounsafe パッケージを使用するため App Engine では使用できない高速バージョンですが、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 インフラストラクチャは、ListenAndServe に相当するものを実行する独自のメイン関数を提供します。main.go を App Engine アプリに変換するには、ListenAndServe の呼び出しを削除し、init 関数 (メイン関数が実行される前に実行される) でハンドラーを登録します。これが 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 固有の部分はないため、2つのファイルに分割するだけです。

app.go はハンドラ関数を指定して登録します。これは上記のコードリストと同一であり、プログラムのすべてのバージョンに含める必要があるため、ビルド制約は必要ありません。

main.go は Web サーバーを実行します。これは「!appengine」ビルド制約を含んでいます。なぜなら、スタンドアロンバイナリをビルドするときにのみ含める必要があるからです。

// +build !appengine

package main

import "net/http"

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

より複雑なハイブリッドアプリを見るには、present ツールをご覧ください。

結論

これらの変更により、外部依存関係を持つアプリでの作業が容易になり、スタンドアロンプログラムと App Engine アプリの両方を含むコードベースの保守が容易になることを願っています。

次の記事: 並行性は並列性ではない
前の記事: 最近のGoに関する2つの講演
ブログインデックス