【发布时间】:2017-11-20 22:49:59
【问题描述】:
我在 Go 中有以下基本的 http 服务器。对于每个传入请求,它都会发布 5 个传出 http 请求。他们每个人大约需要3-5秒。我无法在 8 gig Ram 四核机器上实现超过 200 个请求/秒。
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 requestHandler(w,r) 而不是 requestHandler(w,r) 我得到 http:多个响应。WriteHeader 调用
【问题讨论】:
-
检查 ulimit、maxfiles 和 somaxconn。可能是系统资源不足。
-
我同意 Eugene:1024 是典型的基于 Linux 的商品操作系统上打开文件(在 UNIX 上还包括套接字)数量的典型限制。
-
只是一个建议.. 您的用例实际上可以毫无问题地使用通道而不是等待组。这也将防止您由于竞争条件而获得的任何损坏/格式错误的字符串作为输出。此外,处理程序已经在 goroutine 中处理,以回答您的问题,因此将其放在另一个中无济于事
-
如果您使用的是 Linux,方法是提高
/etc/security/limits.conf中nofile参数的所谓“硬”限制 — 为用户和/或组设置它的用户,其中包括用于运行您的服务器的用户。然后,在启动服务器之前,在 shell 中发出ulimit -n hard— 将当前的“软”限制(将有一个合理的默认值,通常为 1024,如前所述)提升到“硬”限制。之后生成的服务器将继承此设置并使用它。致电ulimit -n或ulimit -a以查看当前设置。 -
@biosckon 测试了多达 10K 个并发请求,它运行良好。令人惊讶的是,使用的内存仅为~120 MB。但是 CPU 一直保持在 50-60 %。
标签: go