【问题标题】:Golang: Make HTTP call and parse JSON with Go Routines and JSONGolang:使用 Go Routines 和 JSON 进行 HTTP 调用和解析 JSON
【发布时间】:2018-12-15 13:29:44
【问题描述】:

我对 golang 比较陌生,我想创建一种方法来同时调用多个 URL,并解析 JSON 文档。但是,我真的不确定我是否正确使用了 goroutine 和 channels。在这一点上,我不确定我是否没有正确地“用 Go 思考”,或者我对 goroutine 和通道的理解/方法是否不准确。

另外,在解析时,我想从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 ??
    }
}

【问题讨论】:

    标签: json go channel goroutine


    【解决方案1】:

    修复

    这一行:

    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 SpecificationEffective Go 以及 the sync package documentation 的 Mutex 和 WaitGroup 部分。

    可运行演示

    这里有一个完整的、可运行的例子来演示这个原理:

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

    【讨论】:

      猜你喜欢
      • 2018-01-27
      • 2014-01-27
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      • 2018-09-01
      相关资源
      最近更新 更多