func xHandle(w http.ResponseWriter, r *http.Request) {
fmt.Printf("new request: %p\n", r)
w.Write([]byte("Hello\n"))
for {}
}
func main() {
http.HandleFunc("/", xHandle)
http.ListenAndServe(":8080", nil)
}
对于每一个http请求,http服务都会启动一个go routine 来调用xHandle函数。目前搜到的资料都是在xHandle内部做并发数控制,就是说等http服务启go routine后,发现超限了再直接退出。
我想问的是如何从源头上控制http服务的go routine数量,也是当go routine到达上限后,都不用启动go routine回调xHandle,直接在底层响应500给客户端?
<a href="/user/GGXXLL" title="@GGXXLL">@GGXXLL</a> 感谢指点,我没有描述清我的问题。我看了下源码,我的需求该是无解的。我想要的效果在资源超限时阻止创建go routine,也就是下面代码最后一行不要执行:`go c.serve(connCtx)`。
现在看来就是来了一个请求,只能先执行这行代码创建go routine,进入limit-handler,然后再退出了。
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
baseCtx := context.Background()
if srv.BaseContext != nil {
baseCtx = srv.BaseContext(origListener)
if baseCtx == nil {
panic("BaseContext returned a nil context")
}
}
var tempDelay time.Duration // how long to sleep on accept failure
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
if err != nil {
if srv.shuttingDown() {
return ErrServerClosed
}
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
#3
更多评论
```
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"time"
"golang.org/x/time/rate"
)
type LimiterOption struct {
lm *rate.Limiter
}
func WithLimiter(duration time.Duration, count int) func(o *LimiterOption) {
return func(o *LimiterOption) {
o.lm = rate.NewLimiter(rate.Every(duration), count)
}
}
// LimiterWrap 每个 handler 单独限制
func LimiterWrap(f http.HandlerFunc, opts ...func(o *LimiterOption)) http.HandlerFunc {
o := &LimiterOption{
lm: rate.NewLimiter(rate.Every(100*time.Millisecond), 10),
}
for _, opt := range opts {
opt(o)
}
return func(w http.ResponseWriter, r *http.Request) {
if !o.lm.Allow() {
w.WriteHeader(http.StatusInternalServerError)
return
}
f(w, r)
}
}
func xHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello\n"))
}
func main() {
// 单独控制一个接口
http.HandleFunc("/", LimiterWrap(xHandle, WithLimiter(100*time.Millisecond, 1)))
// 所有接口都走同一个限流
// http.ListenAndServe(":8080", LimiterWrap(http.DefaultServeMux.ServeHTTP))
for i := 0; i < 10; i++ {
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/", nil)
http.DefaultServeMux.ServeHTTP(w, req)
fmt.Println(i, w.Code)
time.Sleep(time.Millisecond * 20)
}
//output
//0 200
//1 500
//2 500
//3 500
//4 500
//5 200
//6 500
//7 500
//8 500
//9 500
}
```
#1
```go
package main
func success() {}
func fail() {}
func main() {
c := make(chan struct{}, 16)
select {
case c <- struct{}{}:
success()
<-c
default:
fail()
}
}
```
#2