繁体   English   中英

将值发送到 Channel 并在就绪时读取 output

[英]Sending value into Channel and Reading output when Ready

我正在尝试使用 Golang 中的两个通道构建接收方和发送方模式。 我正在执行一项任务(API 调用),并收到一个Response结构。 我的目标是,当收到响应时,我想将其发送到另一个通道 ( writeChan ) 以进行额外处理。

我想持续读取/收听该接收通道 ( respChan ) 并处理通过的任何内容(例如Response )。 然后我想启动一个线程到 go 并在另一个 goroutine 中对该 Response 做进一步的操作。

我想了解如何将此模式链接在一起以允许数据从 API 调用流出并同时写入它(每个响应将写入Write()函数处理的单独文件目标。

基本上我目前的模式如下:

package main

import (
    "fmt"
    "sync"
)

func main() {

    var wg sync.WaitGroup
    respChan := make(chan Response) // Response is a struct that contains API response metadata
    defer close(respChan)
    // requests is just a slice of requests to be made to an API
    // This part is working well
    for _, req := range requests {
        wg.Add(1)
        go func(r Request) {
            defer wg.Done()
            resp, _ := r.Get() // Make the API call and receive back a Response struct
            respChan <- resp // Put the response into our channel
        }(req)
    }

    // Now, I want to extract the responses as they become available and send them to another function to do some processing. This I am unsure of how to handle properly
    writeChan := make(chan string)
    defer close(writeChan)
    select {
        case resp := <-respChan: // receive from response channel
            go func(response Response) {
                signal, _ := Write(response) // Separate func to write the response to a file. Not important here in this context.
                writeChan <- signal // Put the signal data into the channel which is a string file path of where the file was written (will be used for a later process)

            }(resp)
        case <-time.After(15 *time.Second):
            fmt.Println("15 seconds have passed without receiving anything...")

    }
    wg.Wait()
}

让我与您分享一个您可以从中受益的工作示例。 首先,我将展示代码,然后,我将引导您完成所有相关部分。

package main

import (
    "fmt"
    "net/http"
    "os"
    "strings"
    "time"
)

type Request struct {
    Url            string
    DelayInSeconds time.Duration
}

type Response struct {
    Url        string
    StatusCode int
}

func main() {
    requests := []Request{
        {"https://www.google.com", 0},
        {"https://stackoverflow.com", 1},
        {"https://www.wikipedia.com", 4},
    }

    respChan := make(chan Response)
    defer close(respChan)

    for _, req := range requests {
        go func(r Request) {
            fmt.Printf("%q - %v\n", r.Url, strings.Repeat("#", 30))
            // simulate heavy work
            time.Sleep(time.Second * r.DelayInSeconds)
            resp, _ := http.Get(r.Url)
            res := Response{r.Url, resp.StatusCode}
            fmt.Println(time.Now())
            respChan <- res
        }(req)
    }

    writeChan := make(chan struct{})
    defer close(writeChan)

    for i := 0; i < len(requests); i++ {
        select {
        case res := <-respChan:
            go func(r Response) {
                f, err := os.Create(fmt.Sprintf("%v.txt", strings.Replace(r.Url, "https://", "", 1)))
                if err != nil {
                    panic(err)
                }
                defer f.Close()
                f.Write([]byte(fmt.Sprintf("%q OK with %d\n", r.Url, r.StatusCode)))
                writeChan <- struct{}{}
            }(res)
        case <-time.After(time.Second * 2):
            fmt.Println("Timeout")
        }
    }
}

设置

首先,我定义了将在示例中使用的两个结构: RequestResponse 在前者中,我放了一个DelayInSeconds来模拟一些重负载和耗时的操作。 然后,我定义了包含所有必须完成的请求的requests变量。

写作部分

在这里,我检查了requests变量。 对于每个请求,我将向目标 URL 发出 HTTP 请求time.Sleep模拟重负载。 然后,我将响应写入无缓冲的respChan通道。

阅读部分

在这里,主要的变化是将select构造包装到一个for循环中。 多亏了这一点,我们将确保迭代正确的时间(基于requests变量的长度)。

最后的笔记

首先,请记住代码过于简单只是为了展示相关部分。 因此,缺少很多错误处理,一些内联函数可以外推到命名函数中。 你不需要使用sync.WaitGroup来实现你需要的,使用通道就足够了。
随意玩延迟并检查写入了哪些文件!

让我知道这是否对您有帮助!

编辑

根据要求,我将根据您的需求为您提供更准确的解决方案。 新的阅读部分将类似于以下内容:

count := 0
for {
    // this check is need to exit the for loop and not wait indefinitely
    // it can be removed based on your needs
    if count == 3 {
        fmt.Println("all responses arrived...")
        return
    }
    res := <-respChan
    count++
    go func(r Response) {
        f, err := os.Create(fmt.Sprintf("%v.txt", strings.Replace(r.Url, "https://", "", 1)))
        if err != nil {
            panic(err)
        }
        defer f.Close()
        f.Write([]byte(fmt.Sprintf("%q OK with %d\n", r.Url, r.StatusCode)))
        writeChan <- struct{}{}
    }(res)
}

在这里,执行在for循环中无限期地等待。 无论每个请求需要多长时间才能完成,它都会在到达时立即获取。 我在for循环的顶部放置了一个if语句,以便在它处理完我们需要的请求后退出。 但是,您可以避免它并让代码运行直到出现取消信号(由您决定)。

让我知道这是否更符合您的要求,谢谢!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM