前回の記事でGo言語の第一印象とチュートリアルに関する気づきをまとめました。
今回はそれに引き続き、Google App Engineに関する気づきや詰まった点をまとめていきます。
Google App Engineに触れる
現在Google App Engineでは、java、python、PHP、Goをサポートしていますが、スピンアップが最も高速に立ち上がるのがGo言語のようです。
そのため、多くの方がGo言語をGoogle App Engineへの利用を前提として使っているのではないでしょうか。
インストールや実行に関してはGoogle App Engine SDKなどを参照していただくことにして、実際に実装してきた中で問題となった箇所やその解決策を記載していきます。
json parse
webサービスの要件にrequest bodyにjsonがくるのでそれをparseして値を適宜処理するというものがありました。
他の言語やプラットフォームだと、body parserやjson parserがあり思っていた通りの動作をしてくれることが多いのですが、Google App Engineにはbody(io.Reader interfaceを持つ)を"encoding/json"
のjson.NewDecoder
へ渡し、struct
へ変換するという流れを取ります。
サンプルコードは以下のような形になります。
import (
"encoding/json"
"io"
)
type Data struct {
Type int `json:"type"`
Value string `json:"value"`
}
func ParseJson(r io.Reader) (*Data, error) {
decoder := json.NewDecoder(r)
data := Data{Type: -1}
for {
if err := decoder.Decode(&data); err == io.EOF {
break
} else if err != nil {
return nil, err
}
}
return data, nil
}
こんな感じになります。
struct
に`json:"type"`
というようなtagをつけることにより、jsonのnameをstruct
内のfieldにマッピングします。データの方はstructと一致している必要があります。
問題1. 文字列と数値
jsonではvalueに数値がある場合には”“で囲まないようです。そのため、”“が入力値となっている値をintにマッピングしようとするとエラーを出力します。(個人的にはzero valueという仕組みを持っているのだから0入れておいてよと思いますが。)
これはクライアントアプリとの仕様の問題なので、今回は全てstringで値をもらい、後から全て変換する方針にしました。
問題2. マッピングが失敗する
これの解決が一番時間がかかりました。ある特定のfieldだけ値のマッピングがされないという事象に遭遇したのです。
よく見てみるとtagの中にスペースが入っていることが分かり、これを消せばマッピングがうまく動いてくれました。
json: "type"
→json:"type"
です。
カスタムヘッダー
今回オリジナルのヘッダーをクライアントが送信してくるので、サーバーがそれを取得して値を使うという実装がありました。実装自体はそんなに難しくなく、SDKもきちんとHeaderを取得するためのインターフェースを備えていました。
ソースコードしては以下のような感じです。
const MY_HEADER = "x-my-header"
func GetHeader(r *http.Request) string {
h := r.Header[MY_HEADER]
if h == nil {
return ""
}
if len(h) == 0 {
return ""
}
return h[0]
}
問題. カスタムヘッダーが取得できない
実際にテストコードは動き、ローカルサーバーも無事に動作することを確認しました。問題は本番環境にデプロイした後に発生しました。
Headerの取得ができない!!
Headerの内容をログに書き出してみると、Google App Engine内ではHeader Keyがキャメルケースになるようです。(RFCを読んでいないので詳細はわからないのですが)
そのため上記サンプルのx-my-header
はX-My-Header
に修正する必要があります。
気をつけましょう。
datastore
問題. datastore console viewが正常に動作しない
ある構成のstructをdatastoreに入れたところ、console viewからデータが確認できなくなってしまいまいした。
これに関しては現在調査中で、実際に値が入っているかどうかはwebサービスにendpointを一つ用意して、そのendpointロジック内でqueryを発行して結果を出力して確認できるようにしましたが、いまいちです。
情報. Ancestor(祖先)は自動生成
datastoreの概念にancestorというentityのグルーピングや親子関係を表現するための仕組みがあります。ソースコードではdatastore.NewKey(c, "datastorename", "", 0, ancestor)
のようにkeyを生成する際に第5引数に入れます。
このancestor keyに関しては、もし存在しない場合には自動で生成されるので、先に作っておいたりする必要はありません。
interface{}型
Go言語でのinterfaceの定義の仕方は
type Interfacer interface {
Method() string
}
func (d *Data) Method() string {
return "hogehoge"
}
このような形でした。interfaceとはメソッド群を定義しています。言語仕様を読むと、全ての型はinterface{}
という空のinterfaceを実装しているとの記載があります。
このinterface{}
はjavaでいうObject型のような扱いをすることができ、かなり抽象度を上げた取り扱いをすることができます。
また、以下のような形でinterface型から値を取り出し、 それぞれの型に適した処理を実装することができます。
switch i := x.(type) { // xがinterface{}型
case nil:
printString("x is nil")
case int:
printInt(i) // iはint
case float64:
printFloat64(i) // iはfloat64
case func(int) float64:
printFunction(i) // iは関数
case bool, string:
printString("type is bool or string")
default:
printString("don't know the type")
}
testing
前回の記事でも簡単に取り上げましたが、datastoreのテストをする際に一貫性というキーワードが大切になります。これはdataの性質を表すACIDの一つであり、処理が正しく行われた場合は、その処理は一貫した結果を返すという類のものです。
なので誰かがデータを更新した直後に他の人がデータを読みにいったケースを考えると、一貫性が保たれているシステムの場合は更新したデータを100%読むことができます。
例えば金融機関、人事給与の給与データなどはそういう設計をすべきです。
一貫性が保たれていないシステムの場合は、書き込みの直後に読み取りにいってもデータが変わっていない可能性があります。
例えば、ログデータであったり、チャット内容であったり、DNSレコードの更新であったりは、書き込みしたという事実があれば、読み取りは直後でなくてもよいという性質があります。
さて、Google Cloud Platformのdatastoreは一貫性を保証していません。(厳密言えばqueryにancestorを指定した場合など、強い一貫性を持たせて実行することができる箇所もあります)そのためテストを行う際に問題が生じます。それはテストロジックの最初の方でデータをinputするロジックを追加したのに、テストメソッドの箇所では反映されておらずまともにテストができないということです。
今回これにはまりました。
解決策はaetest.NewInstance
を生成する際にオプションでStronglyConsistentDatastore: true
を追加する必要があります。
確かにこのオプション名は強い一貫性のdatastoreにするという意味になりそうですね。
なのでContext
を生成する部分を以下のように書き換えます。
opt := aetest.Options{
AppID: "test",
StronglyConsistentDatastore: true,
}
inst, _ := aetest.NewInstance(opt)
defer inst.Close()
req, _ := inst.NewRequest("POST", "/", nil)
c := appengine.NewContext(req)
このcを全てに使い回せば、一貫性を保ったテストを記載することができます。
いかがでしたでしょうか。個人的にはGo言語自体は嫌いではないです。ただし、自由度がかなり制限されていると感じています。誰が書いてもしばらくは割と似たようなソースコードになるのではないでしょうか。
また、rubyやnodeに比べると周囲のライブラリがまだ貧弱でやることは少し多くなるのかもしれないなと思います。
ただ、Google App Enginge上での速度は捨てがたいですけどね。
今日はここまでです。
では良いインプットと良いプログラミングを。
0 件のコメント:
コメントを投稿