在Go中,并行执行客户端请求相对简单。 在下面示例中,我们将使用客户端使用Go缓冲通道执行多个URL请求。 响应和错误都将转到一个单独的通道,任何有权访问客户端的人都可以轻松访问。

在本节中,创建客户端,读取通道以及处理响应和错误都将在main.go文件中完成。

实践

建立 config.go:

package async

import "net/http"

// NewClient 建立一个新的client并为其分配通道
func NewClient(client *http.Client, bufferSize int) *Client {
    respch := make(chan *http.Response, bufferSize)
    errch := make(chan error, bufferSize)
    return &Client{
        Client: client,
        Resp:   respch,
        Err:    errch,
    }
}

type Client struct {
    *http.Client
    Resp chan *http.Response
    Err  chan error
}

// AsyncGet 执行Get然后将resp/error返回到适当的通道
func (c *Client) AsyncGet(url string) {
    resp, err := c.Get(url)
    if err != nil {
        c.Err <- err
        return
    }
    c.Resp <- resp
}

建立 exec.go:

package async

// FetchAll 遍历请求所有的url
func FetchAll(urls []string, c *Client) {
    for _, url := range urls {
        go c.AsyncGet(url)
    }
}

建立 main.go:

package main

import (
    "fmt"
    "net/http"

    "github.com/agtorre/go-cookbook/chapter6/async"
)

func main() {
    urls := []string{
        "https://www.google.com",
        "https://golang.org",
        "https://www.github.com",
    }
    c := async.NewClient(http.DefaultClient, len(urls))
    async.FetchAll(urls, c)

    for i := 0; i < len(urls); i++ {
        select {
        case resp := <-c.Resp:
            fmt.Printf("Status received for %s: %d\n", resp.Request.URL, resp.StatusCode)
        case err := <-c.Err:
            fmt.Printf("Error received: %s\n", err)
        }
    }
}

这会输出:

Status received for https://www.google.com: 200
Status received for https://golang.org: 200
Status received for https://github.com/: 200

说明

该示例创建了一个框架,用于使用单个客户端以扇出异步方式处理请求。它尝试尽可能快地检索尽可能多的URL。 在许多情况下,你可能希望通过类似工作池的方式进一步限制此操作。在特定存储或检索接口时这些异步Go例程也是有借鉴意义的。

这里还使用case语句处理多个通道。我们无需担心处理锁定问题,因为我们非常清楚将收到多少响应。如果放弃某些响应处理,那么另一种选择就是超时。

最后编辑: kuteng  文档更新时间: 2021-01-03 15:03   作者:kuteng