Go中的有限并发连接
我在Go中有以下基本http服务器。对于每个传入的请求,它都会发出5个传出http请求。每一次大约需要3-5秒。我无法在8G内存的四核机器上实现超过200个请求/秒Go中的有限并发连接,go,Go,我在Go中有以下基本http服务器。对于每个传入的请求,它都会发出5个传出http请求。每一次大约需要3-5秒。我无法在8G内存的四核机器上实现超过200个请求/秒 package main import ( "flag" "fmt" "net/http" _"net/url" //"io/ioutil" "time" "log" "sync" //"os" "io/ioutil" ) // Job holds
package main
import (
"flag"
"fmt"
"net/http"
_"net/url"
//"io/ioutil"
"time"
"log"
"sync"
//"os"
"io/ioutil"
)
// Job holds the attributes needed to perform unit of work.
type Job struct {
Name string
Delay time.Duration
}
func requestHandler(w http.ResponseWriter, r *http.Request) {
// Make sure we can only be called with an HTTP POST request.
fmt.Println("in request handler")
if r.Method != "POST" {
w.Header().Set("Allow", "POST")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// Set name and validate value.
name := r.FormValue("name")
if name == "" {
http.Error(w, "You must specify a name.", http.StatusBadRequest)
return
}
delay := time.Second * 0
// Create Job and push the work onto the jobQueue.
job := Job{Name: name, Delay: delay}
//jobQueue <- job
fmt.Println("creating worker")
result := naiveWorker(name, job)
fmt.Fprintf(w, "your task %s has been completed ,here are the results : %s", job.Name, result)
}
func naiveWorker(id string, job Job) string {
var wg sync.WaitGroup
responseCounter := 0;
totalBodies := "";
fmt.Printf("worker%s: started %s\n", id, job.Name)
var urls = []string{
"https://someurl1",
"https://someurl2",
"https://someurl3",
"https://someurl4",
"https://someurl5",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Fetch the URL.
resp, err := http.Get(url)
if err != nil {
fmt.Printf("got an error")
// panic(err)
} else {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
totalBodies += string(body)
}
}
responseCounter ++
// Decrement the counter when the goroutine completes.
defer wg.Done()
}(url)
}
wg.Wait()
fmt.Printf("worker%s: completed %s with %d calls\n", id, job.Name, responseCounter)
return totalBodies
}
func main() {
var (
port = flag.String("port", "8181", "The server port")
)
flag.Parse()
// Start the HTTP handler.
http.HandleFunc("/work", func(w http.ResponseWriter, r *http.Request) {
requestHandler(w, r)
})
log.Fatal(http.ListenAndServe(":" + *port, nil))
}
我有以下问题:
当并发线程数超过1000时,http连接将被重置。这是可接受的/预期的行为吗
如果我写go-requestHandlerw,r而不是requestHandlerw,r,我得到
http:multiple response.WriteHeader调用
问题2:多个response.WriteHeader调用:
如果你不设置你的标题,去为你做。当您启动go例程时,服务器会看到还没有设置头,然后会自动设置,但之后您的go例程会再次设置头
Q1:当并发线程数超过1000时,http连接将重置:
Go例程不是系统线程,这意味着您可以运行比系统通常可以运行的线程更多的例程。在最坏的情况下,您的请求将并发运行,而不是并行运行。我没有看到您的代码中有任何错误,这让我觉得您发出请求的服务器限制了您并删除了您的请求,因为您可能超过了服务器允许的一个ip的最大连接数
您还可以修改请求中的http.Transport参数,看看这是否有助于解决内存消耗和并发连接的问题
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
问题2:多个response.WriteHeader调用:
如果你不设置你的标题,去为你做。当您启动go例程时,服务器会看到还没有设置头,然后会自动设置,但之后您的go例程会再次设置头
Q1:当并发线程数超过1000时,http连接将重置:
Go例程不是系统线程,这意味着您可以运行比系统通常可以运行的线程更多的例程。在最坏的情况下,您的请求将并发运行,而不是并行运行。我没有看到您的代码中有任何错误,这让我觉得您发出请求的服务器限制了您并删除了您的请求,因为您可能超过了服务器允许的一个ip的最大连接数
您还可以修改请求中的http.Transport参数,看看这是否有助于解决内存消耗和并发连接的问题
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
http处理程序应同步运行,因为处理程序函数的返回表示请求结束。在处理程序返回后访问http.Request和http.ResponseWriter是无效的,因此没有理由在goroutine中调度处理程序 正如注释所指出的,您不能打开比进程ulimit允许的更多的文件描述符。除了适当增加ulimit之外,您还应该限制可以一次调度的并发请求的数量 如果要与同一主机建立多个连接,还应该相应地调整http.Transport。每个主机的默认空闲连接只有2个,因此,如果需要到该主机的2个以上并发连接,则不会重用新连接。看 如果您连接到许多不同的主机,设置Transport.idlecontimeout是一个摆脱未使用的连接的好主意
和往常一样,在长时间运行的服务上,您需要确保为所有内容设置超时,以便慢速或断开的连接不会占用不必要的资源。http处理程序应同步运行,因为处理程序函数的返回表示请求的结束。在处理程序返回后访问http.Request和http.ResponseWriter是无效的,因此没有理由在goroutine中调度处理程序 正如注释所指出的,您不能打开比进程ulimit允许的更多的文件描述符。除了适当增加ulimit之外,您还应该限制可以一次调度的并发请求的数量 如果要与同一主机建立多个连接,还应该相应地调整http.Transport。每个主机的默认空闲连接只有2个,因此,如果需要到该主机的2个以上并发连接,则不会重用新连接。看 如果您连接到许多不同的主机,设置Transport.idlecontimeout是一个摆脱未使用的连接的好主意
和往常一样,在长时间运行的服务上,您需要确保为所有内容设置超时,以便慢速或中断的连接不会占用不必要的资源。检查ulimit、maxfiles和somaxconn。可能系统资源不足。我同意Eugene的观点:1024是打开文件数量的典型限制,在UNIX上,它还包括基于Linux的典型商品操作系统上的套接字。这只是一个建议。。您的用例实际上可以使用没有问题的通道,而不是等待组。这也可以防止任何伤害
由于竞态条件,您可能会得到一个异常/格式错误的字符串作为输出。此外,为了回答您的问题,处理程序已经在goroutine中处理,因此如果您使用Linux,将其放在另一个goroutine中也不会有帮助,方法是提高/etc/security/limits.conf中nofile参数的所谓硬限制,即为用户和/或一组用户(包括用于运行服务器的用户)设置该参数。然后,在启动服务器之前,在shell中发出ulimit-n hard命令,以使当前的软限制具有合理的默认值,通常为1024,正如前面讨论的硬限制一样。aftwerwards生成的服务器将继承并使用此设置。调用ulimit-n或ulimit-a查看当前设置。@biosckon测试了多达10K个并发请求,效果良好。令人惊讶的是,使用的内存只有约120MB。然而,CPU始终保持在50-60%之间。请检查ulimit、maxfiles和somaxconn。可能系统资源不足。我同意Eugene的观点:1024是打开文件数量的典型限制,在UNIX上,它还包括基于Linux的典型商品操作系统上的套接字。这只是一个建议。。您的用例实际上可以使用没有问题的通道,而不是等待组。这还可以防止由于竞争条件而导致输出的字符串出现任何损坏/格式错误。此外,为了回答您的问题,处理程序已经在goroutine中处理,因此如果您使用Linux,将其放在另一个goroutine中也不会有帮助,方法是提高/etc/security/limits.conf中nofile参数的所谓硬限制,即为用户和/或一组用户(包括用于运行服务器的用户)设置该参数。然后,在启动服务器之前,在shell中发出ulimit-n hard命令,以使当前的软限制具有合理的默认值,通常为1024,正如前面讨论的硬限制一样。aftwerwards生成的服务器将继承并使用此设置。调用ulimit-n或ulimit-a查看当前设置。@biosckon测试了多达10K个并发请求,效果良好。令人惊讶的是,使用的内存只有约120MB。然而,CPU始终保持在50-60%。