如何限制http.HandleFunc并发数量?

leenzhu · · 4266 次点击
``` 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
<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 = &amp;onceCloseListener{Listener: l} defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } if !srv.trackListener(&amp;l, true) { return ErrServerClosed } defer srv.trackListener(&amp;l, false) baseCtx := context.Background() if srv.BaseContext != nil { baseCtx = srv.BaseContext(origListener) if baseCtx == nil { panic(&#34;BaseContext returned a nil context&#34;) } } 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 &amp;&amp; ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay &gt; max { tempDelay = max } srv.logf(&#34;http: Accept error: %v; retrying in %v&#34;, err, tempDelay) time.Sleep(tempDelay) continue } return err } connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic(&#34;ConnContext returned nil&#34;) } } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew, runHooks) // before Serve can return go c.serve(connCtx) } }
#3