読者です 読者をやめる 読者になる 読者になる

write ahead log

ロールフォワード用

golang + Google App Engineを試したかったので掲示板を作った

golang GAE 作った/試した

ふと, Google App Engine(GAE)を試してみたかったのでなにか作ろうと思った.

「掲示板が作れれば大体なんとかなる」とか言いますし,とりあえず掲示板を作りました.

出来たものは以下.適当に時間が経って邪魔になったら多分インスタンス消します.

app engine - golang BBS

ソースコードは以下です.

github.com

あとは以下の感じで覚書です.

  1. 環境構築の方法
  2. Hello,worldする
  3. デプロイしてHello,worldする
  4. Datastoreを使う(掲示板アプリのちょっとした解説)

1. 開発環境の構築

やることは以下です.

  1. SDKのインストール
  2. Googleアカウントの準備

流石にGoogleアカウントが無いってことはないだろうということで,SDKだけメモります. ちなみに試した環境はUbuntu16.04 LTSです.

1-1. SDKをダウンロード

以下にリンクがあるのでダウンロードします.

Quickstart for Go App Engine Standard Environment  |  App Engine standard environment for Go  |  Google Cloud Platform

ちなみにクイックスタートなのでここの通りにやればHello, world出来ます.流石Googleです.

1-2. 解凍して配置

ダウンロードしたディレクトリを解凍して,適当な場所に配置します. 僕は面倒なのでHOMEに直で置きました.

1-3. PATHの設定

上記までで解凍・配置したディレクトリにパスを通します. 僕の場合は.bashrcに以下を書いています.

export PATH=$PATH:$HOME/go_appengine

ここまでで完了ですが,念の為コマンドを動かしておきます.

$ goapp version

上記でダメな場合はPythonのバージョンが大事なそうなので確認するほうがよいかもしれません.

/usr/bin/env python -V

公式曰く, 2.7系でないとダメだそうです.

2. Hello worldする

公式に記載がありますが,一応書いておきます.

GitHubに公式のリポジトリが用意されています.

GitHub - GoogleCloudPlatform/appengine-guestbook-go at part1-helloworld

転載させてもらうと以下の通りの非常に普通のgoコードです.

package hello

import (
    "fmt"
    "net/http"
)

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

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

これを書いて以下のように配置します.

go_appengine(DLして解凍してPATH通したディレクトリ)
  |
  |_ goroot
         |
         |_hello
              |
              |_ hello.go
              |_ app.yaml

helloディレクトリの下へ移動して以下のコマンドを実行するとローカルで動作確認ができます.

$ goapp serve

あとはlocalhost:8000へアクセスすれば世界に挨拶できます.

3. GAE上でHello worldする

3-1.プロジェクトを作る

Google Cloud Consoleへアクセスしましょう.

Google Cloud Platform

IAMと管理 => すべてのプロジェクト => プロジェクトの作成

でプロジェクトが作れます.

ここで作成したプロジェクトIDをapp.yamlに記述することになります.

3-2. デプロイ

まず,app.yamlを編集します.

application: [登録したプロジェクトID]
version: 1
runtime: go
api_version: go1
handlers:
-   url: /.*
    script: _go_app

公式だとappcfg.pyを使えとなってますね.

僕は色々な他サイトも合わせて見ていたので直接yamlを編集しました.

あとはデプロイのコマンドをコンソールから入力するだけです.

$ goapp deploy

初回は認証が必要です.

デプロイが済んだら以下のようなURLでアクセスします.

http://[登録したプロジェクトID].appspot.com/.

これでGAE上でHello worldできます.

4.Datastoreを使う(掲示板アプリの解説を若干)

ここまで来ると普通のgolangでのWebアプリケーション開発と変わらないのですが, データの永続化にDatastoreを使うのにはやはり少し調べる必要がありました.

とはいえまぁ,安いのでDatastoreを使うのは良いかと思います. 安いし早いし癖はありますが良いみたいですので.なにせ無料枠がありますからね.

App Engine の料金  |  App Engine Documentation  |  Google Cloud Platform

CRUDが出来ればなんとかなるはずなのでコードを記載して簡単にメモ書きしておきます. 詳細は冒頭でリンクを貼ったGitHubリポジトリへどうぞ.

なお,以下のコードは下記の構造体が用意されていることが前提です.

type Comment struct {
    Id int64 `datastore:"-"`
    HandleName string
    Comment string
    Like int64
    EntryTime time.Time
}

また,GAE on Goではリクエストパラメータからコンテキストを生成して,これをたらい回しにして 使うことになります.

c := appengine.NewContext(r)

なので以下のコードでcという名の変数があればすべてコンテキストです.

4-1. Create
        var com *Comment
    key := datastore.NewIncompleteKey(c, "comment", nil)
    key, err := datastore.Put(c, key, com)

NewIncompleteKeyメソッドでコンテキストとkind(2番目のパラメータ.テーブル名みたいなもん)を渡して上げてキーを作成します. その後Putメソッドでキー・バリューを対応付けして値を登録といった感じ.

4-2. Refere(複数件)
   q := datastore.NewQuery("comment").Order("-EntryTime")

    comments := make([]Comment, 0)
    iter := q.Run(c)
    for {
        var com Comment
        key, err := iter.Next(&com)
        if err == datastore.Done {
            break
        } else if err != nil {
            return nil, err
        }
        com.Id = key.IntID()
        comments = append(comments, com)
    }

NewQueryでkind指定ですべて取得しています.

Orderでソートできますが,先頭に-をつけると降順になるようです.

Queryを実際に実行するにはq.Runでイテレータを取って,iter.Nextで実際に値を取得します.

イテレータの完了はdatastore.Doneをiter.Nextの戻り値のerrと比較すれば判断出来ます.

iter.NextはKeyも返しますが,key.IntIDでキーのint値を得ることができます.

これで特定の値を一意に特定できるのでフォームに埋め込んでやったりと何かと便利です.

4-3. Refere(単一)
   keyInt, err := strconv.ParseInt(keyStr, 10, 64)
    if err != nil {
        return err
    }
    key := datastore.NewKey(c, "comment", "", keyInt, nil)

    var com Comment
    err = datastore.Get(c, key, &com)

単一のデータを得るときにはキーIDからキーオブジェクトを新たに作成し(NewKey)これを使ってGetを呼び出すことで得られます.

キーIDは上記のkey.IntIDでの情報ですね.

4-4. Update

RefereとCreateの組み合わせなので省略

4-5. Delete
   q := datastore.NewQuery("comment").Filter("__key__=", key).KeysOnly()
    iter := q.Run(c)
    var com Comment
    _, err = iter.Next(&com)
    if err == datastore.Done {
        return fmt.Errorf("key not found")
    }
    if err != nil {
        return err
    }
    if err = datastore.Delete(c, key); err != nil {
        return err
    }

Deleteを使います.

削除するオブジェクトのキーを得るのに上記のコードではNewQueryでFilterを利用しています.

Filterはその名の通り構造体のメンバでフィルターをかけれるメソッドですが, key予約語になっていて キーとマッチさせるようです.

またKeysOnlyメソッドを使っていますが,これはキーのみを取得するため,若干パフォーマンスが良くなるようです.

その他

開発中,ローカルでデータを見たくなると思いますが,そんな時は

$ goapp serve

の後に

localhost:8080

へブラウザでアクセスするとローカルのDatastoreの中身を見ることができてとても便利です.

まとめ

解説らしい解説をほぼ入れていないので, 実際はソースを見て頂きたいところです.

とはいえ

  • 割と簡単に使えて
  • (ある程度は)無料で
  • パフォーマンスも良い(課金すれば青天井)

というのは素晴らしい環境です.

次は認証API全文検索APIを使ってみたいなぁと思います.