HTTP Chunked流式传输到WebSocket

时间:2016-02-11 21:17:36

标签: go

我想收听多个传输编码响应的HTTP流,然后逐行从中获取消息,然后将消息推送到一个通道。我想从通道读取并稍后通过websocket。

func subscribe(ws chan<- string, group string) (scanner *bufio.Scanner, err error){
    res, _ := req(STREAM_URL, channelTemplate(group))
    reader := bufio.NewScanner(res.Body)
    return reader, reader.Err()
}

func main() {
    ws := make(chan string)
    request, _ := http.NewRequest("GET", URL, nil)
    request.Header.Add("Content-Type", "application/json")
    client := &http.Client{}
    resp, _ := client.Do(request)
    ac := ACResponse{}
    json.NewDecoder(resp.Body).Decode(&ac)
    resp.Body.Close()
    var scanners = make([]*bufio.Scanner, 0)
    for _, group := range ac.Groups {
        fmt.Println("Started worker for", group)
        //listen to all stream URLs
        scanner, err := subscribe(ws, group)
        if err != nil {
            panic(err)
        }
        // keep track of Scanner to read later
        scanners = append(scanners, scanner)
    }
    for {
        select {
        case msg := <-ws:
            fmt.Println("[events] ", msg)
        default:
            randScanner := rand.Intn(len(ac.Groups)-1)
            fmt.Println("Reading from", randScanner)
            reader := scanners[randScanner]
            reader.Scan()
            if err := reader.Err(); err != nil {
                panic(err)
            }
            text := reader.Text()
            ws <- text
        }
    }
}

该程序在reader.Scan()处阻止。输出为Reading from 1,没有别的。我看着wireshark,消息传来。

如何使用Go更好地设计此问题?

1 个答案:

答案 0 :(得分:0)

发送到无缓冲通道ws的主要块。要解决此问题,请将ws更改为缓冲频道:

ws := make(chan string, 1)

第二个问题是main()在达到EOF后继续读取扫描仪。问题出在以下几个方面:

        reader.Scan()
        if err := reader.Err(); err != nil {
            panic(err)
        }
        text := reader.Text()

Scan()在EOF返回false,但忽略扫描返回。 Err()在EOF上返回nil。修改应用程序以使用Scan()的返回值。

另一个问题是读取任何一个扫描仪的主要块。为避免阻塞单个连接,请启动goroutine以读取每个连接:

func subscribe(wg *sync.WaitGroup, ws chan<- string, group string) {
    defer wg.Done()
    res, err := req(STREAM_URL, channelTemplate(group))
    if err ! nil {
         // handle error
    }
    defer resp.Body.Close()
    reader := bufio.NewScanner(res.Body)
    for reader.Scan() {
       ws <- reader.Text()
    }
    if err := reader.Err(); err != nil {
        // handle error
    }
}

func main() {
    ws := make(chan string)
    request, _ := http.NewRequest("GET", URL, nil)
    request.Header.Add("Content-Type", "application/json")
    resp, err := http.DefaultClient.Do(request)
    if err != nil {
         // handle error
    }
    var ac ACResponse
    if err := json.NewDecoder(resp.Body).Decode(&ac); err != nil {
       // handle error
    }
    resp.Body.Close()
    var wg sync.WaitGroup
    for _, group := range ac.Groups {
         wg.Add(1)
         go subscribe(&wg, ws, group)
    }

    go func() {
       wg.Wait()
       close(ws)
    }()

    for msg := range ws {
        fmt.Println("[events] ", msg)
    }
}

以上代码未经编译且未经测试。我已经标记了需要进行错误处理的位置。所有连接到达EOF后,我编写代码退出main。这可能是你想要的,也可能不是你想要的。