2014年7月26日土曜日

Golang Cafe #39を開催しました。

Golang Cafe #39を開催しました。

今回も前回に引き続き、Gorilla Toolkitのパッケージを使ってみる会になりました。

使ったのは、gorilla/contextgorilla/sessionsの2つですが、どちらも単純な機能を提供しています。

gorilla/contextの方は、任意の値をcontextに保存しておくだけです。非常に機能が少ないのでサンプルコードの提示も難しいのですが、以下のように使うことができます。

package main

import (
 "fmt"
 "log"
 "net/http"
 "strings"

 "github.com/gorilla/mux"
 "github.com/gorilla/context"

)

func main() {
 r := mux.NewRouter()


 r.HandleFunc("/", IndexHandler)

 log.Fatal(http.ListenAndServe(":8080", r))

}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
 for _, param := range strings.Split(r.URL.RawQuery, "&") {
  params := strings.Split(param, "=")
  context.Set(r, params[0], params[1])
 }

 fmt.Fprintf(w, "IndexHandler\n")
 fmt.Fprintf(w, "hoge = %s\n", context.Get(r, "hoge"))
 fmt.Fprintf(w, "fuga = %s\n", context.Get(r, "fuga"))
}

context.Set(http.Request, キー、値);でリクエストが生きている間はキーと値のペアによって保存されます。非常に単純ですが、パラメータを解析した後、contextの中に保存するなどすれば、便利に使えるのか?と考えていましたが、どうやら、gorillaの他のパッケージの内部で使われているようです。単体で使ったりすることがあるかどうかは、当日の話では出なかったと思います。

次に、gorilla/sessionsを使ってみました。
sessionsを使うポイントは、パッケージ変数にsessions.NewCookieStore()で新しく確保する変数を定義しておきます。
この定義した変数内にsessionの値が保存されることになります。

package main

import (
 "fmt"
 "log"
 "net/http"

 "github.com/gorilla/mux"
 "github.com/gorilla/sessions"

)

func main() {
 r := mux.NewRouter()


 r.HandleFunc("/", IndexHandler)
 r.HandleFunc("/session", SessionHandler)

 log.Fatal(http.ListenAndServe(":8080", r))

}

var store = sessions.NewCookieStore([]byte("something-very-secret"))

func IndexHandler(w http.ResponseWriter, r *http.Request) {
 session, _ := store.Get(r, "mysession")

 session.Values["hoge"] = "1"
 session.Values[2] = 2

 session.Save(r, w)

 fmt.Fprintf(w, "IndexHandler\n")
}

func SessionHandler(w http.ResponseWriter, r *http.Request) {
 session, _ := store.Get(r, "mysession")

 fmt.Println(session)

 hoge := session.Values["hoge"]
 val := session.Values[2]

 session.Values["hoge"] = "session"
 session.Values[2] = 5

 session.Save(r, w)

 fmt.Fprintf(w, "SessionHandler [%s] [%d]\n", hoge, val)

}

動きとしては、sessionの動きと同じだったのですが、
なぜか、

  • サーバを再起動してもsessionが残っている
  • Chromeを閉じてもSessionの値が残っている

という現象に遭遇して、原因を特定する作業に没頭しました。
シークレット・ウインドウを使った場合は、ブラウザを閉じるとsessionは無くなっていました。(Windowsの場合は、シークレット・ウインドウを閉じても残っているということでしたが、環境なのか、設定なのかは特定できていません)

実際、ChromeのDevToolでCookieの中身を確認すると、session IDだけが保存されているかと思っていたら、データがかなり多く、どうやら、Cookieの中に全てのデータが保存されているようです。確かに、GAEなどのように複数のインスタンスが起動するような時に、サーバ側のメモリにのみ保存しておくと、別のインスタンスにリクエストが送られてしまった時に困るのでこういう実装になっているのだろうと考えていました。

が、Cookieのデータは4KBまでという制約があるので、Sessionに何でもかんでも入れておけば良いという考えはやめておきましょう。

次回も、Gorilla Toolkitの残りのパッケージ(reverse、rpc、schema、securecookie)を読み進めます。

2014年7月18日金曜日

Golang Cafe #38を開催しました。

Golang Cafe #38を開催しました。今回からGoのフレームワークを触ってみるというテーマにするということにしました。最初はgorilla toolkitを使ってみようということで、今回は、gorilla/muxを使いました。

gorilla/muxは主にURLからのroutingをサポートします。標準パッケージ(net/http)と同じように使えるので、薄いラッパーのようにも見えますが、強力な機能を持っているようなので、実際に自分のアプリケーションに利用してみようと思っています。

gorillaの使い方ですが、まず、Handlerを登録しておきます。

 r := mux.NewRouter()

 // RouterにHandlerを登録する。
 // 正規表現に一致しないURLは全て404になる。
 // 末尾に"/"をつけると、付けないと404になる。
 // Host()を使うとアクセス制限ができる。
 r.HandleFunc("/", IndexHandler).Host("www.sample.com")
 r.HandleFunc("/articles/{category}/", ArticleHandler).Methods("POST")
 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

 r.NotFoundHandler = http.HandlerFunc(NotFound)

 log.Fatal(http.ListenAndServe(":8080", r))

ここで、http.HandleFunc()を使うのではなく、mux.NewRouter()を呼び出した戻り値のRouter#HandleFunc()を呼び出すのがポイントになります。
muxパッケージを介することで、gorilla toolkitでHandlerを認識することができるようになります。

引数は、http.HandleFunc()と同じなので省略しますが、指定するパスは拡張されていて、正規表現を入れることもできますし、{}で囲んでキーを指定することで、プログラムから指定されたパスの値を取得する事ができるようになります。

コメントにもありますが、Handlerに登録されていないパスのリクエストは全て404が返されます。
(これについては、私は400を返すものと思っていたので404を返す方が良いというのを聞いて新しい発見でした。400だと、存在している事を教えてしまうから、攻撃対象になるということでした。)

最後に、mux.Routerをhttp.ListenAndServe()の引数に指定することで、Routingの設定は完了です。
(GAE/Goだと、http.Handle()の引数に指定すれば同じように反映させる事ができます)

Routing時に登録したキーを取得するためには、mux.Vars()を呼び出します。
戻り値には、map[string] stringが返ってくるので、文字として扱えばPath内にある、キーの値を取得する事ができます。

次に挑戦したのが、NotFoundを返す時にログを出力するというのができるかどうかを試してみました。NotFoundを返すのは良いのですが、アクセスログぐらいは残せた方が便利だという理由からです。

方法は、ソースコードのr.NotFoundHandlerのHandlerを上書きすることで可能になるようです。ただし、上書きしたHandlerの中で、NotFoundのレスポンスを返すように処理を記述しなければいけません。

次回は、gorilla toolkitのcontextとsessionsを見る予定です。

2014年7月12日土曜日

Golang Cafe #37を開催しました。

Golang Cafe #37を開催しました。

今回は、Gmail APIをGoで実行するというテーマで進めました。
OAuth2の認証が必要だったので、#18のサンプルコードを見ながら実行するというアプローチをとりました。

goauth2パッケージは結構便利に使えるので良いですね。

リクエストを送信する所は、以前と同じなので、省略するとして、受信後にデータを展開する所は、レスポンスがjson文字列なので、encoding/jsonを使って、Unmarshal()をすればオブジェクトに復元できます。

最初にUsers.messages.listにリクエストを投げました。これは、Gmailのメールのリストが返ってきますが、本文は含まれていません。

    // Users.messages.listにアクセス
    r, err := transport.Client().Get(request_url)
    if err != nil {
        fmt.Println("Get: ", err)
        return
    }

    defer r.Body.Close()

    buf := bytes.Buffer{}
    if _, err = buf.ReadFrom(r.Body); err != nil {
     log.Fatalln(err)
    }

    if err = json.Unmarshal(buf.Bytes(), &l); err != nil {
     fmt.Println(buf.String())
     log.Fatalln(err)
    }

goauth2のtransportから戻ってくるのはio.Readerなので、bytes.Bufferにでもコピーして書き出さないとUnmarshal()に渡す事ができないので、一つ処理を入れています。

jsonをオブジェクトに変換した後は、もう一度、Users.messages.getにリクエストを投げて、詳細を取得しました。

    // Users.messages.getにアクセス。(最初の1件目)
 r2, err := transport.Client().Get(message_get_url + l.Messages[0].Id)
 if err != nil {
        fmt.Println("Get: ", err)
        return
 }

    // // Write the response to standard output.
    // io.Copy(os.Stdout, r2.Body)

    buf2 := bytes.Buffer{}
    if _, err = buf2.ReadFrom(r2.Body); err != nil {
     log.Fatalln(err)
    }

    if err = json.Unmarshal(buf2.Bytes(), &t); err != nil {
     fmt.Println(buf2.String())
     log.Fatalln(err)
    }

これで、中身が取得できたのですが、本文がBase64エンコードでエンコードされた状態だったので、復元しないといけなかったのですが、復元には至らず。ここで終了。

それでも、後日時間を取って、粘ってみたら、Base64.Stdencodingを使っていたのを、Base64.URLencodingを使えば本文が読めるようになるというのを+Takanobu Haginoさんが見つけていたので、それを適用して本文が読めるようになりました。
    // 本文をエンコードする場合はURLEncodingを使う。
    data, encerr := base64.URLEncoding.DecodeString(string(t.Payload.Body.Data))
    if encerr != nil {
        // fmt.Println("base64", t.Payload.Body.Data[190])
        log.Fatalln(encerr)
    }

あと、q=is:unreadをつけてリクエストを送信したりしたものがありますが、今は時間がないので、あとで、githubに上げておこうと思います。


2014年7月5日土曜日

Golang Cafe #36を開催しました。

Golang Cafe #36を開催しました。

今回は、Google I/O 2014以降公開された動画を見る予定だったのですが、セッションが無い事に気がついていたのですが、いろんな動画が公開されていたので、1個ぐらいはGoの動画があるかと思ったのですが、(DevByteあたりのやつが…)残念ながら全くなかったので、仕方なく、Codelabに一つだけGo関連のものがあったので、Go:Build a Backend on Google App Engineを進めることにしました。

Codelabの内容はGAE/Goのチュートリアルだったので、#34の時にやった公式のチュートリアルと内容が被ってしまったのですが、このCodelabでは、gorillaフレームワークを使ったルーティングの実装があったので、その点が新しいものでした。

あとは、jsonを返すAPIを実装して、クライアント側はAngularJSを使ったAjaxでのやり取りを作り上げるという内容になっていました。

いろいろ説明しつつゆっくり進めたので、今回はStep03まで進めて終了となりました。

WindowsでのGAE/Gの開発はまだまだ苦しいのかなー。と思わされる事例も出てきて、ためになりました。

さて、今後暑い季節がやってきますが、もうすぐ40回目(と言っても、順調にいってまだ1ヶ月先)ですが、いろんな資料を読み進めるというのはだいぶやってきたので、
次回は、GoでGmail APIを実行する。ということになっていますが、以降の数回は、独断と偏見で、Goのいろんなフレームワークを使ってみるというのにしたいと思います。

個人的には、Effective Goをしっかり読んでみたりしたいと思ったりするのですが、
それよりも先に、ここ最近でいろんなフレームワークが開発されてリリースされているのでGopherの端くれとして、一度は触ってみないといけないだろうと思っています。

Pure Goも良いですが、そろそろ幅を広げてみるというのも必要かな…?と思ってみたり。

ということで、明日は、#37で、GoでGmail APIを実行する回です。
恐らく、参加者の皆さんはすでにダウンロード済みだと思いますが、goauth2をダウンロードしておいて下さい。コマンドは

go get code.google.com/p/goauth2/oauth

です。

google-go-api-clientも存在していますが、これだと多分まだ実行できないのではないかと思われるので、少し中身を見るかどうかはまた考えましょう。

2014年6月28日土曜日

Golang Cafe #35を開催しました。

Golang Cafe #35を開催しました。

今回は、GoCon 2014 Springの時に発表されたDave Cheneyさんの資料
読み進めました。

今回の資料は、「Goのプログラムを早くする5つのこと」ということで話されたようです。
最初、参加者の全員が思っていたと思うのですが、「Goのプログラムを早くするために”プログラマ”がすればいいこと」が書かれていると思っていました。

しかし、中身は「Goのコンパイラがコードを解析してコンパイルしてリンクする時にやっていること」が書かれているようでした。
しかも、結構深い内容で、読む方も疲れてしまいました。(私だけかもしれませんが)

5つのうち、2つ目までを読み進めたので、そのまとめを書いておきます。

1つ目は"value"
ポイントは、Goでは、例えばint32の変数だと、正確に4バイトが確保されます。
ですが、Pythonだと、24バイト。Javaだと、16バイト(32bit版)か32バイト(64bit版)が確保されます。同じ、4バイト変数なのに。

で、これが重要な理由はなにか?というと、CPUとメモリのバスの速度差がでてくるようになった背景があって、メモリの転送時間がCPUの処理時間に比べて長いので、CPUが転送待ちになってしまうので、処理速度が上がらないわけです。

そこで、キャッシュの機構をCPUとメモリの間にいれてやれば早くなるわけですが、キャッシュの領域もそこまで大きくない。

ということで、コンパクトな構造体を作り、余計な間接部分を避けるようにGoのコンパイラが良いようにしてくれている。
コンパクトなデータ構造はキャッシュをしっかり使える。
これにより、より良いパフォーマンスを発揮できるようになっている。

ということでした。

2つ目は"inlining"
ここでのポイントは関数呼び出しをインライン化すること。
デッドコードの除去。

1つ目の関数呼び出しのオーバヘッドは回避できないので、Goのコンパイラはinline化できるものは、勝手にinline化しているようです。これにより、関数呼び出しのオーバーヘッドをなくしています。ですが、inline化は、「コピペ」であるわけですから、バイナリサイズは大きくなってしまいます。速度を求めるために、バイナリサイズが大きくなる事に目をつぶっているわけですね。
(個人的には、バイナリサイズが大きくなるとは言え、1ファイルになるのはいいことだと思うけど…。こだわりがある人はまた違う見解があるんだろうか。)

そして、デッドコードの除去に関しては、if(false) { ... }となるようなケースだと、「絶対にif文の中を通らないわけですから、なくしても構わない。ということで、思い切ってコンパイル時には外しているようです。
したがって、Debug用コードを書く場合は、単純にDebugフラグをtrueにして、本番環境ではDebugフラグをfalseにした状態にしてビルドすればいいわけです。
(ただし、オプション引数によってtrue/falseを切り替えるケースでは、デッドコードにならないので、除去されません。)

今回は、結構深くて重たい資料だったと思うのですが、がんばって読んでみると良いものだと思います。が、これは業務とかに利用するのは難しい気が…。

次回は、Google I/O 2014が終わったので、いろんな動画やCodelabの資料が公開されてきているので、動画を見たり、Codelabの資料を追いかける予定です。
(ただし、Goのセッションは今年はなかった…)







2014年6月19日木曜日

Golang Cafe #34を開催しました。

Golang Cafe #34を開催しました。

今回はhtmlday 2014の一環として開催しましたので、GAE/Goのチュートリアルを進めてみました。

内容は

  1. 開発環境の構築
  2. Hello World
  3. ユーザ認証
  4. FormからのPOST
  5. データストアの使い方
  6. deploy
という流れで、私が以前執筆した書籍の頃からあまり変化はなく、GAE/Goに関してはGoの変化に対して大きく変わっていないというのがわかりました。

本当は、世界の+Ryuji IwataさんがGAE/Pythonを使われているようなので、解説をお願いしたかったのですが、どうしても遠慮したいということだったので、未熟者の私が少し解説をさせていただきました。

Hello Worldは、text/plainのデータを出力しますので、文字コードが化ける事があるようでした。ブラウザがShift-Jisで表示しようとしてしまう(?)ようなので、本番では、<meta charaset="UTF-8" />とか、httpヘッダに文字コードを設定するなどが必要になります。

ユーザ認証のためのappengine/userパッケージがGoogleの認証をそのまま利用できる優れもので、アプリケーションを作るときには非常に便利なのですが、実際使われているケースは少ないのかもしれません…。(独自認証とか、OAuthを使うとか…)

FormからのPOSTは単純なPOSTの処理と、リクエストパラメータを表示させるだけのものですが、html/templateを使って、htmlを出力させています。
html/templateはAngularJSと相性があまり良くない気がする…。({{と}}を使うので。)
ちゃんとこの時に、<!DOCTYPE html>をつけてhtml5感を出しておきました。

データストアの使い方に関してはGetとPutだけで、Goのノウハウとは違ってくるのですが、話題としては色々話したと思います。

最後のdeployまでは私はしませんでしたが、+Hideki Matsudaさんは実行されたのかな?と思いますが、コマンドとしては、appcfg.pyのフロント側なので、特に難しい所は無いと思います。(appcfg.pyもまだ使えます)

また、GAE/Goを扱ったりしてみたいと思っているのである程度資料を読んだら
戻ってくることにしようと思います。

次回は、GoConの資料の後で公開されたものを読み進める予定です。


2014年6月15日日曜日

Golang Cafe #33を開催しました。

Golang Cafe #33を開催しました。

今回はGo Conference 2014のKeynoteの資料のGo: 90% Perfect, 100% of the time.を読み進めました。この資料はKeynoteなので、「Goは良いよ」という事が書かれていました。他の言語と比べてのGoの評価もあっておもしろいのですが、ちょっと評価が高過ぎるかな?と思えましたが、Keynoteなのでその辺は少し大きく表現しているのかもしれませんね。

Camlistoreという会社でのGoの導入までの話がつらつらと書かれていましたが、以前はPerlと、C、たまにJavaという感じでアプリケーションを開発していたけど、Perlとかだと、Callback地獄になってしまうのでちょっと面倒という話がありました。
Goだと、ある程度軽減できるようです。

気になるキーワードとしては、
Framework:gorilla、martini、Revelなど
net/httpの標準ライブラリ
nginxが不要な標準httpサーバ
libpng、imagemagickが不要(Goにはimage/pngパッケージがあるので)
pure goでpng、jpeg、gifのエンコード、デコードができる。
標準ライブラリでの暗号化
シェルスクリプトの置き換え

ということで、Go良いよ。という話題を見て終わりました。

次回は、htmldayの一環として、GAE/Goをやりま「した。」
またblogを書きます…。