クリスマス?
_____________________
< そんなもの、うちにはないよ >
---------------------
\
\
\__\_ :. ___/ \
..\ /--
:.______ : .:* : . _ .: :.. . : . . : ()_ .:
(( \. :./(__ :._O_)________:______,____:____/ *\_o
====(( \: (****) (***) :. ...: .. . ()_______/\\ __-'
\____(( \ ()oo()_/ /.: : ..________/_____ll -/.: ..
( (( \(())))__/ . .. \\.: ..( ) ll ( l_.:
( / (( \__*__)___:___ : : )) .) /--------\ \ \
( / ((_____________) .. // . / / /..:: . )_)_\
(____/_____________________\__// : /_/_/ :.. :/_/ \_\
/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ /_/_/
というわけで明日はクリスマスイブですね!!
この記事はGo (その2) Advent Calendar 2016 23日目の記事です!
今回作るもの
今回はリクエストが来たら cowsay を行うサーバーを Go で書いてみたいと思います.
とても簡単に作成ができるので、まだサーバー作成したことのない皆さんも挑戦してみてください!!
- GET
/cow
(Hello!! な cowsay を返す) - GET
/cow/:say
(:say の部分に入力された文字列な cowsay を返す)
その前に...
cowsay ってなんですか!?
http://qiita.com/Kei-Kamikawa/items/a5245720524aecfab666 を読むとあーこんなものかーと分かるはずです.
今回簡単に cowsay を実現するために Neo-cowsay というパッケージを使っていきます.
まずは net/http でシュッと作ってみる
net/http でと言ってますが、routing は httprouter を利用しています.
package main
import (
"fmt"
"log"
"net/http"
cowsay "github.com/Code-Hex/Neo-cowsay"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
router.GET("/cow", cowHello)
router.GET("/cow/:say", cowSay)
if err := http.ListenAndServe(":3000", router); err != nil {
log.Fatalf("Error in ListenAndServe: %s", err.Error())
}
}
func cowHello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
say, err := cowsay.Say(&cowsay.Cow{
Phrase: "Hello!!",
Type: "default",
BallonWidth: 40,
})
if err != nil {
fmt.Fprintf(w, "Error in cowHello: %s", err.Error())
return
}
fmt.Fprintf(w, say)
}
func cowSay(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
say, err := cowsay.Say(&cowsay.Cow{
Phrase: ps.ByName("say"),
Type: "default",
BallonWidth: 40,
})
if err != nil {
fmt.Fprintf(w, "Error in cowSay: %s", err.Error())
return
}
fmt.Fprintf(w, say)
}
httprouter で作成する場合 w http.ResponseWriter, r *http.Request, _ httprouter.Params
を 3 つ取るハンドラ(ここでは cowHello
や cowSay
の事です)を作成する必要があります. /cow/:say
のように URL からパラメータを取得する場合は ps.ByName("say")
という感じで取得することができます.
次に fasthttp でサッと作る
ところで fasthttp とはなんぞ?となってる方もいると思います. net/httpより10倍速いvalyala/fasthttpが面白そうなので調査してみた件 の記事を読むとわかりやすいのではないかと思います. まとめると net/http より高速なんだぜ!! と謳っているパッケージです.
高速なら使ってみたい!ということで, fasthttprouter と組み合わせて作っていきます.
package main
import (
"fmt"
"log"
cowsay "github.com/Code-Hex/Neo-cowsay"
"github.com/buaazp/fasthttprouter"
"github.com/valyala/fasthttp"
)
func main() {
router := fasthttprouter.New()
router.GET("/cow", cowHello)
router.GET("/cow/:say", cowSay)
if err := fasthttp.ListenAndServe(":3000", router.Handler); err != nil {
log.Fatalf("Error in ListenAndServe: %s", err.Error())
}
}
func cowHello(ctx *fasthttp.RequestCtx) {
say, err := cowsay.Say(&cowsay.Cow{
Phrase: "Hello!!",
Type: "default",
BallonWidth: 40,
})
if err != nil {
fmt.Fprintf(ctx, "Error in cowHello: %s", err.Error())
return
}
fmt.Fprintf(ctx, say)
}
func cowSay(ctx *fasthttp.RequestCtx) {
say, err := cowsay.Say(&cowsay.Cow{
Phrase: ctx.UserValue("say").(string),
Type: "default",
BallonWidth: 40,
})
if err != nil {
fmt.Fprintf(ctx, "Error in cowSay: %s", err.Error())
return
}
fmt.Fprintf(ctx, say)
}
先ほどの net/http と httprouter の組み合わせで開発した cowserver よりシンプルに記述することができているのではないかと思います. fasthttprouter を使用するときは ctx *fasthttp.RequestCtx
の引数を 1 つ取るハンドラを作成すれば完了です.
ただし, /cow/:say
のような URL パラメーターを受け取るとき, ctx.UserValue("say")
のようにすると受け取ることができるのですが, 返り値の方が interface{}
ですので型アサーションを行う必要があります. そういうことからここでは ctx.UserValue("say").(string)
というように string
でアサーションを行っています.
動かしてみる
早速動かしてみましょう. そして curl http://localhost:3000/cow
や curl http://127.0.0.1:3000/cow/サンタさん彼女ください
などを打ち込んで試してみてください!!
__________________
< サンタさん彼女ください >
------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
おおーー!!クリスマス最高だゼェ!!
ベンチマーク
ところで fasthttp って本当に早いの?と思って, apache bench を用いて秒間どれくらいのリクエストをさばけるのか見ていきます. 使用したコマンドは ab -n 10000 -c 1
です.
net/http | fasthttp | |
---|---|---|
/cow | 2808.56 [#/sec] | 2011.16 [#/sec] |
/cow/:say | 2591.93 [#/sec] | 2175.72 [#/sec] |
この結果だと net/http の方が早いですね. タイトル的に fasthttp の方が早かったと記述したかったのですが, 結果が結果なので仕方ないですね!
まとめ
- net/http, fasthttp を使って cowserver を作った
- fasthttprouter を使うとまあまあシンプルに記述できた
- 結果的に net/http の方が早い