在后台使用goroutine的http并发延迟

时间:2016-08-31 12:45:04

标签: go concurrency

为什么主服务器goroutine处理传入请求会有这样的延迟?如何避免这种延迟?

无延迟

的简单代码
package main
import (
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/", root)
    http.ListenAndServe(":8090", nil)
}
//---------------------------------------------------------------------------
//  http handlers
//---------------------------------------------------------------------------
func root(w http.ResponseWriter, r *http.Request) {
    log.Printf("[root] `%v`\n", r.URL.Path)
    w.Write([]byte("What the hell"))
}

测试加载的结果

    ╰─➤  wrk -d20s -t5 -c100 http://localhost:8090
Running 20s test @ http://localhost:8090
  5 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    17.07ms   46.63ms 368.77ms   91.73%
    Req/Sec    10.99k     4.87k   24.48k    62.49%
  1038912 requests in 20.10s, 128.80MB read
Requests/sec:  51684.63
Transfer/sec:      6.41MB

添加goroutines

package main
import (
    "log"
    "net/http"
)
func main() {
    _ = NewTesterGo(100)

    http.HandleFunc("/", root)
    http.ListenAndServe(":8090", nil)
}
//---------------------------------------------------------------------------
//  http handlers
//---------------------------------------------------------------------------
func root(w http.ResponseWriter, r *http.Request) {
    log.Printf("[root] `%v`\n", r.URL.Path)
    w.Write([]byte("What the fuck"))
}

//---------------------------------------------------------------------------
//  tester segment
//---------------------------------------------------------------------------
type (
    TesterGo struct {
        Work chan string
    }
)

func NewTesterGo(count int) *TesterGo {
    t:=&TesterGo{
        Work:make(chan string,100),
    }
    for ; count > 0 ; count -- {
        go t.Worker()
    }
    return t

}
func (t *TesterGo) Worker() {
    log.Printf("[testergo][worker][work] стартовал....\n")
    for {
        select {
        case work := <-t.Work:
            log.Printf("[testerGo][Worker] %v\n", work)
        default:
        }
    }
}

加载结果

╰─➤  wrk -d20s -t5 -c100 http://localhost:8090
Running 20s test @ http://localhost:8090
  5 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   464.71ms  305.44ms   1.90s    77.90%
    Req/Sec    54.62     43.74   200.00     67.50%
  3672 requests in 20.05s, 466.17KB read
  Socket errors: connect 0, read 0, write 0, **timeout 97**
Requests/sec:    **183.11**
Transfer/sec:     23.25KB

1 个答案:

答案 0 :(得分:4)

你的goroutines使用default,如果频道中没有任何东西,它们会立即旋转(你的例子中没有任何内容)。这可能会使Go的调度程序更多地进行上下文切换,并且可能会消耗大量的CPU。

循环中有default的原因吗?如果没有尝试以下之一:

要么没有默认值,goroutines就会简单地“睡觉”#34;直到那里工作。

 for {
        select {
        case work := <-t.Work:
            log.Printf("[testerGo][Worker] %v\n", work)
        }
    }

这个BTW使选择完全冗余,所以只是摆脱它:

 for { //you can also use a range on the channel
     work := <- t.Work 
     log.Printf("[testerGo][Worker] %v\n", work)
 }

第二个选项 - 在继续循环之前使它们等待的超时:

 for {
        select {
        case work := <-t.Work:
            log.Printf("[testerGo][Worker] %v\n", work)

        case <- time.After(100*time.Millisecond): //or whatever you prefer

        }
    }