在现代化的Web应用中,网络请求是至关重要的部分。通过网络请求,我们可以轻松地获取和发送数据。但是,随着应用规模的不断扩大,请求的数量也会增加。在这种情况下,如何保证系统的稳定性和效率就变得尤为重要。

Go语言是一种高效的并发编程语言,具有良好的内存管理和并发控制,因此在处理高并发请求时非常出色。本文介绍如何使用Go语言处理并发请求的方法。

  1. 并发处理请求

一般而言,网络请求由三个步骤组成:建立连接、发送请求和接收响应。在传统的应用程序中,每个请求都会依次执行这三个步骤。但是,在高并发应用中,这种方式效率很低,因为每个请求都需要等待前一个请求完成后才能开始执行。

这里介绍一种不同的方法。我们可以使用Go语言的并发特性,同时执行多个请求,使得应用程序可以同时处理多个请求。

下面是一个简单的示例代码:

func main() {
    urls := []string{
        "http://example.com",
        "http://example.net",
        "http://example.org",
    }

    ch := make(chan string)

    for _, url := range urls {
        go fetch(url, ch)
    }

    for range urls {
        fmt.Println(<-ch)
    }
}

func fetch(url string, ch chan<- string) {
    resp, err := http.Get(url)
    if err != nil {
        ch <- fmt.Sprint(err)
        return
    }
    defer resp.Body.Close()

    text, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        ch <- fmt.Sprint(err)
        return
    }

    ch <- fmt.Sprintf("url:%s, body:%s", url, text[:100])
}
gofor range
  1. 控制并发量

在上面的示例中,我们使用了goroutine来并发处理多个请求。但是这样做可能导致系统被过多的请求阻塞而崩溃。为了避免这种情况,我们需要控制并发量。

syncWaitGroup
func main() {
    urls := []string{
        "http://example.com",
        "http://example.net",
        "http://example.org",
    }

    var wg sync.WaitGroup

    for _, url := range urls {
        wg.Add(1)
        go func(url string) {
            defer wg.Done()

            resp, err := http.Get(url)
            if err != nil {
                fmt.Println(err)
                return
            }
            defer resp.Body.Close()

            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                fmt.Println(err)
                return
            }

            fmt.Printf("url:%s, body:%s", url, body[:20])
        }(url)
    }

    wg.Wait()
}
WaitGroupAddWait
  1. 并发地处理请求结果

在处理多个请求时,我们不仅需要控制并发量,还需要处理并发结果。一般而言,我们需要把所有请求的结果收集到一个数组或其他数据结构中,并在所有请求完成后进行处理。

syncMutexMutex

下面是一个示例代码:

type Result struct {
    url string
    body []byte
    err error
}

func fetch(url string, ch chan<- Result) {
    resp, err := http.Get(url)
    if err != nil {
        ch <- Result{url: url, err: err}
        return
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        ch <- Result{url: url, err: err}
        return
    }

    ch <- Result{url: url, body: body}
}
func main() {
    urls := []string{
        "http://example.com",
        "http://example.net",
        "http://example.org",
    }

    var results []Result
    ch := make(chan Result)

    for _, url := range urls {
        go fetch(url, ch)
    }

    for range urls {
        results = append(results, <-ch)
    }

    for _, result := range results {
        if result.err != nil {
            fmt.Println(result.err)
            continue
        }
        fmt.Printf("url:%s, body:%s", result.url, result.body[:20])
    }
}
ResultMutex

总结

WaitGroupMutex