如何限制Go API的并发连接

i am starting up a Go API with listenandserve to accept HTTP requests.

How can I achieve the below goal?

  1. Allow maximum 100 simultaneous HTTP requests
  2. The 101st request (and any others) should wait for 10mins to attempt to fall into this '100 simultaneous' limit (ie hopefully some of the first 100 requests should finish)
  3. If 10mins pass and no available request 'slots' have opened up then return error for that request that has been waiting
  4. Order of which request 101...102...x that gets run next is unimportant

current version is completely un-go:

    timeout := time.After(10 * time.Minute)
    tick := time.Tick(15 * time.Second)
    fullcmdfirst := fmt.Sprintf("netstat -anp | grep procname | grep ESTABLISHED | grep -v grep | wc -l")
    outputfirst, err1first := exec.Command("/bin/sh", "-c", fullcmdfirst).CombinedOutput()
    if strconv.ParseFloat(string(outputfirst)) < 100 {
        return nil
    }

    // Keep trying until we're timed out or lock acquired
    for {
        select {
        // Got a timeout! fail with a timeout error
        case <-timeout:
            return errors.New("Error: timed out ")
        // Got a tick, we should check if we can acquire
        case <-tick:
            fullcmd := fmt.Sprintf("netstat -anp | grep procname | grep ESTABLISHED | grep -v grep | wc -l")
            output, err1 := exec.Command("/bin/sh", "-c", fullcmd).CombinedOutput()
            if strconv.ParseFloat(string(outputfirst)) < 100 {
                l.Printf("start req")
                return nil
            }
        }
    }

There's no need for the netstats or the tickers or any of that (it won't work anyway - as soon as netstat sees <100 connections nothing is stopping the next 100 request from all noticing and you ending up running 199 requests at once; plus, a request that's waiting to be handled is still going to turn up in netstat - throttling connections is a different problem altogether). Just use a buffered channel as a semaphore; it's already thread-safe.

sem := make(chan struct{}, 100)

func myHandler(w http.ResponseWriter, r *http.Request) {
    timeout := time.After(10 * time.Minute)
    select {
        case <- timeout:
            http.Error(w, "Sorry", http.StatusUnavailable)
            return
        case sem <- struct{}:
            w.Write([]byte("Hello"))
            <- sem
            return
    }
}

Note though that most clients will have already timed out long before the 10 minute mark.