2014年8月2日土曜日

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

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

今回は、Gorilla Toolkitの残りの4つ(reverse、rpc、schema、securecookie)を使う回でしたが、schemaとsecurecookieはドキュメントを読むだけで終わっています。

reverseですが、元になるURLの文字列をコンパイルしておいて、部分文字列を指定するとURLの文字列が生成されるというパッケージです。用途として、URLの一部の文字列を渡して復元してリクエストを送信するというケースで使えそう(クライアント側の処理を作る時など)ですが、いまいちそこまでの需要があるのかどうかは疑問が残る結果となりました。

package main

import (
 "log"
 "net/url"

 "github.com/gorilla/reverse"
)

func main() {
 var err error

 regexp, err := reverse.CompileRegexp(`/foo/1(\d+)3`)
 if err != nil {
  log.Fatalln(err)
 }

 // url.Valuesの値からURLを生成。
 url, err := regexp.Revert(url.Values{"":{"45678"}})
 if err != nil {
  log.Fatalln(err)
 }

 log.Println(url)
}

reverse.CompileRegexp()で正規表現を含むURL文字列のひな形を作成して、
reverse.Revert()で復元した文字列を返しています。最初は他のパッケージで使っているものなのかと思いましたが、どうやら使われている様子はなさそうです。

次に、rpcパッケージですが、これは、rpcサーバを作る時に利用します。
サーバ側の実装はそこまで難しくなく、Codecの登録と、RPCのリクエストに対して呼び出されるServiceのTypeのインスタンスを登録しておくだけです。

package main

import (
 "net/http"

 "github.com/gorilla/rpc"
 "github.com/gorilla/rpc/json"
)

func main() {
 s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterService(new(HelloService), "")
    http.Handle("/rpc", s)

 http.ListenAndServe(":8080", nil)
}

type HelloArgs struct {
    Who string
}

type HelloReply struct {
    Message string
}

type HelloService struct {}

func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
    reply.Message = "Hello, " + args.Who + "!"
    return nil
}

サーバ側はそんなに難しくないのですが、動作を確認するためにクライアントを作成して通信させる所でハマりました。

Goにも標準のnet/rpcパッケージが存在するのでそれを利用するのが便利だと思うのですが、標準のパッケージだとgorilla/rpcと通信することはできません。
理由は、gorilla/rpcでの待受はPOSTのリクエストを待っているようなのですが、
標準のnet/rpcパッケージはどうやらGETのリクエストを送っているらしく、レスポンスが400とか、415が戻されてしまいます。したがって、自分でリクエストの処理を実装してやらないといけません。

package main

import (
 "io"
 "log"
 "net/http"
 "os"
 // "net/rpc"
 "strings"
)

type HelloArgs struct {
 Who string
}
func main() {
 // HTTP ClientによるRPC呼び出し。
 // jsonのパラメータを自分で作って上げる必要がある。
 client := http.Client{}
 res, err := client.Post("http://localhost:8080/rpc", "application/json", strings.NewReader(`{"method":"HelloService.Say","params":[{"Who":"Test"}], "id":"1"}`))
 if err != nil {
  log.Fatalln(err)
 }

 // 標準のDialHTTPPathはGETのリクエストが投げられるらしい。
 // したがって、標準のrpcパッケージは使えない(?)
 // client, err := rpc.DialHTTPPath("tcp", "localhost:8080", "/rpc")
 // if err != nil {
 //  log.Println("DialHTTP")
 //  log.Fatalln(err)
 // }

 // defer client.Close()

 // var reply int

 // args := HelloArgs{Who: "hoge"}
 // err = client.Call("HelloService.Say", args, &reply)
 // if err != nil {
 //  log.Println("Call")
 //  log.Fatalln(err)
 // }

 // log.Println(reply)
 io.Copy(os.Stdout, res.Body)
}

RPCのリクエストを送ったことがなかったので、どうすればいいのかわかりませんでしたが、curlの例が検索にヒットしたのでパラメータを設定して送り、無事レスポンスが得られました。でも、これはめんどくさい!
設定があるのか、コードが悪いのかは調べきる事はできなかったので、また今後調べていこうと思います。ただ、RPCサーバを実装する事があるかどうかはわかりません。

次回は、GoのO/Rマッパーであるgorpを使ってみる会になります。