$30 off During Our Annual Pro Sale. View Details »

REST is not only (web) API interface

REST is not only (web) API interface

RESTだけじゃなくて他にもインターフェイスは色々あるという話です。
「そうだGo、京都。」で発表。
参考リンクは https://gist.github.com/lufia/79a17579be449cf4e9aad790b58db284 に置きました。

kadota kyohei

April 29, 2017
Tweet

More Decks by kadota kyohei

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. REST

    View Slide

  10. ૹ৴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ׂΓ౰ͯ
    ݻ༗ͷ஋͸ϔομͰ༩͑Δ

    View Slide

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

    View Slide

  12. REST͡Όͳͯ͘΋...

    View Slide

  13. gRPC

    View Slide

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

    View Slide

  15. 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ίʔυੜ੒ ग़ྗσΟϨΫτϦ

    View Slide

  16. 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;
    }

    View Slide

  17. 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
    }
    ΠϯλʔϑΣΠεΛຬͨͤ͞Δ

    View Slide

  18. 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
    }

    View Slide

  19. 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)
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. GraphQL

    View Slide

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

    View Slide

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

    View Slide

  26. GitHub

    View Slide

  27. GitHub API
    Issues
    Issue Comments

    View Slide

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

    View Slide

  29. 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"
    },
    {
    ...
    }
    ]
    }

    View Slide

  30. 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Ͱ͢
    ࠷ॳ͸͔͜͜Β

    View Slide

  31. 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ΩʔΛ୳͢

    View Slide

  32. 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͸ΦϒδΣΫτ

    View Slide

  33. 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ΩʔΛ୳͢

    View Slide

  34. 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ΩʔΛ୳͢

    View Slide

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

    View Slide

  36. 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{ ... },
    },
    })

    View Slide

  37. 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{ ... },
    },
    })

    View Slide

  38. 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ʹ଍ͤΔͷͰ͓खܰ

    View Slide

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

    View Slide

  40. FUSE

    View Slide

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

    View Slide

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

    View Slide

  43. 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͸σΟϨΫτϦͱΧʔωϧʹ఻͑Δ
    ଞͷϝιουΛ࣮૷͍ͯ͠ͳ͍ͷͰɺۭͷσΟϨΫτϦΛϚ΢ϯτ͢Δ͚ͩ

    View Slide

  44. 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
    }
    σΟϨΫτϦΤϯτϦΛදݱ͢Δ৔߹͸͜ͷลΓͷϝιουΛ࣮૷͢Δ

    View Slide

  45. 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
    }
    ϑΝΠϧૢ࡞Λ͢Δ৔߹ͷϝιουͳͲ

    View Slide

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

    View Slide