Slide 1

Slide 1 text

©2018 Wantedly, Inc. Go + grpc-gateway Ͱͭ͘Δ JSON API αʔό଎शձ How to create microservice with Go in Wantedly 27.Mar.2018 - @izumin5210

Slide 2

Slide 2 text

©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?

Slide 3

Slide 3 text

©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)

Slide 4

Slide 4 text

©2018 Wantedly, Inc. Agenda ΍͍ͬͯ͘ ͳͥH31$͕ඞཁ͔ͩͬͨ ͳͥH31$͚ͩͰ͸μϝ͔ͩͬͨ ͳͥHSQDHBUFXBZΛ࢖͏ͷ͔ ͳͥHSBQJΛ࡞ͬͨͷ͔ ࣮ࡍʹH31$HSQDHBUFXBZͰ༡Ϳ

Slide 5

Slide 5 text

©2018 Wantedly, Inc. WHY gRPC ͳͥ gRPC Λ࢖͍͍ͨͷ͔

Slide 6

Slide 6 text

©2018 Wantedly, Inc. ͋ΘͤͯΑΈ͍ͨ ͳͥϚΠΫϩαʔϏεʁ ͳͥ3BJMT ͳͥ(Pʁ ‣ ͦ΋ͦ΋%PDLFS؀ڥ͕ૣ͔͘Β͋ͬͨ .JDSPTFSWJDFTLTͷಋೖোน͕গͳ͍ ‣ ཉ͍͠΋ͷׂ͕Γͱ໌֬αʔϏεؒ࿈ܞ (Pͱͷ૬ੑ˕㷉◔ϖ◔㷊 ˞͔ͳΓ୺ં͍ͬͯΔͷͰɼৄ͘͠͸3BJMT%.ͷߨԋΛ લఏ https://speakerdeck.com/altech/microservices-on-rails-wantedly-falsemaikurosabisushi-li

Slide 7

Slide 7 text

©2018 Wantedly, Inc. ʮΩϛ͸ԿΛ͢Δαʔόͳͷʁʯ໰୊ ʮͨͩͨͩ࡞Δͷ͕໘౗ʯ໰୊ WHY gRPC .JDSPTFSWJDFTͳ"1*αʔόΛॻ͘ͱ͖ͷOݸͷ՝୊XJUI(PMBOH

Slide 8

Slide 8 text

©2018 Wantedly, Inc. ʮΩϛ͸ԿΛ͢Δαʔόͳͷʁʯ໰୊ ʮͨͩͨͩ࡞Δͷ͕໘౗ʯ໰୊ WHY gRPC .JDSPTFSWJDFTͳ"1*αʔόΛॻ͘ͱ͖ͷOݸͷ՝୊XJUI(PMBOH

Slide 9

Slide 9 text

©2018 Wantedly, Inc. ࢓༷ɺ͓΅͑ͯ·͔͢ʁ Ϣʔβʹૣ͘Ձ஋Λಧ͚͍ͨʂ ͦͯ٘͠ਜ਼ʹͳΔυΩϡϝϯτ ༨ܭͳίϛϡχέʔγϣϯίετ ʮ͜ͷ"1*ͬͯԿ͕ฦͬͯ͘ΔΜ͚ͩͬʯ +40/4DIFNB΍4XBHHFS΋͍͍͚Ͳʜ ʮڧ੍ྗʯ͕ͳ͍ͷͰ༉அ͢Δͱ෗Δ Ϩίʔυ͚ͭͩ࡞Δ"1*ͷςετॻ͖·͢ʁ 8):H31$ ʮΩϛ͸ͳʹΛ͢Δαʔόͳͷʁʯ Wantedly People Web൛͸AndroidΞϓϦͷίʔυΛࢀߟʹ࣮૷͞Ε͍ͯ·͢ ͳͥͳΒͦ͜ʹ͸ܕ͕͔͋ͬͨΒ

Slide 10

Slide 10 text

©2018 Wantedly, Inc. Ϣʔβ͕ͨ͘͞Μ͍Δ ϞόΠϧΞϓϦ͚ͩͰͳ͘ɼ
 ΄͔ͷϚΠΫϩαʔϏε΋Ϣʔβʢ"1*ར༻ऀʣ ͋ΔαʔϏεͷ࣮૷ͷͨΊʹɼ
 ผͷαʔϏεʹ΋खΛೖΕͳ͍͜ͱ͸ଟʑ͋Δ ࣗ෼͕ॻ͍ͨ΍ͭͱ͸ݶΒͳ͍ ʮ͜ͷࢠͲΜͳ"1*͚࣋ͬͯͨͬʯ 8):H31$ ʮΩϛ͸ͳʹΛ͢Δαʔόͳͷʁʯ ໊ࢗࡱӨͷϑϩʔΛ೺Ѳ͢ΔͨΊͷγʔέϯεਤɼޕલத·Δ·Δ௵ͨ͠ ʢ࣮ࡍͷਤ͸΋͏ͪΐͬͱෳࡶʣ

Slide 11

Slide 11 text

©2018 Wantedly, Inc. ʮΩϛ͸ԿΛ͢Δαʔόͳͷʁʯ໰୊ ʮͨͩͨͩ࡞Δͷ͕໘౗ʯ໰୊ WHY gRPC .JDSPTFSWJDFTͳ"1*αʔόΛॻ͘ͱ͖ͷOݸͷ՝୊XJUI(PMBOH

Slide 12

Slide 12 text

©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 Λͦͷ··࢖ͬͨ৔߹

Slide 13

Slide 13 text

©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 Λ࢖ͬͨ৔߹ ͪΐͬͱϚγ

Slide 14

Slide 14 text

©2018 Wantedly, Inc. HSBQJHTDBGGPMETFSWFSCPPL "1*ͷఆ࣮ٛ૷εέϧτϯΛੜ੒͢Δ 8):H31$ خ͠͞Λମײͯ͠ΈΑ͏

Slide 15

Slide 15 text

©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; }

Slide 16

Slide 16 text

©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Λຬͨ͢Α͏ʹ࣮૷͢Δ͚ͩ

Slide 17

Slide 17 text

©2018 Wantedly, Inc. *OUFSGBDF%SJWFO%FWFMPQNFOU ࠷ॳʹJOUFSGBDFΛܾΊ͔ͯΒ։ൃɼมߋ͢Δͱ͖͸JOUFSGBDFΛ࠷ॳʹมߋ AJOUFSGBDF׬શͳυΩϡϝϯτʢͨͿΜʣA ͜ΕΛເ͡Όͳ࣮͘ݱͰ͖Δͷ͕H31$ͷڧΈ 8):H31$ ͳΜ͔ΤϞ͍͜ͱΛݴ͍͍ͨ

Slide 18

Slide 18 text

©2018 Wantedly, Inc. WHY not gRPC ͳͥ gRPC "͚ͩ" ͩͱμϝͩͬͨͷ͔

Slide 19

Slide 19 text

©2018 Wantedly, Inc. ίϛϡχέʔγϣϯίετ w QSPUPCVGόΠφϦ w ϞόΠϧΞϓϦ΍3BJMTαʔόʹڧཁ͢Δͷʁ w ૉͷ··ͩͱDVSM΋Ͱ͖ͳ͍ͥ Πϯϑϥίετ w H31$͸)551ͷίωΫγϣϯΛӬଓԽ͢Δ w NPOJUPSJOH͸ʁɼMPBECBMBODJOH͸ʁʜߟ͑Δ͜ͱ͸ଟ͍ ϝϦοτ͕ଟ͍൓໘ɼٻΊΒΕΔ஌ࣝͱલఏ΋ଟ͍ WHY not gRPC gRPC ͸ϜζΧγΠ

Slide 20

Slide 20 text

©2018 Wantedly, Inc. WHY grpc-gateway ͳͥ grpc-gateway ΛڬΉͷ͔

Slide 21

Slide 21 text

©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͳͥHSQDHBUFXBZΛڬΉʁ https://github.com/grpc-ecosystem/grpc-gateway

Slide 22

Slide 22 text

©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͳͥHSQDHBUFXBZΛڬΉʁ https://github.com/grpc-ecosystem/grpc-gateway H31$αʔόͷલஈʹཱͭ 3FWFSTF1SPYZΛੜ੒ͯ͘͠ΕΔ

Slide 23

Slide 23 text

©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͳͥHSQDHBUFXBZΛڬΉʁ https://github.com/grpc-ecosystem/grpc-gateway Ϣʔβʢ"1*ୟ͘ਓʣ͔Β͸ )551ܦ༝Ͱ+40/Λ஻͍ͬͯΔΑ͏ʹΈ͑Δ ैདྷ௨Γͷ஌ࣝɾલఏͰ"1*Λར༻Ͱ͖Δʂʂ

Slide 24

Slide 24 text

©2018 Wantedly, Inc. 8):HSQDHBUFXBZ ͳͥHSQDHBUFXBZΛڬΉʁ service BookService { rpc CreateBook (CreateBookRequest) returns (Book) { option (google.api.http) = { post: "/books" body: "book" }; } } gRPC -> HTTP ͷϚοϐϯά͸ ϝιου͝ͱʹ option ͻͱͭॻ͚ͩ͘

Slide 25

Slide 25 text

©2018 Wantedly, Inc. ࣮ࡍʹಈ͔ͯ͠ΈΑ͏ 8):HSQDHBUFXBZ

Slide 26

Slide 26 text

©2018 Wantedly, Inc. HSBQJTFSWFS αʔόΛىಈͯ͠ΈΔAcurl http://localhost:3000/booksA 8):HSQDHBUFXBZ ͓΋ΉΖʹ࿩͔͚ͯ͠ΈΑ͏

Slide 27

Slide 27 text

©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 Λࣗ෼Ͱॻ͔ͳ͍ͱ͍͚ͳ͍͔΋

Slide 28

Slide 28 text

©2018 Wantedly, Inc. ✏࣮ࡍʹϞοΫͳ"1*Λॻ͍ͯΈΑ͏ ABookAʹॻ໊Λ௥Ճͯ͠ΈΑ͏ w )*/5Agrapi protocAΛ࣮ߦ͢Δͱίʔυ͕࠶ੜ੒͞ΕΔ AGET /booksAͰద౰ͳຊΛฦͯ͠ΈΑ͏ APOST /booksAͰύϥϝʔλΛड͚औͬͯΈΑ͏ 8):HSQDHBUFXBZ H31$HSQDHBUFXBZͰ༡΅͏

Slide 29

Slide 29 text

©2018 Wantedly, Inc. WHY grapi ͳͥ grapi ͕ඞཁͩͬͨͷ͔

Slide 30

Slide 30 text

©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-* ʹύεΛ௨͢ͷ΋΍Δ)

Slide 31

Slide 31 text

©2018 Wantedly, Inc. αʔόΛىಈ͢Δίʔυ͕௕͍ Anet.ListenerA͕গͳ͘ͱ΋ͭඞཁ w H31$HSQDHBUFXBZΛͭͳ͙΍ͭɼHSQDHBUFXBZ͕֎ͱ௨৴͢Δ΍ͭ w H31$ͱ)551྆ํ֎͔Β࢖͑ΔΑ͏ʹ͠Α͏ͱ͢Δͱߋʹ໽հ w ͜Ε͕ಉ͡ϙʔτͩͱߋʹ H31$TFSWFSͱHBUFXBZ྆ํʹ࣮૷Λొ࿥͢Δඞཁ͕͋Δ w ยํͰ΋࿙ΕΔͱαʔό͕ฦࣄͯ͘͠Εͳ͍ 8):HSBQJ H31$HSQDHBUFXBZͷऑ఺ͦͷᶄ

Slide 32

Slide 32 text

©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() } ͖ͬ͢Γ

Slide 33

Slide 33 text

©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

Slide 34

Slide 34 text

©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() }

Slide 35

Slide 35 text

©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, } }

Slide 36

Slide 36 text

©2018 Wantedly, Inc. w H31$ʹΑΓ*OUFSGBDF%SJWFO%FWFMPQNFOU͕࣮ݱՄೳ w յΕ͍ͯͳ͍׬શͳυΩϡϝϯτ w H31$ಋೖʹΑΔίετʢΠϯϑϥίετɾલఏ஌ࣝʣ͸
 HSQDHBUFXBZΛ࢖͏͜ͱͰ௿ݮͰ͖Δ w ϘΠϥϓϨʔτͷଟ͞ɼQSPUPDͷ೉͠͞͸HSBQJʹΑΓΧόʔ ·ͱΊ

Slide 37

Slide 37 text

©2018 Wantedly, Inc. 8BOUFEMZ5FDI#PPL!ٕज़ॻయ 50%0ͳΜ͔ॻ͘ ࣍ճ༧ࠂ