GoCon2016 spring 自作Webフレームワーク uconを作った話

199 views
0 views

Published on

GoCon2016 spring 自作Webフレームワーク uconを作った話
http://gocon.connpass.com/event/27521/

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
199
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

GoCon2016 spring 自作Webフレームワーク uconを作った話

  1. 1. 自作Webフレームワーク uconを作った話 わかめ まさひろ
  2. 2. わかめ まさひろ @vvakame TypeScript Masahiro Wakame DefinitelyTyped appengine/go photo from golang.org/doc/gopher/
  3. 3. GoogleAppEngine/Go 神 いわゆる GOD
  4. 4. 諸君、私はappengine/goが好きだ • 2008年4月 始まる • サーバレスアーキテクチャ • 2011年5月 Go対応始まる • 2015年7月 Go, GAになる • 2016年4月 Go 1.6対応 1.9.35→❌ 1.9.36を使おう!
  5. 5. Eric Schmidt said. https://www.youtube.com/watch?v=HgWHeT_OwHc&t=1461 GCP Next 2016 Day 1 Keynote
  6. 6. GAE用ライブラリ作ってます • testerator github.com/favclip/testrator • UnitTest高速化 • qbg github.com/favclip/qbg • Datastore用TypeSafeクエリビルダ • smg github.com/favclip/smg • Search API用TypeSafeラッパ productionで利用中!
  7. 7. GoCon 2015 Summer • appengine専用じゃないけど • jwg github.com/favclip/jwg • genbase github.com/favclip/genbase • GoCon 2015で話をしました! • SlideShare goo.gl/45lZDK
  8. 8. gb • gb • getgb.io/ • gb keeps the peace of our project🌹 • gb gae • github.com/PalmStoneGames/gb-gae
  9. 9. Google API Discovery Service 誰か知ってる?
  10. 10. APIs Explorer is 神 https://developers.google.com/apis-explorer/
  11. 11. APIs Explorer • 誰でも簡単に使える • 実際のAPIが叩かれる • 結果を共有しやすい • コードからUIが生成されている
  12. 12. Cloud Endpoints • appengine専用の仕組み • cloud.google.com/endpoints/ • 自前APIでAPIs Explorer使える • APIの構造がわかる! • 実例がわかる!(DevTool) • UIを省く極道管理画面も!
  13. 13. 周辺ツールも充実 • クライアントライブラリの自動生成 • golangだとこの辺全部そう • github.com/google/google-api-go- client • TypeScript用型定義の生成 • www.npmjs.com/package/gapidts
  14. 14. but…
  15. 15. Googleの闇の領域 ユーザ GAE闇 path mapping request format Version切替後reqをなかった事に custom domain不可
  16. 16. go-endpoints • CloudEndpoints用framework • github.com/GoogleCloudPlatform/ go-endpoints • Service & Method の組み合わせで定義 • w http.ResponseWriterが取れない • CloudEndpoints的には不要なので…
  17. 17. 評価 • 細かい事を気にしなければかなり良い • カスタムドメイン不可がやはり辛い • デバッグ不可能な闇の領域が辛い • ちょいちょいそこが不安定な気が… • 拡張性が低い • 横断的な処理を入れにくい…
  18. 18. 僕達が必要なもの
  19. 19. Alt Cloud Endpoints • APIs Explorer的なものが欲しい! • サバクラの意思疎通が楽 • デバッグが楽 • コード→仕様が良い • 仕様→コード は努力が必要(努力やだ • クライアントコードの生成 • 変わったら壊れてほしい
  20. 20. 代替ツールの検討
  21. 21. 各ツールの評価 • RAML • API Blueprint (apiary.io • JSON Schema v4 • Swagger • gRPC
  22. 22. ✨swagger✨ • Swaggerが一番良さそう! • 個人の見解です • Open API Initiative発足 • Swagger仕様をbaseに • 長いものには巻かれたい • Qiitaに少し書きました goo.gl/BLS3uH
  23. 23. 既存GoなSwagger実装の話 最初からswagger対応 の物を選ぶと楽そう
  24. 24. 既存Go実装の比較 • go-swagger • goswagger.io/ • type safeじゃない • yvasiyarov/swagger • github.com/yvasiyarov/swagger • comment baseでつらい • type safeじゃない
  25. 25. 既存Go実装の比較 • go-restful • github.com/emicklei/go-restful/ • type safeじゃない • goa • goa.design/ • type safeじゃない • DSLがヤバイ
  26. 26. 既存Go実装の比較 • grpc-gateway • github.com/gengo/grpc-gateway • gRPCのJSONなreverse proxy • いつのまにかswagger対応してた • appengineだと❌
  27. 27. 既存Webフレームワーク
  28. 28. 重要なポイント • net/httpに近いほうがわかりやすい • あまりに独自っぽいのはちょっと… • go-endpointsからの移行 • しばらくCloudEndpointsと両立したい • コード上の互換性があると嬉しい… • swagger-uiが使える
  29. 29. 既存フレームワーク調べた • なるべくnet/httpに近い • revel→❌ • なるべくCloudEndpointsのまま • net/http→❌ • goji→❌ • Swagger対応! • martini→❌
  30. 30. 結論 •自分でつくろう 趣味に走ったわけではないです
  31. 31. 自分で作る話
  32. 32. 前提 • appengine縛りにはしない • とはいえappengineで使えないと困る • net/httpに近いAPI • 柔軟性 • go-endpointsとの互換性 • swaggerはopt-in やっていく
  33. 33. ucon https://github.com/favclip/ucon
  34. 34. 名付け親 某a2cさん martiniとか ginとかに 対抗して
  35. 35. 名付け親 某a2cさん uconと 名付けよう! In japan, ucon (= turmeric) is to be effective in hangover.
  36. 36. 決めた後 某a2cさん 💩 ←ゆるさない!!
  37. 37. 仕様紹介
  38. 38. まずgo-endpoints s := &fooService{}
 
 api, err := endpoints.RegisterService(s, "foo", "v1", "Foo API", true)
 if err != nil {
 panic(err.Error())
 }
 
 info := api.MethodByName("Get").Info()
 info.HTTPMethod, info.Path, info.Desc = “GET", "/foo/{id}", “Fooを1件取得する" … type IntIDRequest struct {
 ID int64 `json:"id,string"`
 }
 func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) { … }
  39. 39. まずgo-endpoints s := &fooService{}
 
 api, err := endpoints.RegisterService(s, "foo", "v1", "Foo API", true)
 if err != nil {
 panic(err.Error())
 }
 
 info := api.MethodByName("Get").Info()
 info.HTTPMethod, info.Path, info.Desc = “GET", "/foo/{id}", “Fooを1件取得する" … type IntIDRequest struct {
 ID int64 `json:"id,string"`
 }
 func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) { … } Handler Response Setup
  40. 40. まずgo-endpoints s := &fooService{}
 
 api, err := endpoints.RegisterService(s, "foo", "v1", "Foo API", true)
 if err != nil {
 panic(err.Error())
 }
 
 info := api.MethodByName("Get").Info()
 info.HTTPMethod, info.Path, info.Desc = “GET", "/foo/{id}", “Fooを1件取得する" … type IntIDRequest struct {
 ID int64 `json:"id,string"`
 }
 func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) { … } Request Response 闇の領域でもろもろ変換されてる
  41. 41. ucon Features • net/http との類似性 • Routing • Method, Path Matching • Middleware • Bubble • Dependency Injection • Plugin
  42. 42. API likes net/http ucon.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {})
  43. 43. API likes net/http ucon.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {})
  44. 44. Routing ucon.HandleFunc(“*", “/“, … ucon.HandleFunc(“OPTIONS", “/“, … ucon.HandleFunc(“GET", “/“, … ucon.HandleFunc(“POST", “/“, … ucon.HandleFunc(“GET", “/api/user“, … ucon.HandleFunc(“GET", “/api/user/me“, … ucon.HandleFunc(“GET", “/api/user/{id}“, …
  45. 45. Routing rule • METHODが一致する • * 指定も可 厳密一致優先 • Request Pathが一致する • 複数候補ある場合より長い節一致 • Request GET /api/user/123 • 🌟 GET /api/user/{id} • ❌ GET /api/user • 先登録優先
  46. 46. Middleware • 1 request毎の処理に介入 • JavaでいうServletFilter • ASP.NET MVCでいうFilter • Logging, DI, CORS用Header, error→JSON変換 etc, etc…
  47. 47. Middleware Middleware Middleware Middleware Handler ServeHTTP DI Cache-Control Cookie appengine.Context etc, etc… CORS Header Path, Query, Body → JSON *http.Request http.ResponseWriter
  48. 48. Middleware type MiddlewareFunc func(b *Bubble) error type Bubble struct {
 R *http.Request
 W http.ResponseWriter
 Context context.Context
 RequestHandler interface{}
 
 ArgumentTypes []reflect.Type
 Arguments []reflect.Value
 Returns []reflect.Value
 }
 func (b *Bubble) Next() error { …
 } func (b *Bubble) do() error {
 hv := reflect.ValueOf(b.handler())
 …
 
 b.Returns = hv.Call(b.Arguments)
 
 return nil
 } var httpReqType = reflect.TypeOf((*http.Request)( var httpRespType = reflect.TypeOf((*http.Response func HTTPRWDI() MiddlewareFunc {
 return func(b *Bubble) error {
 for idx, argT := range b.ArgumentTypes {
 if argT == httpReqType {
 b.Arguments[idx] = reflect.ValueOf(b.R)
 continue
 }
 if argT == httpRespType {
 b.Arguments[idx] = reflect.ValueOf(b.W)
 continue
 }
 }
 
 return b.Next()
 }
 }
  49. 49. built-in middleware • HTTPRWDI • *http.Request, http.ResponseWriterのDI • NetContextDI • net/contextのContextをDI
  50. 50. built-in middleware • RequestObjectMapper • path parameter, query paramter, post bodyをObjectに変換しDI • ResponseMapper • HandlerがreturnしたObjectやerrorを JSONに変換
  51. 51. Plugin • プロセス起動時1回だけ動作 • 全Handlerの走査 • Handler→Plugin間の値の伝達機構 • swaggerはplugin • 全Handlerの情報から処理 • swagger.json出力用Handlerの追加
  52. 52. Plugin type pluginContainer struct {
 base interface{}
 }
 
 type HandlersScannerPlugin interface {
 HandlersScannerProcess(m *ServeMux, rds []*RouteDefinition) error
 }
 type RouteDefinition struct {
 Method string
 PathTemplate *PathTemplate
 HandlerContainer HandlerContainer
 } func (m *ServeMux) Prepare() {
 for _, plugin := range m.plugins {
 used := false
 if sc := plugin.HandlersScanner(); sc != nil {
 err := sc.HandlersScannerProcess(m, m.router.handlers)
 if err != nil {
 panic(err)
 }
 used = true
 }
 if !used {
 panic(fmt.Sprintf("unused plugin: %#v", plugin))
 }
 }
 }
  53. 53. Plugin type pluginContainer struct {
 base interface{}
 }
 
 type HandlersScannerPlugin interface {
 HandlersScannerProcess(m *ServeMux, rds []*RouteDefinition) error
 }
 type RouteDefinition struct {
 Method string
 PathTemplate *PathTemplate
 HandlerContainer HandlerContainer
 } func (m *ServeMux) Prepare() {
 for _, plugin := range m.plugins {
 used := false
 if sc := plugin.HandlersScanner(); sc != nil {
 err := sc.HandlersScannerProcess(m, m.router.handlers)
 if err != nil {
 panic(err)
 }
 used = true
 }
 if !used {
 panic(fmt.Sprintf("unused plugin: %#v", plugin))
 }
 }
 }
  54. 54. swagger plugin usage swPlugin := swagger.NewPlugin(…)
 ucon.Plugin(swPlugin) s := &fooService{}
 
 tag := swPlugin.AddTag(&swagger.Tag{Name: "Foo", Description: ""})
 var info *swagger.HandlerInfo
 
 info = swagger.NewHandlerInfo(s.List)
 ucon.Handle("GET", "/api/foo/{id}", info)
 info.Description, info.Tags = "Fooを1件取得する", []string{tag.Name} … type IntIDRequest struct {
 ID int64 `json:"id,string"`
 }
 func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) { … }
  55. 55. go-endpoints(再掲 s := &fooService{}
 
 api, err := endpoints.RegisterService(s, "foo", "v1", "Foo API", true)
 if err != nil {
 panic(err.Error())
 }
 
 info := api.MethodByName("Get").Info()
 info.HTTPMethod, info.Path, info.Desc = “GET", "/foo/{id}", “Fooを1件取得する" … type IntIDRequest struct {
 ID int64 `json:"id,string"`
 }
 func (s *fooService) Get(r *http.Request, req *IntIDRequest) (*FooJSON, error) { … }
  56. 56. コード規模の話 • 本体 1329行 • ls | grep .go | grep -v _test.go | xargs wc -l • swaggerプラグイン 1138行 • find ./swagger -type f | grep .go | grep -v sample | grep -v _test.go | xargs wc -l
  57. 57. 利用事例
  58. 58. 利用サイト • favclip • 技術書典 応募サイト • 怖くてOSSにでけんかったすまんな… • Topgate社内では今後使っていくはず…
  59. 59. swagger関連ツール • swagger-uiの話 • Go用クライアントライブラリの話 • TypeScript用型定義ファイル生成の話
  60. 60. 求む!
  61. 61. 求む! • 利用してみてブログ書く • 利用してみて質問する • 利用してみて… 自分が使えるようになると 満足するタイプ
  62. 62. We are hiring
  63. 63. We are hiring 1 • 開発:テレビ朝日 • jwg, genbase 他 必要に応じて製造 • http://www.favclip.com/ • appengine/go開発者絶賛募集中!
  64. 64. We are hiring 2 • Topgate社も絶賛募集中です • appengineできる人 • HTML, CSS, JS得意な人 • その他
  65. 65. 雑談
  66. 66. https://github.com/golang/proposal/blob/master/ design/15292-generics.md
  67. 67. 👍for Web app

×