GraphQL的踩坑之路
為了更好的支援前端能夠自由組合、展示收到的資料,筆者使用graphql-go 框架開發了負責的模組,但測試過程中發現,使用GraphQL會有n+1 query的問題。經過調研發現兩種解決方案:
- 使用graphql-gophers下的graphql-go,已經支援如下特性:
- minimal API
- support for
context.Context
- support for the
OpenTracing
standard - schema type-checking against resolvers
- resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct).
- handles panics in resolvers
- parallel execution of resolvers
- 在使用graphql-go的同時使用facebook開源的dataloader工具,詳細例子見dataloader的issues: Example to use with graphql-go
n + 1 query解釋
Scheme (simplified):
““
type Query {
orders: [Order]
}
type Order {
id: ID
client: Client
}
type Client {
id: ID
name: String
}
““
Query:
query GetOrders {
orders {
client {
name
}
}
}
解釋:
為了返回100個orders,必須傳送101個請求給微服務(1個請求獲取批量的orders,100個獲取它的client資訊(每個order都需要執行一次client資訊查詢)。詳細資訊見issues: No way to get requested fields inside resolver
效能分析過程
使用vegeta工具向服務傳送流量,然後使用pprof和go-torch工具對服務進行分析。
vegeta命令說明
向服務傳送流量命令
echo "POST http://localhost:8080/graphql" | vegeta attack -body /vegeta/rqst.txt -duration=30s -rate=200 > r.bin
傳送流量結果分析
vegeta vegeta report -inputs=r.bin
Requests [total, rate] 6000, 200.03
Duration [total, attack, wait] 29.998652838s, 29.994999868s, 3.65297ms
Latencies [mean, 50, 95, 99, max] 3.740812ms, 3.649598ms, 4.53176ms, 5.088892ms, 22.366809ms
Bytes In [total, mean] 222000, 37.00
Bytes Out [total, mean] 2802000, 467.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
- 向服務傳送流量命令解釋: 向部署在本地的埠號為8080的服務傳送POST請求,持續時間為30s,每秒傳送200個請求,請求的內容存放在vegeta目錄下的rqst.txt檔案中。
- 傳送流量結果分析解釋: 對壓測的結果以報表的形式展示出來。
pprof命令說明
在使用vegeta命令壓測的同時使用go tool的pprof工具分析CPU的耗時go tool pprof -seconds 25 http://localhost:8080/debug/pprof/profile
,持續25s後輸入web ,生成如下所示的內容:
雖然go1.9在使用pprof工具生成CPU耗時分析的時候較之前的版本已經加入了顏色,但是看起來仍不是很直觀,這個時候就需要使用火焰圖了。
go-torch生成火焰圖
在使用vegeta命令壓測的同時使用go-torch工具生成火焰圖,命令go-torch -u http://192.168.189.168 -t 25
生成的火焰圖如下圖所示:(192.168.189.168為本地的localhost)
通過火焰圖分析,graphql-go框架的佔用很多的CPU耗時,查詢資料發現,主要原因是graphql的Resolver函式的解析並非併發解析,所以會產生上面描述的n + 1 query的問題。