Share
Embed
全てのAPIをProtocol Buffersで管理する / Manage all APIs with Protocol Buffers
全てのAPIをProtocol Buffersで管理する方法と価値を #shinjukult で発表した資料
disc99
October 18, 2019
Transcript
49 slides
-
"1*ΛͲ͏ཧͯ͠·͔͢ w ςΩετɺΤΫηϧΈ͍ͨͳ੩తͳυΩϡϝϯτ w 0QFO"1* 4XBHHFS w +40/4DIFNB w
1SPUPDPM#V⒎FST w (SBQI2- -
ࠓͷ w ςΩετɺΤΫηϧΈ͍ͨͳ੩తͳυΩϡϝϯτ w 0QFO"1* 4XBHHFS w +40/4DIFNB w
1SPUPDPM#V⒎FST w (SBQI2- -
ͪͳΈʹ w ςΩετɺΤΫηϧΈ͍ͨͳ੩తͳυΩϡϝϯτ w 0QFO"1* 4XBHHFS w +40/4DIFNB w
1SPUPDPM#V⒎FST w (SBQI2-˞ࠓͷςʔϚͱҰ෦ྨࣅ͢Δ෦͋Γ -
w 'VODUJPO"͔Β'VODUJPO#Λݺͼग़͢ 4ZTUFN 3VOUJNF ैདྷͷॲཧ 'VODUJPOͷੈք $PEF 'VODUJPO"
\ ʜ ʜ 'VODUJPO# ʜ ʜ ^ $PEF 'VODUJPO# \ ʜ ʜ ʜ ʜ ʜ ^ -
4ZTUFN 3VOUJNF $PEF 'VODUJPO" \ ʜ ʜ "1*&YFDVUF ʜ
ʜ ^ "1*$MJFOU "1*&YFDVUF \ ʜ ʜ ʜ ʜ ʜ ^ "1*ͷੈք w 'VODUJPO"͔Β'VODUJPO#Λݺͼग़͢ /FUXPSL 4ZTUFN 3VOUJNF "1*4FSWFS &OEQPJOU \ ʜ ʜ )BOEMF ʜ ʜ ^ $PEF 'VODUJPO# \ ʜ ʜ ʜ ʜ ʜ ^ -
ఆٛ w ఆٛ w ςΩετɺ0QFO"1*4QFDJpDBUJPOɺ(SBQI2-ɺ1SPUPDPM#V⒎FST w ϑΥʔϚοτ w +40/ɺ9.-ɺόΠφϦ w
ϓϩτίϧ w )551ɺ)551ɺ8FC4PDLFUɺ5$1 -
'VODUJPOͷੈքWT"1*ͷੈք 'VODBUJPO "1* ఆٛ ݴޠ༷ ༷ʑͳఆٛ ϑΥʔϚοτ ݴޠ༷
༷ʑͳϑΥʔϚοτ ϓϩτίϧ ݴޠ༷ ༷ʑͳϓϩτίϧ ίʔυ ͙͢ʹδϟϯϓͰ͖Δ ༷ʑͳϙΠϯτΛܦ༝͢Δඞཁ͕͋Δ ݺͼग़͠ ݴޠ*%&Ͱߴਫ਼อূ جຊࣦഊ͠ͳ͍ ఆ͕࣮ٛ͞Ε͍ͯΔอূ͕͠ʹ͍͘ ωοτϫʔΫͰࣦഊ͋Δ -
1SPUPDPM#V⒎FST w ಛఆͷϓϩάϥϛϯάݴޠɾϓϥοτϑΥʔϜඇґଘͳεΩʔϚݴޠ w ΠϯλʔϑΣΠεΛఆٛ͢ΔͨΊͷ*%- *OUFSGBDF%FpOJUJPO -BOHVBHF w ओཁͳχʔζΛຬͨͨ͢ΊͷίϯύΫτͳ༷
w ߴͳػೳ͕ͳ͍ͨΊ୭ͰಡΈॻ͖͍͢͠ w γϦΞϥΠζϑΥʔϚοτ +40/ͳͲͷผϑΥʔϚοτͷมߋՄೳ w σϑΝΫτͷ௨৴ϑϨʔϜϫʔΫ H31$ syntax = "proto3"; package example; import "google/api/annotations.proto"; // Service for handling books. service BookService { // Get book rpc GetBook (GetBookRequest) returns (Book) {} // Create book rpc CreateBook (CreateBookRequest) returns (Book) {} } // Request for get book message GetBookRequest { string id = 1; } // Request for get book message CreateBookRequest { string name = 1; } // Book data message Book { string id = 1; string name = 2; } -
εΩʔϚݴޠͷՁ w ΞϓϦέʔγϣϯ։ൃલʹࣄલఆٛɺؔऀͰ߹ҙͰ͖Δ w ΠϯλʔϑΣΠεΛఆٛ͢Δ͜ͱʹಛԽ %4- ͍ͯ͠ΔͨΊՄಡੑ͕ߴ͍ w ιʔείʔυཧ͕͍͢͠ɾमਖ਼ͳͲ13ͳͲͰ؆୯ʹཧͰ͖Δ w
ಛఆͷݴޠϓϥοτϑΥʔϜʹඇґଘͳͨΊɺಉҰͷఆ͔ٛΒཁ݅ʹ߹Θ࣮ͤ ΛબͰ͖Δ w ҙͷίʔυΛੜ͢Δ͜ͱ͕Ͱ͖Δ -
ଞͷ"1*ཧWT1SPUPDPM#V⒎FST ଞͷ"1*ཧ 1SPUPDPM#V⒎FST બࢶ ఆٛݺͼग़͠ͷબࢶ͕૿͑Δ ඪ४తͳํ͕ࣜఆ·͍ͬͯΔ ৴པੑ ఆٛΛ࣮֬ʹ࣮Ͱ͖Δอূ͕ͳ͍
ίʔυੜʹΑΓ৴པੑ্͕͢Δ ֶशίετ ෯͍χʔζΛΧόʔ͠Α͏ͱ͍ͯ͠ΔͨΊ ֶशίετ͕ߴ͘ͳΓ͍͢ ओཁͳχʔζΛλʔήοτͱ͍ͯ͠ΔͨΊ ֶशίετ͕͍ Մಡੑ ൚༻తͳϑΥʔϚοτ :BNMɺ+40/ Λ ༻ͯ͠Δͷ࢝Ί͍͕͢Մಡੑ͕Լ͕ Γ͍͢ ઐ༻ͷ%4-ͷͨΊ'VODBUJPOʹΑΔཧͷ ײ֮ʹ͍ۙ -
1SPUPDPM#V⒎FSTʹΑΔ3&45"1*ཧ w QSPUPDHFOTXBHHFS w εΩʔϚఆٛʹै͍ɺTXBHHFS 0QFO"1* ͷ+40/Λग़ྗ͢Δπʔϧ w ಉҰͷఆ͔ٛΒ3&45"1*ཧɺ4XBHHFS$PEFHFOʹΑΔίʔυੜͱ࿈ܞ
// Service for handling books. service BookService { // Get book rpc GetBook (GetBookRequest) returns (Book) { option (google.api.http) = { get: "/books/{id}" }; }; } // Request for get book message GetBookRequest { string id = 1; } // Book data message Book { string id = 1; string name = 2; } { "swagger": "2.0", "info": {…}, "schemes": […], "consumes": […], "produces": […], "paths": { "/books/{id}": { "get": { "summary": "Get book", "operationId": "GetBook", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": “#/definitions/exampleBook" } } }, "parameters": [ { "name": "id", "in": "path", "required": true, "type": "string" } ], "tags": […] } } }, "definitions": { "exampleCreateBookRequest": { "type": "object", "properties": { "name": { "type": "string" } }, "title": "Request for get book" }, “exampleBook": {…} } } } -
1SPUPDPM#V⒎FSTʹΑΔ੩తυΩϡϝϯτཧ w QSPUPDHFOEPD w εΩʔϚఆٛʹै͍ɺ)5.-ɺ+40/ɺ%PD#PPLɺ.BSLEPXOϑΝΠϧΛੜ͢Δπʔϧ // Service for
handling books. service BookService { // Get book rpc GetBook (GetBookRequest) returns (Book) {} // Create book rpc CreateBook (CreateBookRequest) returns (Book) {} } // Request for get book message GetBookRequest { string id = 1; // Book ID } // Request for get book message CreateBookRequest { string name = 1; // Book name } // Book data message Book { string id = 1; // Book ID string name = 2; // Book name } -
ͪΖΜH31$ w QSPUPDHFOHSQD w εΩʔϚఆٛʹै͍ɺH31$αʔόʔͱελϒΛੜ public class BookService
extends BookServiceGrpc.BookServiceImplBase { @Override public void getBook(GetBookRequest request, StreamObserver<Book> responseObserver) { Book response = Book.newBuilder().setId(request.getId()).setName("My book").build(); responseObserver.onNext(response); responseObserver.onCompleted(); } @Override public void createBook(CreateBookRequest request, StreamObserver<Book> responseObserver) { String id = UUID.randomUUID().toString(); Book response = Book.newBuilder().setId(id).setName(request.getName()).build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } BookServiceGrpc.BookServiceBlockingStub stub; void getBook(String id) { GetBookRequest request = GetBookRequest.newBuilder().setId(id).build(); try { Book response = stub.getBook(request); log.info("Book name: " + response.getName()); } catch (Exception e) { log.error("RPC failed", e); } } // Service for handling books. service BookService { // Get book rpc GetBook (GetBookRequest) returns (Book) {} // Create book rpc CreateBook (CreateBookRequest) returns (Book) {} } // Request for get book message GetBookRequest { string id = 1; } // Request for get book message CreateBookRequest { string name = 1; } // Book data message Book { string id = 1; string name = 2; } -
ඇಉظΠϕϯτͷཧ w QSPUPDHFO\MBOHVBHF^ w NFTTBHFʹΑΔΠϕϯτఆٛͱίʔυੜɺγϦΞϥΠζ ΠϕϯτۦಈϚΠΫϩαʔϏεΞʔΩςΫνϟ IUUQTTQFBLFSEFDLDPNEJTDFWFOUESJWFONJDSPTFSWJDFTBSDIJUFDUVSF TMJEF
ར༻ྫ ".-νʔϜ͕ͲͷΑ͏ʹϝϧϖΠͷσʔλΛ4QMVOLʹूΊ׆༻͍ͯ͠Δ͔ IUUQTUFDINFSDBSJDPNFOUSZ -
ͪͳΈʹ w ֤ݴޠ͚ίʔυੜΛͯ͠Δπʔϧ w QSPUPDHFOHP w QSPUPDHFOKBWB w
QSPUPDHFOQZUIPO w QSPUPDHFOEBSU w QSPUPDHFOʜ w ཱͭπʔϧ w QSPUPDHFOWBMJEBUFఆٛʹόϦσʔγϣϯΛՃ w QSPUPDHFOMJOUఆٛͷ-JOUΛ࣮ߦ w QSPUPDHFOKTPOTDIFNBఆ͔ٛΒ+40/4DIFNBΛੜ w QSPUPDHFOVNMఆ͔ٛΒ6.-Λੜ -
εΩʔϚఆٛʹΑΔΤίγεςϜ TXBHHFS 0QFO"1* +BWB NBSLEPXO QSPUP ʜ ʜ
ʜ H31$ 1ZUIPO (P IUNM ίʔυੜ -
εΩʔϚఆٛʹΑΔΤίγεςϜ TXBHHFS 0QFO"1* +BWB NBSLEPXO QSPUP ʜ ʜ
ʜ H31$ 1ZUIPO (P IUNM ֤πʔϧͷΤίγεςϜͱͷ࿈ܞ -
QSPUPDϓϥάΠϯ w ϓϥάΠϯ QSPUPDHFOTXBHHFS QSPUPDHFOEPD QSPUPDHFOHSQD QSPUPDHFOHP w ࣮ߦίϚϯυ QSPUPD1SPUPDPM#V⒎FSTίϯύΠϥ
QSPUPDTXBHHFS@PVUFYBNQMFQSPUP QSPUPDEPD@PVUFYBNQMFQSPUP QSPUPDHSQD@PVUFYBNQMFQSPUP QSPUPDHP@PVUFYBNQMFQSPUP -
QSPUPDϓϥάΠϯ w ϓϥάΠϯ QSPUPDHFOTXBHHFS QSPUPDHFOEPD QSPUPDHFOHSQD QSPUPDHFOHP w ࣮ߦίϚϯυ QSPUPD1SPUPDPM#V⒎FSTίϯύΠϥ
QSPUPDTXBHHFS@PVUFYBNQMFQSPUP QSPUPDEPD@PVUFYBNQMFQSPUP QSPUPDHSQD@PVUFYBNQMFQSPUP QSPUPDHP@PVUFYBNQMFQSPUP -
QSPUPDϓϥάΠϯͷΈ ɹ$ protoc —-xxx_out=. example.proto ίϚϯυ࣮ߦ NFTTBHF#PPL\ ʜ
ʜ ʜ ^ FYBNQMFQSPUP -
QSPUPDϓϥάΠϯͷΈ QSPUPDղੳ݁ՌΛϓϥάΠϯʹඪ४ग़ྗ ɹ$ protoc —-xxx_out=. example.proto QSPUPDQMVHJO FYQSPUPDHFOYYY
QSPUPD NFTTBHF#PPL\ ʜ ʜ ʜ ^ FYBNQMFQSPUP -
QSPUPDϓϥάΠϯͷΈ ϓϥάΠϯग़ྗ͍ͨ͠༰Λඪ४ग़ྗ ɹ$ protoc —-xxx_out=. example.proto QSPUPDQMVHJO FYQSPUPDHFOYYY
QSPUPD NFTTBHF#PPL\ ʜ ʜ ʜ ^ FYBNQMFQSPUP -
QSPUPDϓϥάΠϯͷΈ QSPUPDQMVHJO FYQSPUPDHFOYYY QSPUPD NFTTBHF#PPL\ ʜ ʜ ʜ
^ FYBNQMFQSPUP (FOFSBUFE $PEF QSPUPDϓϥάΠϯͷग़ྗʹै͍ϑΝΠϧΛੜ ɹ$ protoc —-xxx_out=. example.proto -
QSPUPDϓϥάΠϯͷ࡞ w FYQSPUPDHFOEVNQ w ೖྗ͞ΕͨίʔυੜϦΫΤετΛͦͷ··μ ϯϓσʔλͱͯ͠ग़ྗ w ͜Ε͚ͩͷίʔυͰϓϥάΠϯ͕؆୯ʹ࡞Մೳ w ೖྗग़ྗͷܗࣜ1SPUPDPM#V⒎FSTͰఆٛࡁΈ
func main() { log.SetFlags(0) log.SetPrefix("protoc-gen-proto: ") data, err := ioutil.ReadAll(os.Stdin) if err != nil {…} request := &plugin.CodeGeneratorRequest{} if err := proto.Unmarshal(data, request); err != nil {…} if len(request.FileToGenerate) == 0 {…} params := util.ParseParams(request) name := "out.gob" if v, ok := params["out"]; ok { name = v } response := &plugin.CodeGeneratorResponse{} data, err = proto.Marshal(request) if err != nil { response.Error = proto.String(err.Error()) } else { response.File = []*plugin.CodeGeneratorResponse_File{ &plugin.CodeGeneratorResponse_File{ Name: proto.String(name), Content: proto.String(string(data)), }, } } data, err = proto.Marshal(response) if err != nil {…} _, err = io.Copy(os.Stdout, bytes.NewReader(data)) if err != nil {…} } -
ϓϥάΠϯʹΑΔ֦ு TXBHHFS 0QFO"1* +BWB NBSLEPXO QSPUP ʜ ʜ
ʜ H31$ 1ZUIPO (P IUNM ඞཁͳ ίʔυ ϓϥάΠϯͰՃੜ -
ϓϥάΠϯʹΑΔ֦ு TXBHHFS 0QFO"1* +BWB NBSLEPXO QSPUP ʜ ʜ
ʜ H31$ 1ZUIPO (P IUNM ඞཁͳ ίʔυ ߋʹ࿈ܞͷڧԽ -
Ԡ༻ྫ w QSPUPDHFOTQSJOHXFCqVY IUUQTHJUIVCDPNEJTDQSPUPDHFOTQSJOHXFCqVY w H31$αʔόʔΛ+40/PWFS)551ରԠͤ͞ΔϓϥάΠϯ w H31$αʔόʔʹରͯ͠มPSϦόϓϩੜ
MJLF HSQDHBUFXBZ ͷػೳͷ֦ுΛఏڙ w 4QSJOH8FC'MVYͷ)BOEMFSΛੜ w HPPHMFBQJIUUQʹରԠͨ͠3&45"1*ੜՄೳ w *%-ΑΔཧɺίʔυੜʹΑΔ৴པੑɺ+40/ 0QFO"1*ͷΤίγεςϜͷ࿈ܞΛ࣮ݱ