Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go + grpc-gateway でつくる JSON API 速習会 @ Wantedly / Create JSON API with Go + grpc-gateway

Go + grpc-gateway でつくる JSON API 速習会 @ Wantedly / Create JSON API with Go + grpc-gateway

Masayuki Izumi

March 27, 2018
Tweet

More Decks by Masayuki Izumi

Other Decks in Programming

Transcript

  1. ©2018 Wantedly, Inc. Go + grpc-gateway Ͱͭ͘Δ JSON API αʔό଎शձ

    How to create microservice with Go in Wantedly 27.Mar.2018 - @izumin5210
  2. ©2018 Wantedly, Inc. 1. `git clone https://github.com/wantedly/grpc-gateway-study` 2. `cd grpc-gateway-study`

    ※ $GOPATH/src/github.com/wantedly/grpc-gateway-study ʹ഑ஔ͢Δ • Go + protobuf ؀ڥ͕͋Δਓ 1. Install izumin5210/grapi: `brew install izumin5210/tools/grapi` 2. `grapi init .` • Docker ؀ڥ͕͋Δͻͱ 3. Install creasty/rid: `brew install creasty/tools/rid` 4. `rid grapi init .` Setup Are you ready?
  3. ©2018 Wantedly, Inc. $ whoami a.k.a. usamimi engineer @izumin5210 Engineer

    at Wantedly, Inc. Wantedly People Tribe: - Web Application Engineer - Profile Data Strategy Group - Developer Productivity Gopher, Rubyist, JavaScripter (and Androider)
  4. ©2018 Wantedly, Inc. ͋ΘͤͯΑΈ͍ͨ  ͳͥϚΠΫϩαʔϏεʁ  ͳͥ3BJMT ͳͥ(Pʁ ‣

    ͦ΋ͦ΋%PDLFS؀ڥ͕ૣ͔͘Β͋ͬͨ  .JDSPTFSWJDFT LTͷಋೖোน͕গͳ͍ ‣ ཉ͍͠΋ͷׂ͕Γͱ໌֬ αʔϏεؒ࿈ܞ  (Pͱͷ૬ੑ˕㷉◔ϖ◔㷊 ˞͔ͳΓ୺ં͍ͬͯΔͷͰɼৄ͘͠͸3BJMT%.ͷߨԋΛ લఏ https://speakerdeck.com/altech/microservices-on-rails-wantedly-falsemaikurosabisushi-li
  5. ©2018 Wantedly, Inc. ࢓༷ɺ͓΅͑ͯ·͔͢ʁ  Ϣʔβʹૣ͘Ձ஋Λಧ͚͍ͨʂ  ͦͯ٘͠ਜ਼ʹͳΔυΩϡϝϯτ  ༨ܭͳίϛϡχέʔγϣϯίετ

     ʮ͜ͷ"1*ͬͯԿ͕ฦͬͯ͘ΔΜ͚ͩͬʯ  +40/4DIFNB΍4XBHHFS΋͍͍͚Ͳʜ  ʮڧ੍ྗʯ͕ͳ͍ͷͰ༉அ͢Δͱ෗Δ  Ϩίʔυ͚ͭͩ࡞Δ"1*ͷςετॻ͖·͢ʁ 8):H31$ ʮΩϛ͸ͳʹΛ͢Δαʔόͳͷʁʯ Wantedly People Web൛͸AndroidΞϓϦͷίʔυΛࢀߟʹ࣮૷͞Ε͍ͯ·͢ ͳͥͳΒͦ͜ʹ͸ܕ͕͔͋ͬͨΒ
  6. ©2018 Wantedly, Inc. Ϣʔβ͕ͨ͘͞Μ͍Δ  ϞόΠϧΞϓϦ͚ͩͰͳ͘ɼ
 ΄͔ͷϚΠΫϩαʔϏε΋Ϣʔβʢ"1*ར༻ऀʣ  ͋ΔαʔϏεͷ࣮૷ͷͨΊʹɼ
 ผͷαʔϏεʹ΋खΛೖΕͳ͍͜ͱ͸ଟʑ͋Δ

     ࣗ෼͕ॻ͍ͨ΍ͭͱ͸ݶΒͳ͍  ʮ͜ͷࢠͲΜͳ"1*͚࣋ͬͯͨͬʯ 8):H31$ ʮΩϛ͸ͳʹΛ͢Δαʔόͳͷʁʯ ໊ࢗࡱӨͷϑϩʔΛ೺Ѳ͢ΔͨΊͷγʔέϯεਤɼޕલத·Δ·Δ௵ͨ͠ ʢ࣮ࡍͷਤ͸΋͏ͪΐͬͱෳࡶʣ
  7. ©2018 Wantedly, Inc. 8):H31$ ʮͨͩͨͩ࡞Δͷ͕໘౗ʯ data, err := ioutil.ReadAll(req.Body) if

    err != nil { http.Error(w, err.Error(), http.InternalStatusInternalServerError) return } book := &Book{} err = json.Unmarshal(data, book) if err != nil { http.Error(w, err.Error(), http.InternalStatusInternalServerError) return } // ここから本当にやりたい処理 net/http Λͦͷ··࢖ͬͨ৔߹
  8. ©2018 Wantedly, Inc. 8):H31$ ʮͨͩͨͩ࡞Δͷ͕໘౗ʯ book := &Book{} err =

    c.BindJSON(book) if err != nil { c.JSOAbortWithErrorN(http.InternalStatusInternalServerError, err) return } // ここから本当にやりたい処理 github.com/gin-gonic/gin Λ࢖ͬͨ৔߹ ͪΐͬͱϚγ
  9. ©2018 Wantedly, Inc. *%- *OUFSGBDF%FGJOJUJPO-BOHVBHF ͕௒༏ल BQJQSPUPTCPPLQSPUPΛ։͍ͯΈΑ͏  QSPUPCVGͰ"1*ఆٛΛॻ͍ͯɼ 

    ͔͜͜Β(PͷJOUFSGBDFͱ࣮૷Λੜ੒ͯ͠ɼ w ϧʔςΟϯάͯ͠ʜVONBSTIBMͯ͠ʜ w Λɼউखʹॻ͍ͯ͘ΕΔ  ࣮૷ʢ΄Μͱʹ΍Γ͍ͨ͜ͱʣ͚ͩ΍Ε͹0, 8):H31$ H31$ͩͱͳʹ͕͏Ε͍͠ʁ api/protosԿ͍ͬͯΔ͔͍͍ͩͨΘ͔ΔͰ͠ΐʁ service BookService { rpc CreateBook (CreateBookRequest) returns (Book) { } } message Book { string book_id = 1; } message CreateBookRequest { Book book = 1; }
  10. ©2018 Wantedly, Inc. $PEFHFOFSBUPS΋௒༏ल BQJCPPLQCHPΛ։͍ͯΈΑ͏ 8):H31$ H31$ͩͱͳʹ͕͏Ε͍͠ʁ type BookServiceServer interface

    { CreateBook(context.Context, *CreateBookRequest) (*Book, error) } type Book struct { BookId string `protobuf:"..." json:"book_id,omitempty"` } type CreateBookRequest struct { Book *Book `protobuf:"..." json:"book,omitempty"` } ։ൃऀ͸͜ͷJOUFSGBDFΛຬͨ͢Α͏ʹ࣮૷͢Δ͚ͩ
  11. ©2018 Wantedly, Inc.  ίϛϡχέʔγϣϯίετ w QSPUPCVGόΠφϦ w ϞόΠϧΞϓϦ΍3BJMTαʔόʹڧཁ͢Δͷʁ w

    ૉͷ··ͩͱDVSM΋Ͱ͖ͳ͍ͥ  Πϯϑϥίετ w H31$͸)551ͷίωΫγϣϯΛӬଓԽ͢Δ w NPOJUPSJOH͸ʁɼMPBECBMBODJOH͸ʁʜߟ͑Δ͜ͱ͸ଟ͍ ϝϦοτ͕ଟ͍൓໘ɼٻΊΒΕΔ஌ࣝͱલఏ΋ଟ͍ WHY not gRPC gRPC ͸ϜζΧγΠ
  12. ©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͳͥHSQDHBUFXBZΛڬΉʁ service BookService { rpc CreateBook

    (CreateBookRequest) returns (Book) { option (google.api.http) = { post: "/books" body: "book" }; } } gRPC -> HTTP ͷϚοϐϯά͸ ϝιου͝ͱʹ option ͻͱͭॻ͚ͩ͘
  13. ©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͓΋ΉΖʹ࿩͔͚ͯ͠ΈΑ͏ // app/run.go func Run() error

    { s := grapiserver.New( grapiserver.WithDefaultLogger(), grapiserver.WithServers( server.NewBookServiceServer(), // <- 追加 ), ) return s.Serve() } ※ goimport ࢖ͬͯͳ͍ਓ͸ import Λࣗ෼Ͱॻ͔ͳ͍ͱ͍͚ͳ͍͔΋
  14. ©2018 Wantedly, Inc. ✏࣮ࡍʹϞοΫͳ"1*Λॻ͍ͯΈΑ͏  ABookAʹॻ໊Λ௥Ճͯ͠ΈΑ͏ w )*/5Agrapi protocAΛ࣮ߦ͢Δͱίʔυ͕࠶ੜ੒͞ΕΔ 

    AGET /booksAͰద౰ͳຊΛฦͯ͠ΈΑ͏  APOST /booksAͰύϥϝʔλΛड͚औͬͯΈΑ͏ 8):HSQDHBUFXBZ H31$ HSQDHBUFXBZͰ༡΅͏
  15. ©2018 Wantedly, Inc. 8):HSBQJ H31$ HSQDHBUFXBZͷऑ఺ͦͷᶃ # ↓これは grapi protoc

    # ↓こいつらをラップしている protoc \ -I ./vendor/github.com/grpc-ecosystem-grpc-gateway \ -I ./vendor/github.com/grpc-ecosystem-grpc-gateway/thrid_party/googleapis \ --go_out=plugins=grpc:. protoc \ -I ./vendor/github.com/grpc-ecosystem-grpc-gateway \ -I ./vendor/github.com/grpc-ecosystem-grpc-gateway/thrid_party/googleapis \ --grpc-gateway_out=logtostderr=true:grpc:. ίʔυੜ੒ͷίϚϯυ͕௒೉͘͠ɼຖճ Makefile ॻ͘ͷ΋͠ΜͲ͍… ͜ΕΛ `grapi protoc` ͱ͍͏ίϚϯυʹ Ӆṭ (protoc-grpc-* ʹύεΛ௨͢ͷ΋΍Δ)
  16. ©2018 Wantedly, Inc. αʔόΛىಈ͢Δίʔυ͕௕͍  Anet.ListenerA͕গͳ͘ͱ΋ͭඞཁ w H31$HSQDHBUFXBZΛͭͳ͙΍ͭɼHSQDHBUFXBZ͕֎ͱ௨৴͢Δ΍ͭ w H31$ͱ)551྆ํ֎͔Β࢖͑ΔΑ͏ʹ͠Α͏ͱ͢Δͱߋʹ໽հ

    w ͜Ε͕ಉ͡ϙʔτͩͱߋʹ  H31$TFSWFSͱHBUFXBZ྆ํʹ࣮૷Λొ࿥͢Δඞཁ͕͋Δ w ยํͰ΋࿙ΕΔͱαʔό͕ฦࣄͯ͘͠Εͳ͍ 8):HSBQJ H31$ HSQDHBUFXBZͷऑ఺ͦͷᶄ
  17. ©2018 Wantedly, Inc. HSBQJTFSWFSͷىಈ BQQSVOHPΛݟͯΈΑ͏  Կ΋͠ͳ͚Ε͹  H31$VOJY UNQTFSWFSTPDL

     (BUFXBZUDQ   Ұߦॻ͚ͩ͘ͰʮͲͪΒ΋UDQͰʯͱ͔ ΋Մೳ  TFSWFSIBOEMFS΁ͷ࣮૷ͷొ࿥  Agrapiserver.ServerA౉͚ͩ͢  ͸Agrapi g (scaffold-)serverAͰੜ੒ࡁΈ 8):HSBQJ HSBQJͷىಈγʔέϯε func Run() error { s := grapiserver.New( grapiserver.WithDefaultLogger(), grapiserver.WithServers( server.NewBookServiceServer(), ), ) return s.Serve() } ͖ͬ͢Γ
  18. ©2018 Wantedly, Inc. QSPUPͱHPͷࣗಈੜ੒ खΛൈ͚Δͱ͜Ζ͸ൈ͖͍ͨ  Agrapi g service bookAͰҎԼ͕ੜ੒͞ΕΔ

     ABookServiceAͷ਽ܗ  ͷ࣮૷ABookServiceServerAͷ਽ܗ  Agrapi g scaffold-service bookA΋͋Δ  ʮ(PPHMF"1*ઃܭΨΠυʯͷඪ४ϝιου͕
 ͍͍ײ͡ʹੜ͑Δ 8):HSBQJ ASBJMTOFXAͱ͔ASBJMTHA͍ͬͯ͢͝ΑͶ 3BJMTʢͱ͍͏͔3&45 ʹ׳Ε͍ͯΔਓͳΒೃછΈ΍͍͢ https://cloud.google.com/apis/design/standard_methods
  19. ©2018 Wantedly, Inc. TFSWJDFY ઈରඞཁͳύοέʔδΛॳظԽ͢Δ܅  αʔϏεΛ࡞Δͨͼʹ΍Δ͜ͱ  .POJUPSJOH/FX3FMJD 

    &SSPSSFQPSUJOH)POFZCBEHFS  "DDFTTMPHHJOH  ͜ΕΒͷઃఆΛ؀ڥม਺ݟ͍͍ͯײ͡ʹ΍Δ  HJUIVCDPNXBOUFEMZTFSWJDFY QSJWBUF   ͍·ͷͱ͜Ζ(P޲͚ͷΈଘࡏ  1ZUIPOͱ/PEFKT͸Ͳ͔͜Ͱݕূத ิ଍ 'PS8BOUFEMZ&OHJOFFST ͍ͭ͜Β نఆ͞Εͨ؀ڥม਺Ληοτ͢Δ͚ͩ func Run() error { defer servicex.Close() // <- 追加 s := grapiserver.New( grpcx.WithDefault() // <- 追加 grapiserver.WithServers( server.NewBookServiceServer(), ), ) return s.Serve() }
  20. ©2018 Wantedly, Inc. ิ଍ %#ͱ͔ʹͭͳ͍͗ͨ AfooServiceServerImplAʹ%#ΦϒδΣΫτ౉͢ͱ͍͍Α IUUQTHJUIVCDPNXBOUFEMZHSQDHBUFXBZTUVEZUSFFEFWFMPQ // NewBookServiceServer creates

    a new BookServiceServer instance. func NewBookServiceServer( db *sqlx.DB, ) interface { api_pb.BookServiceServer grapiserver.Server } { return &bookServiceServerImpl{ db: db, } }