gpt4 book ai didi

concurrency - 为什么这个并发 HTTP 客户端会随机崩溃?

转载 作者:IT王子 更新时间:2023-10-29 02:34:39 24 4
gpt4 key购买 nike

我正在通过编写 HTTP 测试客户端(如 Apache 的 ab)来学习 Go。下面的代码看起来非常简单:我创建了一个可配置数量的 goroutine,每个 goroutine 发送一部分 HTTP 请求并记录结果。我遍历 resultChan channel 并检查/记录每个结果。当消息数量为 100 条时,这会起作用。但是,当我增加消息数量时,它会挂起并且 htop 显示该进程的 VIRT 为 138G。

这是有问题的代码:

package main

import "net/http"
import "fmt"
import "time"

const (
SUCCESS = iota
TOTAL = iota
TIMEOUT = iota
ERROR = iota
)

type Result struct {
successful int
total int
timeouts int
errors int
duration time.Duration
}

func makeRequests(url string, messages int, resultChan chan<- *http.Response) {
for i := 0; i < messages; i++ {
resp, _ := http.Get(url)
if resp != nil {
resultChan <- resp
}
}
}

func deployRequests(url string, threads int, messages int) *Result {
results := new (Result)
resultChan := make(chan *http.Response)
start := time.Now()
defer func() {
fmt.Printf("%s\n", time.Since(start))
}()
for i := 0; i < threads; i++ {
go makeRequests(url, (messages/threads) + 1, resultChan)
}

for response := range resultChan {
if response.StatusCode != 200 {
results.errors += 1
} else {
results.successful += 1
}
results.total += 1
if results.total == messages {
return results
}
}
return results
}

func main () {
results := deployRequests("http://www.google.com", 10, 1000)
fmt.Printf("Total: %d\n", results.total)
fmt.Printf("Successful: %d\n", results.successful)
fmt.Printf("Error: %d\n", results.errors)
fmt.Printf("Timeouts: %d\n", results.timeouts)
fmt.Printf("%s", results.duration)
}

显然有一些遗漏或愚蠢的事情(没有超时检查, channel 是同步的,等等)但我想在修复这些问题之前让基本情况正常工作。编写的程序导致如此多的内存分配的原因是什么?

据我所知,只有 10 个 goroutine。如果每个 HTTP 请求都创建一个,这是有道理的,那么如何执行会在循环中创建许多 goroutine 的操作?或者这个问题完全不相关。

最佳答案

我认为导致挂起的顺序是:

  1. http.Get in makeRequests 失败(连接被拒绝,请求超时等),返回一个nil 响应和一个错误值
  2. 错误被忽略,makeRequests 继续下一个请求
  3. 如果出现任何错误,makeRequestsresultChan 发送少于预期数量的结果
  4. deployRequests 中的 for .. range .. chan 循环永远不会中断,因为 results.total 总是小于 messages

一种解决方法是:

如果 http.Get 返回错误值,将 nil 响应发送到 resultChan:

    resp, err := http.Get(url)
if err != nil {
resultChan <- nil
} else if resp != nil {
resultChan <- resp
}

deployRequests 中,如果 for 循环从 resultChan 中读取一个 nil 值,则将其计为错误:

for response := range resultChan {
if response == nil {
results.errors += 1
} else if response.StatusCode != 200 {

// ...

关于concurrency - 为什么这个并发 HTTP 客户端会随机崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22534113/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com