Upload
yosuke-furukawa
View
9.840
Download
4
Embed Size (px)
Citation preview
How to create a Web application for Golang
@yosuke_furukawa
@yosuke_furukawaNode.jsユーザーグループ代表 / DeNA所属
Golang歴:2ヶ月くらい 作ったもの
!
- socket.io-go-emitter - golang + angularjs で chat sample
- qiita アドベントカレンダーをスクレイピングするツール
Webアプリの作り方
Webアプリの作り方
net/httpでのwebサーバの建て方
DBアクセス/ORM
Web Application Framework
golangの勉強会を有志の数名集めてDeNAでやってた
golangの勉強会をDeNAでやってた
今回の話はそのまとめ
Goでweb applicationを 作る時に知っておいた方が
良いこと
net/httpでWebサーバをたててみる
https://gist.github.com/yosuke-furukawa/8309096
net/httpでWebサーバをたてるpackage main!import ( "fmt" "net/http" "log")!func hello(w http.ResponseWriter, r *http.Request) { //fmt.Fprintfでwに入るものがクライアントに出力される fmt.Fprintf(w, "Hello World!")}!func main() { // アクセスのルーティングを設定する http.HandleFunc("/", hello) // portを指定して起動 err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }}
net/httpでWebサーバをたてるpackage main!import ( "fmt" "net/http" "log")!func hello(w http.ResponseWriter, r *http.Request) { //fmt.Fprintfでwに入るものがクライアントに出力される fmt.Fprintf(w, "Hello World!")}!func main() { // アクセスのルーティングを設定する http.HandleFunc("/", hello) // portを指定して起動 err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }}
HandleFuncでrouting
ListenAndServeでportを指定して起動
ListenAndServeの動きfunc (srv *Server) Serve(l net.Listener) error { defer l.Close() var tempDelay time.Duration // how long to sleep on accept failure for { rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 c, err := srv.newConn(rw) if err != nil { continue } go c.serve() }}
ListenAndServeの動きfunc (srv *Server) Serve(l net.Listener) error { defer l.Close() var tempDelay time.Duration // how long to sleep on accept failure for { rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 c, err := srv.newConn(rw) if err != nil { continue } go c.serve() }}
リスナーを通じてリクエストを受け取る
新しくコネクションのinstanceを作る
goroutineを起動し、リクエストを処理 ユーザーからのリクエストは全て新しいgoroutine で行われ、互いに影響しない作りになっている
GoでRESTFul APIを作る
RESTFul APIおさらい
各URIがリソースを表す
このリソースを操作するため4つのHTTPのメソッドを扱う
RESTFul APIおさらい
/users/:id
ユーザーを表現するためのリソース
GET, POST, PUT, DELETE
HTTP methodを使って操作する
RESTFul APIを作るpackage main!import ( "fmt" "github.com/drone/routes" "net/http")!// GET用のHandlerfunc getuser(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() id := params.Get(":id") fmt.Fprintf(w, "you are get user %s", id)}// PUT, DELETE, POST用の…!func main() { mux := routes.New() // GET, PUT, DELETE, POST 用のハンドラー関数をroutingする mux.Get("/users/:id", getuser) mux.Put("/users/:id", modifyuser) mux.Del("/users/:id", deleteuser) mux.Post("/users/", adduser) http.Handle("/", mux) http.ListenAndServe(":9090", nil)}
RESTFul APIを作るpackage main!import ( "fmt" "github.com/drone/routes" "net/http")!// GET用のHandlerfunc getuser(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() id := params.Get(":id") fmt.Fprintf(w, "you are get user %s", id)}// PUT, DELETE, POST用の…!func main() { mux := routes.New() // GET, PUT, DELETE, POST 用のハンドラー関数をroutingする mux.Get("/users/:id", getuser) mux.Put("/users/:id", modifyuser) mux.Del("/users/:id", deleteuser) mux.Post("/users/", adduser) http.Handle("/", mux) http.ListenAndServe(":9090", nil)}
drone/routesとかgowebとかの ルーティング用の 3rd modulesを使う
大したことはやってないので、 net/httpだけで頑張る事も可能だが、
:idから始まる文字のパースと methodによるrouting処理が面倒なら こういうモジュール使うのもあり。
これだけで簡単なREST APIは作れる
DBアクセス
https://gist.github.com/yosuke-furukawa/8552841
DBアクセスpackage main!import ( "database/sql" _ "github.com/go-sql-driver/mysql" "log")!func main() { // sql.Open 第一引数にdriverの種類、第二引数に接続アドレスを指定、ここではhelloに接続することを宣言。 db, err := sql.Open("mysql", "root@tcp(127.0.0.1:3306)/hello") // errがあればそれを表示 if err != nil { log.Fatal(err) } // deferで関数の終わりにそれをclose defer db.Close()}
DBアクセスpackage main!import ( "database/sql" _ "github.com/go-sql-driver/mysql" "log")!func main() { // sql.Open 第一引数にdriverの種類、第二引数に接続アドレスを指定、ここではhelloに接続することを宣言。 db, err := sql.Open("mysql", "root@tcp(127.0.0.1:3306)/hello") // errがあればそれを表示 if err != nil { log.Fatal(err) } // deferで関数の終わりにそれをclose defer db.Close()}
sql.OpenでDBアクセス開始
SELECT操作 var ( id int name string ) // db.PrepareでPreparedStatement実行 stmt, err := db.Prepare("select id, name from users where id = ?") if err != nil { log.Fatal(err) } // statementのClose忘れずに。 defer stmt.Close() // stmt.QueryでQueryを実行 rows, err := stmt.Query(1); for rows.Next() { err := rows.Scan(&id, &name) if err != nil { log.Fatal(err) } log.Println(id, name) } defer rows.Close();
SELECT操作 var ( id int name string ) // db.PrepareでPreparedStatement実行 stmt, err := db.Prepare("select id, name from users where id = ?") if err != nil { log.Fatal(err) } // statementのClose忘れずに。 defer stmt.Close() // stmt.QueryでQueryを実行 rows, err := stmt.Query(1); for rows.Next() { err := rows.Scan(&id, &name) if err != nil { log.Fatal(err) } log.Println(id, name) } defer rows.Close();
db.PrepareでSQL実行する
stmt.QueryでQueryを実行 ここでは、id=1を実行
INSERTとかUPDATEとかDELETEとか! stmt, err := db.Prepare("INSERT INTO users(id, name) VALUES(?, ?)") if err != nil { log.Fatal(err) } res, err := stmt.Exec(2, "haruyama") if err != nil { log.Fatal(err) }
INSERTとかUPDATEとかDELETEとか! stmt, err := db.Prepare("INSERT INTO users(id, name) VALUES(?, ?)") if err != nil { log.Fatal(err) } res, err := stmt.Exec(2, "gopher") if err != nil { log.Fatal(err) }
stmt.Execで更新系のQueryを発行する
ここまでで簡単なDBアクセスと永続化はできる
ORM
こんだけある…
http://present.go-steel-programmers.org/talk-review-orms/gsp-go-orms.slide#1
あんまり試せてない…シンプルでquery builderっぽいgorpか
gormが良さそうだけど、API的には どれも有意差はない
Web Application Frameworks
https://gist.github.com/yosuke-furukawa/8816249
TL;DR
net/http
martini, beego
revel, falcore
goweb, drone/routes
gorilla/negroni
need simple
REST Framework
toolkit/compatible with net/http
Sinatra/Express like
Fullstack RoR like
martini!
• フルスタックフレームワーク • martiniがsinatraならこっちはRoR, Play
Framework、ちなみに初期ページすごくPlayっぽい
• 中にmail senderやwebsocketも組み込まれてる • ミドルウェアもフィルターという概念を使うことで使える
• コード変更を検知して自動リロード/テストフレームワークが中にある、など開発にも便利
Falcore
• 我らがngmoco製、最近はメンテをfitstarさんに譲っている様子
• フルスタックフレームワーク • HUPを送ったら自動でprocessをforkして子プロセスがreadyになったら自分を殺す動きをする
• これにより、hot restartが可能になっている
beego• revelほど高機能ではないが、一応フルスタックフレームワーク • ドキュメントが超親切 • beego/ORMをサポートし、MVCに特化している
• 中国では採用実績あり?
!!!!!
martini
!
!
• 人気がある(github/star的な意味で)
• すごくSinatra/Expressライク • 基本的な機能しかコアには存在せず、ミドルウェアで機能拡張する
• 豊富なミドルウェアが存在している。 • ただし、作者はあんまり推してない。
martini!!
• Martiniの作者がHTTP serverをより簡易的に、イディオム的に扱えるようにするために開発したライブラリ
• 正確にはフレームワークではなく、ライブラリ群 • 今後Martiniの代わりにスタンダードになる可能性があるかも。
!!
martini!!
• こちらも正確にはツールキット • router, secure session, websocket等、まぁWebアプリ作るには使うよねっていうのが揃ってる。
• isucon っていうweb アプリ高速化チューニングコンテストで使われてる。
• 他にも実はrevelの中で使われてたりと汎用性高い。
!
WAFまとめ
net/http
martini, beego
revel, falcore
goweb, drone/routes
gorilla/negroni
need simple
REST Framework
toolkit/compatible with net/http
Sinatra/Express like
Fullstack RoR like
まとめ• Webサーバの建て方とnet/httpの基礎的な話
• DBアクセスの方法
• Web Application Frameworkまとめ
今後やりたい事
今後やりたい事
Go + socket.io で死ぬ予定