要在
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 寫了一個 範例 ,大家可以參考看看以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 支援一波 《面试数十人有感》
- gofight 支援檔案上傳測試
- iOS App 如何支援 RTL 語言
- RDS 支援 Storage Auto Scaling
- 開源專案 Gitea 支援 OAuth Provider
- 如何使 VS Code 支援 CSS Intellisense ?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。