Go 語言的 graphQL-go 套件正式支援 Concurrent Resolvers

栏目: Go · 发布时间: 7年前

Go 語言的 graphQL-go 套件正式支援 Concurrent Resolvers 要在 Go 語言 寫 graphQL,大家一定對 graphql-go 不陌生,討論度最高的套件,但是我先說,雖然討論度是最高,但是效能是最差的,如果大家很要求效能,可以先參考此 專案 ,裡面有目前 Go 語言的 graphQL 套件比較效能,有機會在寫另外一篇介紹。最近 graphql-go 的作者把 Concurrent Resolvers 的解法寫了一篇 Issue 來討論 ,最終採用了 Resolver returns a Thunk 方式來解決 Concurrent 問題,這個 PR 沒有用到額外的 goroutines,使用方式也最簡單
"pullRequests": &graphql.Field{
    Type: graphql.NewList(PullRequestType),
    Resolve: func(p graphql.ResolveParams) (interface{}, error) {
        ch := make(chan []PullRequest)
        // Concurrent work via Goroutines.
        go func() {
            // Async work to obtain pullRequests.
            ch <- pullRequests
        }()
        return func() interface{} {
            return <-ch
        }, nil
    },
},

使用方式

先用一個簡單例子來解釋之前的寫法會是什麼形式
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"

    "github.com/graphql-go/graphql"
)

type Foo struct {
    Name string
}

var FieldFooType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Foo",
    Fields: graphql.Fields{
        "name": &graphql.Field{Type: graphql.String},
    },
})

type Bar struct {
    Name string
}

var FieldBarType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Bar",
    Fields: graphql.Fields{
        "name": &graphql.Field{Type: graphql.String},
    },
})

// QueryType fields: `concurrentFieldFoo` and `concurrentFieldBar` are resolved
// concurrently because they belong to the same field-level and their `Resolve`
// function returns a function (thunk).
var QueryType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Query",
    Fields: graphql.Fields{
        "concurrentFieldFoo": &graphql.Field{
            Type: FieldFooType,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                type result struct {
                    data interface{}
                    err  error
                }
                ch := make(chan *result, 1)
                go func() {
                    defer close(ch)
                    time.Sleep(1 * time.Second)
                    foo := &Foo{Name: "Foo's name"}
                    ch <- &result{data: foo, err: nil}
                }()
                r := <-ch
                return r.data, r.err
            },
        },
        "concurrentFieldBar": &graphql.Field{
            Type: FieldBarType,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                type result struct {
                    data interface{}
                    err  error
                }
                ch := make(chan *result, 1)
                go func() {
                    defer close(ch)
                    time.Sleep(1 * time.Second)
                    bar := &Bar{Name: "Bar's name"}
                    ch <- &result{data: bar, err: nil}
                }()
                r := <-ch
                return r.data, r.err
            },
        },
    },
})

func main() {
    schema, err := graphql.NewSchema(graphql.SchemaConfig{
        Query: QueryType,
    })
    if err != nil {
        log.Fatal(err)
    }
    query := `
        query {
            concurrentFieldFoo {
                name
            }
            concurrentFieldBar {
                name
            }
        }
    `
    result := graphql.Do(graphql.Params{
        RequestString: query,
        Schema:        schema,
    })
    b, err := json.Marshal(result)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", b)
    /*
        {
          "data": {
            "concurrentFieldBar": {
              "name": "Bar's name"
            },
            "concurrentFieldFoo": {
              "name": "Foo's name"
            }
          }
        }
    */
}
接著看看需要多少時間來完成執行
$ time go run examples/concurrent-resolvers/main.go | jq
{
  "data": {
    "concurrentFieldBar": {
      "name": "Bar's name"
    },
    "concurrentFieldFoo": {
      "name": "Foo's name"
    }
  }
}

real    0m4.186s
user    0m0.508s
sys     0m0.925s
總共花費了四秒,原因是每個 resolver 都是依序執行,所以都需要等每個 goroutines 執行完成才能進入到下一個 resolver,上面例子該如何改成 Concurrent 呢,很簡單,只要將 return 的部分換成
return func() (interface{}, error) {
                    r := <-ch
                    return r.data, r.err
                }, nil
執行時間如下
$ time go run examples/concurrent-resolvers/main.go | jq
{
  "data": {
    "concurrentFieldBar": {
      "name": "Bar's name"
    },
    "concurrentFieldFoo": {
      "name": "Foo's name"
    }
  }
}

real    0m1.499s
user    0m0.417s
sys     0m0.242s
從原本的 4 秒多,變成 1.5 秒,原因就是兩個 resolver 的 goroutines 會同時執行,最後才拿結果。

心得

有了這功能後,比較複雜的 graphQL 語法,就可以用此方式加速執行時間。作者也用 Mogodb + graphql 寫了一個 範例 ,大家可以參考看看

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

世界因你不同:李开复自传(纪念版)

世界因你不同:李开复自传(纪念版)

李开复,范海涛 著作 / 中信出版社 / 2015-7-10 / 39.00

编辑推荐 1.李开复唯一一部描写全面生平事迹的传记:《世界因你不同:李开复自传》书中讲述了家庭教育培育的“天才少年”;学校教育塑造的“创新青年”,走入世界顶级大公司,苹果、微软、谷歌等亲历的风云内幕,岁月30载不懈奋斗、追求事业成功的辉煌历程。 2.娓娓道来、字字珠玑、可读性和故事性皆佳。李开复博士是青少年成长成才的励志偶像,年轻家长、学校教师阅读后也能从中得到感悟和启发。 3.......一起来看看 《世界因你不同:李开复自传(纪念版)》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具