Configure / configure HTTP server
The type that implements the HTTP server is http.Server . If you do not create http.Server yourself, for example. because you call the http.ListenAndServe() function, which creates http.Server for you under the hood:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
So, if you want to tweak / configure the HTTP server, then create it yourself and call it Server.ListenAndServe() . http.Server is a structure; its null value is a valid configuration. Look at his document, what fields he has, and what you can customize / customize.
The "process control" of the HTTP server is documented on Server.Serve() :
Serve accepts incoming connections in Listener l , creating a new goroutine service for each . The goroutines services read requests and then call srv.Handler to respond to them. The service always returns a non-zero error.
Thus, each incoming HTTP request is processed in its new goroutine, that is, they are served simultaneously. Unfortunately, the API does not document any way to transition and change how this works.
And looking at the current implementation (Go 1.6.2), there is also no undocumented way to do this. server.go , currently line # 2107-2139 :
2107 func (srv *Server) Serve(l net.Listener) error { 2108 defer l.Close() 2109 if fn := testHookServerServe; fn != nil { 2110 fn(srv, l) 2111 } 2112 var tempDelay time.Duration // how long to sleep on accept failure 2113 if err := srv.setupHTTP2(); err != nil { 2114 return err 2115 } 2116 for { 2117 rw, e := l.Accept() 2118 if e != nil { 2119 if ne, ok := e.(net.Error); ok && ne.Temporary() { 2120 if tempDelay == 0 { 2121 tempDelay = 5 * time.Millisecond 2122 } else { 2123 tempDelay *= 2 2124 } 2125 if max := 1 * time.Second; tempDelay > max { 2126 tempDelay = max 2127 } 2128 srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) 2129 time.Sleep(tempDelay) 2130 continue 2131 } 2132 return e 2133 } 2134 tempDelay = 0 2135 c := srv.newConn(rw) 2136 c.setState(c.rwc, StateNew) // before Serve can return 2137 go c.serve() 2138 } 2139 }
As you can see on line # 2137, the connection is done unconditionally on the new goroutine, so there is nothing you can do about it.
Limit "working" goroutines
If you want to limit the number of requests serving goroutines, you can still do it.
You can limit them to several levels. To limit listener levels, see Darigaaz Response. To limit the level of the handler, read on.
For example, you can insert code in each of the http.Handler or handler functions ( http.HandlerFunc ), which continues only if the number of simultaneous requests serving goroutines is less than the specified limit.
There are many constructions for such restriction-synchronization code. One example would be: creating a buffered channel with the required bandwidth. Each handler must first send a value on this channel, and then do the work. When the handler returns, it should get the value from the channel: so this is best done in a deferred function (so as not to forget to “clear” itself).
If the buffer is full, a new request trying to send over the channel will block: wait for the request to complete.
Note that you don’t need to enter this limit code to all your handlers, you can use the “middleware” template, a new type of handler that wraps your handlers, whether it does this job with a synchronization constraint and invokes a wrapped handler in the middle of it.
The advantage of the restriction in the handler (as opposed to the restriction in Listeners) is that in the handler we know what the handler does, so we can make a selective restriction (for example, we can limit some queries, such as database operations, and not restrict others, for example, serve static resources), or we can create several separate groups of restrictions arbitrarily for our needs (for example, limit concurrent db requests to 10 max, limit static requests to 100 max, restrictions heavy computational queries up to 3 max), etc. We can also easily implement restrictions, such as unlimited (or high limit) for registered users / users and low limit for anonymous / insolvent users.
Also note that you can even limit the speed in one place without using middlewares. Create a "main handler" and go to http.ListenAndServe() (or Server.ListenAndServe() ). This main handler executes a speed limit (for example, using a buffer channel, as mentioned above), and simply forward the http.ServeMux call http.ServeMux you're using.
Here is a simple example that uses http.ListenAndServe() and the default http packet multiplexer ( http.DefaultServeMux ) for demonstration. It limits concurrent requests 2:
func fooHandler(w http.ResponseWriter, r *http.Request) { log.Println("Foo called...") time.Sleep(3 * time.Second) w.Write([]byte("I'm Foo")) log.Println("Foo ended.") } func barHandler(w http.ResponseWriter, r *http.Request) { log.Println("Bar called...") time.Sleep(3 * time.Second) w.Write([]byte("I'm Bar")) log.Println("Bar ended.") } var ch = make(chan struct{}, 2) // 2 concurrent requests func mainHandler(w http.ResponseWriter, r *http.Request) { ch <- struct{}{} defer func() { <-ch }() http.DefaultServeMux.ServeHTTP(w, r) } func main() { http.HandleFunc("/foo", fooHandler) http.HandleFunc("/bar", barHandler) panic(http.ListenAndServe(":8080", http.HandlerFunc(mainHandler))) }
Deployment
Web applications written in Go do not require external servers to control processes, since the Go web server itself processes requests at the same time.
Thus, you can start your web server, written in the section "Go as is": the Go web server is being prepared for release.
Of course, you can use other servers to perform additional tasks (for example, HTTPS processing, authentication / authorization, routing, load balancing between several servers).