How to wait for HTTP server to start?

3,296 views
Skip to first unread message

cpu...@gmail.com

unread,
Mar 27, 2021, 10:13:40 AM3/27/21
to golang-nuts
The typical Go tutorials pattern for starting a server is something like 

    log.Fatal(http.ListenAndServe(":8080"))

But what if the application needs to do other things after the server is started? It seems there is virtually no method to wait for the server actually start listening to requests?

I'm probably missing something and would appreciate a hint.

Thanks,
Andi

Axel Wagner

unread,
Mar 27, 2021, 10:20:25 AM3/27/21
to cpu...@gmail.com, golang-nuts
The best way to do it is probably by making an HTTP request and see if it succeeds. In production, it's always a good idea to have a health check endpoint anyways. So some service manager can check if it's alive and restart it if necessary. Or so that a load balancer doesn't send traffic until the service is healthy.
The advantage is that you can determine yourself what you consider "healthy". That is, if you want to warm up some cache or connect to some backend, you can wait for that before the health-check endpoint returns true.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/a40222e3-e4b3-4996-8232-045fcff43b77n%40googlegroups.com.

Jérôme LAFORGE

unread,
Mar 27, 2021, 1:53:47 PM3/27/21
to golang-nuts
When I want to ensure that the HTTP server is started (or if I want additional stuff), I do this :

Message has been deleted

Jesper Louis Andersen

unread,
Mar 29, 2021, 12:35:31 PM3/29/21
to Axel Wagner, cpu...@gmail.com, golang-nuts
On Sat, Mar 27, 2021 at 3:19 PM 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:
The best way to do it is probably by making an HTTP request and see if it succeeds. In production, it's always a good idea to have a health check endpoint anyways. So some service manager can check if it's alive and restart it if necessary. Or so that a load balancer doesn't send traffic until the service is healthy.

In addition, if a server relies on other parts/systems to work, you can end up in a situation where those parts fail, but the server doesn't. This means it is hard to have a "barrier" in the program after which something is safe to start, since that assumption might fail later on when the program is running. It's yet another argument why Alex' suggestion of having a health check is beneficial. You can fail and report why you don't provide service right now (I need access to a database, but it's timing out, etc...). Systems which can analyze their own state and report back on known errors are far easier to maintain.

roger peppe

unread,
Mar 29, 2021, 3:05:50 PM3/29/21
to cpu...@gmail.com, golang-nuts
I often call net.Listen directly before calling Serve in a goroutine. That way you can connect to the server's socket immediately even though the server might take a while to get around to serving the request.

Look at how net/http/httptest does it.

cpu...@gmail.com

unread,
Mar 30, 2021, 6:50:07 AM3/30/21
to golang-nuts
On Monday, March 29, 2021 at 9:05:50 PM UTC+2 rog wrote:
I often call net.Listen directly before calling Serve in a goroutine. That way you can connect to the server's socket immediately even though the server might take a while to get around to serving the request.
 
It seems this would work as long as the server is not using HTTPS and needs to perform a handshake first?

Look at how net/http/httptest does it.

I wasn't sure if you're referring to goServe https://github.com/golang/go/blob/master/src/net/http/httptest/server.go#L304 which is called from Start()? This seems to return only after the server is stopped.
I've found the idea interesting though. Using a WaitGroup you could at least ensure that the gofunc containing the call to Serve() has at least be started. That still doesn't seem to be sufficient though:

ln, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatal(err)
}

var wg sync.WaitGroup
wg.Add(1)

go func() {
server := &http.Server{Addr: ":" + port, Handler: handler}
wg.Done()
log.Fatal(server.Serve(ln))
}()

wg.Wait()

Cheers,
Andi

Brian Candler

unread,
Mar 30, 2021, 7:37:18 AM3/30/21
to golang-nuts
On Tuesday, 30 March 2021 at 11:50:07 UTC+1 cpu...@gmail.com wrote:
On Monday, March 29, 2021 at 9:05:50 PM UTC+2 rog wrote:
I often call net.Listen directly before calling Serve in a goroutine. That way you can connect to the server's socket immediately even though the server might take a while to get around to serving the request.
 
It seems this would work as long as the server is not using HTTPS and needs to perform a handshake first?

It makes no difference.  The sequence is Listen -> Accept -> start communicating.  From the point of view of the socket, the communication is just application data; it may or may not contain a TLS handshake.

As long as the socket is in listening state, it will start to queue up incoming connections, up to a kernel limit.
Reply all
Reply to author
Forward
0 new messages