LoginSignup
11
8

More than 5 years have passed since last update.

fasthttp で作る cowserver

Posted at

クリスマス?

                                        _____________________
                                       < そんなもの、うちにはないよ >
                                        ---------------------
                                              \
                                               \
                         \__\_ :. ___/          \
                        ..\  /--
 :.______ :  .:*  :  . _ .:  :..  .  :   . .  :    ()_ .:
  ((     \. :./(__ :._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 を利用しています.

net-http.go
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 つ取るハンドラ(ここでは cowHellocowSay の事です)を作成する必要があります. /cow/:say のように URL からパラメータを取得する場合は ps.ByName("say") という感じで取得することができます.

次に fasthttp でサッと作る

ところで fasthttp とはなんぞ?となってる方もいると思います. net/httpより10倍速いvalyala/fasthttpが面白そうなので調査してみた件 の記事を読むとわかりやすいのではないかと思います. まとめると net/http より高速なんだぜ!! と謳っているパッケージです.
高速なら使ってみたい!ということで, fasthttprouter と組み合わせて作っていきます.

fasthttp.go
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/cowcurl 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 の方が早い
11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8