Golang:使用 Go Routines 和 JSON 进行 HTTP 调用和解析 JSON
Golang: Make HTTP call and parse JSON with Go Routines and JSON
我对 golang 比较陌生,我想创建一种方法来同时调用多个 URL,并解析 JSON 文档。但是,我真的不确定我是否正确使用了 go 例程和通道。在这一点上,我不确定我是否不正确 "thinking in Go" 或者我的 goroutines 和 channels understanding/approach 是否不准确。
另外,在解析的时候,我想解析body中的results
属性,它是一个数组,results
中的每个元素包含一个doc
属性 我想过滤掉。
目标是同时执行多个提取,并仅针对响应正文结果数组中的 doc
属性 解析响应。
非常感谢任何有助于更好地理解事物的见解或建议。提前致谢。
package operations
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
// CouchbaseDoc parses .doc property from sync gateway documents
type CouchbaseDoc struct {
Doc map[string]string `json:"doc"`
}
// Results deconstruct... results is a property of body, and is an array of obects
type Results struct {
Results []byte `json:"results"`
}
func createURLs(channels []string) map[string]string {
urlMap := make(map[string]string)
domain := "swap" + strings.TrimSpace(os.Getenv("env"))
bucket := strings.TrimSpace(os.Getenv("bucket"))
for _, doctype := range channels {
urlMap[doctype] = fmt.Sprintf("https://%s.endpoint.com/%s/_changes?filter=sync_gateway/bychannel&channels=%s&include_docs=true", domain, bucket, doctype)
}
return urlMap
}
func getChangesFeed(url string, ch chan map[string]string) {
resp, _ := http.Get(url)
body, _ := ioutil.ReadAll(resp.Body)
go parseBody(body, ch)
}
func parseBody(body []byte, ch chan map[string]string) {
var results Results
var doc CouchbaseDoc
json.Unmarshal(body, &results)
json.Unmarshal(results.Results, &doc)
// write to responses
ch <- doc.Doc
}
func fetchDocs(channels []string) {
urls := createURLs(channels)
// Response channel is where all go routines will do the dirty
responses := make(chan map[string]string)
for _, url := range urls {
go getChangesFeed(url, responses)
}
// Read from responses channel
docs := <-responses
for doc := range docs {
fmt.Println(doc) // This should print results ??
}
}
修复
这一行:
docs := <-responses
只会从频道接收一个元素,而不是所有元素。但是,您可以在通道上每次预期发送时调用一次接收操作,这将是对您的代码最简单的修复:
responses := make(chan map[string]string)
for _, url := range urls {
go getChangesFeed(url, responses)
}
for x := 0; x < len(urls); x++ {
fmt.Println(<-responses)
}
更多信息
请注意,您使用的是无缓冲通道,因为您没有给通道指定长度。 for e := range ch {
循环仅适用于缓冲通道,并且仅在缓冲通道关闭后才适用。
关闭缓冲通道表示通道上不会发送更多数据,并且可能与您的程序设计不匹配(尤其是没有 sync.WaitGroup
)。
所以使用缓冲通道很好:你只需要知道发送和接收操作都不会继续,除非双方都准备好了:这意味着双方都被阻塞并等待对方。
这很容易通过上面的代码完成,方法是将发送放在 goroutine 中,并使用具有相等计数器的循环在主 goroutine 中排队等量的接收操作。
要了解更多信息,请阅读 The Language Specification and Effective Go and the Mutex and WaitGroup sections of the sync package documentation。
可运行的演示
这里有一个完整的、可运行的示例来演示原理:
package main
import(
"fmt"
"time"
)
func Sleep1(ch chan int) {
time.Sleep(time.Second)
ch <- 1
}
func Sleep3(ch chan int) {
time.Sleep(time.Second * 3)
ch <- 3
}
func Sleep5(ch chan int) {
time.Sleep(time.Second * 5)
ch <- 5
}
func main() {
ch := make(chan int)
go Sleep1(ch)
go Sleep3(ch)
go Sleep5(ch)
for x := 0; x < 3; x++ {
fmt.Println(<-ch)
}
}
我对 golang 比较陌生,我想创建一种方法来同时调用多个 URL,并解析 JSON 文档。但是,我真的不确定我是否正确使用了 go 例程和通道。在这一点上,我不确定我是否不正确 "thinking in Go" 或者我的 goroutines 和 channels understanding/approach 是否不准确。
另外,在解析的时候,我想解析body中的results
属性,它是一个数组,results
中的每个元素包含一个doc
属性 我想过滤掉。
目标是同时执行多个提取,并仅针对响应正文结果数组中的 doc
属性 解析响应。
非常感谢任何有助于更好地理解事物的见解或建议。提前致谢。
package operations
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
// CouchbaseDoc parses .doc property from sync gateway documents
type CouchbaseDoc struct {
Doc map[string]string `json:"doc"`
}
// Results deconstruct... results is a property of body, and is an array of obects
type Results struct {
Results []byte `json:"results"`
}
func createURLs(channels []string) map[string]string {
urlMap := make(map[string]string)
domain := "swap" + strings.TrimSpace(os.Getenv("env"))
bucket := strings.TrimSpace(os.Getenv("bucket"))
for _, doctype := range channels {
urlMap[doctype] = fmt.Sprintf("https://%s.endpoint.com/%s/_changes?filter=sync_gateway/bychannel&channels=%s&include_docs=true", domain, bucket, doctype)
}
return urlMap
}
func getChangesFeed(url string, ch chan map[string]string) {
resp, _ := http.Get(url)
body, _ := ioutil.ReadAll(resp.Body)
go parseBody(body, ch)
}
func parseBody(body []byte, ch chan map[string]string) {
var results Results
var doc CouchbaseDoc
json.Unmarshal(body, &results)
json.Unmarshal(results.Results, &doc)
// write to responses
ch <- doc.Doc
}
func fetchDocs(channels []string) {
urls := createURLs(channels)
// Response channel is where all go routines will do the dirty
responses := make(chan map[string]string)
for _, url := range urls {
go getChangesFeed(url, responses)
}
// Read from responses channel
docs := <-responses
for doc := range docs {
fmt.Println(doc) // This should print results ??
}
}
修复
这一行:
docs := <-responses
只会从频道接收一个元素,而不是所有元素。但是,您可以在通道上每次预期发送时调用一次接收操作,这将是对您的代码最简单的修复:
responses := make(chan map[string]string)
for _, url := range urls {
go getChangesFeed(url, responses)
}
for x := 0; x < len(urls); x++ {
fmt.Println(<-responses)
}
更多信息
请注意,您使用的是无缓冲通道,因为您没有给通道指定长度。 for e := range ch {
循环仅适用于缓冲通道,并且仅在缓冲通道关闭后才适用。
关闭缓冲通道表示通道上不会发送更多数据,并且可能与您的程序设计不匹配(尤其是没有 sync.WaitGroup
)。
所以使用缓冲通道很好:你只需要知道发送和接收操作都不会继续,除非双方都准备好了:这意味着双方都被阻塞并等待对方。
这很容易通过上面的代码完成,方法是将发送放在 goroutine 中,并使用具有相等计数器的循环在主 goroutine 中排队等量的接收操作。
要了解更多信息,请阅读 The Language Specification and Effective Go and the Mutex and WaitGroup sections of the sync package documentation。
可运行的演示
这里有一个完整的、可运行的示例来演示原理:
package main
import(
"fmt"
"time"
)
func Sleep1(ch chan int) {
time.Sleep(time.Second)
ch <- 1
}
func Sleep3(ch chan int) {
time.Sleep(time.Second * 3)
ch <- 3
}
func Sleep5(ch chan int) {
time.Sleep(time.Second * 5)
ch <- 5
}
func main() {
ch := make(chan int)
go Sleep1(ch)
go Sleep3(ch)
go Sleep5(ch)
for x := 0; x < 3; x++ {
fmt.Println(<-ch)
}
}