Slide 1

Slide 1 text

REST is not only (web) API interface ͦ͏ͩGoɺژ౎ɻ @plan9user (2017-04-29)

Slide 2

Slide 2 text

ࣗݾ঺հ • ໳ଟګฏ • Favorites • OS: Plan 9 • Editor: acme • Shell: rc • ϑΣϯϦϧͰಇ͍͍ͯ·͢

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

ࠓ೔࿩͢͜ͱ • REST API͸ྑ͍બ୒͚ͩͲ΋ • ͦ͏͡Όͳ͍ํ๏΋͋ΔΑ • gRPC • GraphQL • FUSE

Slide 5

Slide 5 text

RESTͰϓογϡ௨஌API͸ɺͲ ͏ઃܭ͢Ε͹͍͍ΜͩΖ͏ • ΤϯυϙΠϯτ͸send? request? etc… • ϝοηʔδ͸·ͱΊΔʁ෼͚Δʁ

Slide 6

Slide 6 text

લఏ • iOS΋Android΋ɺ୺຤+ΞϓϦ͝ͱʹϢχʔ ΫͳIDΛ࣋ͭ(τʔΫϯ) • τʔΫϯͱϝοηʔδͷηοτͰ௨஌͢Δ • ࡉ͔͍෦෼͸ϓϥοτϑΥʔϜ͝ͱʹҧ͏

Slide 7

Slide 7 text

(ࢀߟ)APNsͷϖΠϩʔυ { "aps": { "alert": { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢", "action-loc-key": "PLAY" }, "badge": 5 }, "attr1": "bar", "attr2": [ "bang", "whiz" ] }

Slide 8

Slide 8 text

(ࢀߟ)FCMͷϖΠϩʔυ { "to": "(τʔΫϯจࣈྻ)", "notification": { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢", "icon": "myicon" }, "data": { "attr1": "bar", "attr2": "bang" } }

Slide 9

Slide 9 text

REST

Slide 10

Slide 10 text

ૹ৴API (v1) POST /devices/:device_token Content-Type: application/json X-Priority: high X-TTL: 300 { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢", "icon": "myicon", "data": { "attr1": "bar", "attr2": "bang" } } σόΠεຖʹURIׂΓ౰ͯ ݻ༗ͷ஋͸ϔομͰ༩͑Δ

Slide 11

Slide 11 text

ૹ৴API (v2) POST /messages Content-Type: application/json [ { "tokens": ["token1", "token2"], "title": "αϯϓϧ", "body": “͜Ε͸ςετ௨஌Ͱ͢", "priority": "high" }, { "tokens": ["token3", "token4", "token5"], "title": "αϯϓϧ2", "body": "͜Ε͸2ͭ໨ͷςετ௨஌Ͱ͢" } ] URI͸·ͱΊΔ

Slide 12

Slide 12 text

REST͡Όͳͯ͘΋...

Slide 13

Slide 13 text

gRPC

Slide 14

Slide 14 text

gRPC • HTTP/2 • Protocol Buffer version 3 • GoogleʹΑͬͯ։ൃ͞Εͨ(ݱࡏ͸OSS) • Go, Java, C++, Ruby, C#, Node.js, PHP ...

Slide 15

Slide 15 text

GoͰgRPC // ίϚϯυͷΠϯετʔϧ $ brew install protobuf $ go get -u github.com/golang/protobuf/protoc-gen-go // ΤσΟλͰprotoϑΝΠϧΛॻ͘ $ acme service.proto // ίʔυੜ੒ $ protoc --go_out=plugins=grpc:. service.proto gRPCίʔυੜ੒ ग़ྗσΟϨΫτϦ

Slide 16

Slide 16 text

GoͰgRPC // service.proto service PushService { rpc Send (Message) returns (stream Result); } message Message { repeated string tokens = 1; Priority priority = 2; string payload = 3; int32 ttl = 4; } enum Priority { HIGH = 0; NORMAL = 1; }

Slide 17

Slide 17 text

GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil } ΠϯλʔϑΣΠεΛຬͨͤ͞Δ

Slide 18

Slide 18 text

GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil }

Slide 19

Slide 19 text

GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) func main() { l, err := net.Listen("tcp", ":8080") if err != nil { log.Fatalln(err) } s := grpc.NewServer() pb.RegisterPushServiceServer(s, &Server{}) s.Serve(l) }

Slide 20

Slide 20 text

GoͰgRPC service PushService { rpc Send (Message) returns (stream Result); } ୯ൃϨεϙϯε ෳ਺Ϩεϙϯε ୯ൃϦΫΤετ 6OBSZ31$ 4FSWFSTUSFBNJOH31$ ෳ਺ϦΫΤετ $MJFOUTUSFBNJOH31$ #JEJSFDUJPOBMTUSFBNJOH31$ छྨʹΑͬͯϝιουͷγάωνϟ͕มΘΔ

Slide 21

Slide 21 text

ࢀߟࢿྉ • Protocol Buffer Language Guide • protoc-gen-go parameters • gRPC Basics - Creating the server • GRPCͷ࣮ફͱݱঢ়Ͱͷར఺ɾܽ఺

Slide 22

Slide 22 text

net/rpcύοέʔδ • جຊతʹGo͔ΒͷΈར༻ • Go 1.8͔Βౚ݁͞Εͨ...

Slide 23

Slide 23 text

GraphQL

Slide 24

Slide 24 text

GraphQL • Query language • JavaScript • Go൛͸ github.com/graphql-go/graphql

Slide 25

Slide 25 text

ࡢ೥ͷΞυϕϯτΧϨϯμʔ

Slide 26

Slide 26 text

GitHub

Slide 27

Slide 27 text

GitHub API Issues Issue Comments

Slide 28

Slide 28 text

ϑΝΠϧγεςϜͳͷͰ... • 1ϦϙδτϦͰʮIssue݅਺+1ʯճϦΫΤετ • IssueͷϦετ • ͦΕͧΕͷIssueίϝϯτ • find͢ΔͱͭΒ͍

Slide 29

Slide 29 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ HTTP/1.1 200 OK { "owner": "lufia", "organization": null, "issues": [ { "title": "add Plan 9 support", "body": "I should support Plan 9.", "html_url": "https://github.com/lufia/taskfs/issues/1", "comments": [ { "body": "9P implementations", "created_at": "2016-12-11T23:14:00+09:00", "updated_at": "2016-12-11T23:14:00+09:00" } ], "created_at": "2016-12-11T23:13:00+09:00", "updated_at": "2016-12-11T23:13:00+09:00" }, { ... } ] }

Slide 30

Slide 30 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ࠷ॳ͸͔͜͜Β

Slide 31

Slide 31 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repositoryΩʔΛ୳͢

Slide 32

Slide 32 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repository͸ΦϒδΣΫτ

Slide 33

Slide 33 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ownerΩʔΛ୳͢

Slide 34

Slide 34 text

GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ organizationΩʔΛ୳͢

Slide 35

Slide 35 text

GoͰGraphQL • ຊ౰͸Schema͔ΒΦϒδΣΫτΛੜ੒͢Δ • GoͰ͸·ͩSchema͔Βੜ੒͸Ͱ͖ͳ͍ • ࠓճ͸ख࡞ۀ...

Slide 36

Slide 36 text

GoͰGraphQL(ϑΟʔϧυ) import ( "github.com/graphql-go/graphql" ) var RepositoryType = graphql.NewObject(graphql.ObjectConfig{ Name: "Repository", Fields: graphql.Fields{ "owner": &graphql.Field{ ... }, "organization": &graphql.Field{ ... }, "issues": graphql.Field{ ... }, }, })

Slide 37

Slide 37 text

GoͰGraphQL(஋ͷղܾ) type Issue struct{ ... } var IssueType = graphql.NewObject(graphql.ObjectConfig{ Name: "Issue", Fields: graphql.Fields{ "title": &graphql.Field{ Type: graphql.NonNull(graphql.String), Description: "λΠτϧ", Resolve: func(p graphql.ResolveParams) (interface{}, error) { if v, ok := p.Source.(*Issue); !ok { return "", nil } return v.Title, nil }, }, "body": &graphql.Field{ ... }, "html_url": &graphql.Field{ ... }, }, })

Slide 38

Slide 38 text

GoͰGraphQL func main() { schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: QueryType, }) http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request){ q, err := ioutil.ReadAll(r.Body) res := graphql.Do(graphql.Params{ Schema: schema, RequestString: string(q), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(res) }) http.ListenAndServe(":8080", nil) } طଘAPIʹ଍ͤΔͷͰ͓खܰ

Slide 39

Slide 39 text

ࢀߟࢿྉ • GraphQL (ݴޠ࢓༷) • Introduction to GraphQL • GraphQLೖ໳ - ࢖͍ͨ͘ͳΔGraphQL • GitHub GraphQL API (Early Access)

Slide 40

Slide 40 text

FUSE

Slide 41

Slide 41 text

FUSE • Filesystem in Userspace • Plan 9 • FUSE, FUSE for macOS, Dokan

Slide 42

Slide 42 text

FUSEͷಈ࡞ • ϢʔβϓϩηεͰϑΝΠϧγεςϜ • γεςϜίʔϧʹద੾ͳԠ౴Λฦ͢ • ͍͍ͩͨશ෦Ͱ30ݸఔ౓ • ඞཁͳ෼͚࣮ͩ૷͢Δ • ࣮ࡍͷ௨৴͸ͳΜͰ΋͍͍(HTTP, MQTT, 9P)

Slide 43

Slide 43 text

GoͰFUSE import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" ) type Root struct { nodefs.Node } func (root *Root) GetAttr(out *fuse.Attr, file nodefs.File, ctx *fuse.Context) fuse.Status { out.Mode = fuse.S_IFDIR | 0755 out.Atime = uint64(time.Now().Unix()) out.Mtime = uint64(time.Now().Unix()) return fuse.OK } func main() { root := &Root{Node: nodefs.NewDefaultNode()} s, _, err := nodefs.MountRoot("/mnt/taskfs", root, &opts) s.Serve() } root͸σΟϨΫτϦͱΧʔωϧʹ఻͑Δ ଞͷϝιουΛ࣮૷͍ͯ͠ͳ͍ͷͰɺۭͷσΟϨΫτϦΛϚ΢ϯτ͢Δ͚ͩ

Slide 44

Slide 44 text

GoͰFUSE func (root *Root) Lookup(out *fuse.Attr, name string, ctx *fuse.Context) (*nodefs.Inode, fuse.Status) { _, status := root.readDir() if status != fuse.OK { return nil, status } c := root.lookupName(name) return c.GetAttr(out, nil, ctx) } func (root *Root) OpenDir(ctx *fuse.Context) ([]fuse.DirEntry, fuse.Status){ kids, err := root.readDir() a := make([]fuse.DirEntry, len(kids)) for i, kid := range kids { kid.Stat().FillDirEntry(&a[i]) } return a, fuse.OK } σΟϨΫτϦΤϯτϦΛදݱ͢Δ৔߹͸͜ͷลΓͷϝιουΛ࣮૷͢Δ

Slide 45

Slide 45 text

GoͰFUSE func (ctl *Ctl) Open(flags uint32, ctx *fuse.Context) (nodefs.File, fuse.Status) { p, err := ctl.ReadFile() if err != nil { return nil, fuse.EIO } return nodefs.NewDataFile(p), fuse.OK } func (ctl *Ctl) Truncate(file nodefs.File, size uint64, ctx *fuse.Context) fuse.Status { return fuse.OK } func (ctl *Ctl) Write(file nodefs.File, data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) { err := ctl.WriteFile(data) if err != nil { return 0, fuse.EINVAL } return uint32(len(data)), fuse.OK } ϑΝΠϧૢ࡞Λ͢Δ৔߹ͷϝιουͳͲ

Slide 46

Slide 46 text

ࢀߟࢿྉ • ϑΝΠϧڞ༗ϓϩηεͷ؆ૉԽ໨ࢦ͢Φʔϓ ϯιʔεϓϩδΣΫτʮUpspinʯൃද • GoͰFUSEΛ࢖ͬͯGitHubͷIssuesΛϚ΢ϯ τ͢Δ • 9P - Wikipedia