How to notify when HTTP server starts successfully

Viewed 3215

I'm trying to start an HTTP server in Go, and when the server is started, a message should be printed, in case of an error, an error message should be printed.

Given the following code:

const (
    HTTPServerPort = ":4000"
)

func main() {
    var httpServerError = make(chan error)
    var waitGroup sync.WaitGroup

    setupHTTPHandlers()

    waitGroup.Add(1)
    go func() {
        defer waitGroup.Done()

        httpServerError <- http.ListenAndServe(HTTPServerPort, nil)
    }()

    if <-httpServerError != nil {
        fmt.Println("The Logging API service could not be started.", <-httpServerError)
    } else {
        fmt.Println("Logging API Service running @ http://localhost" + HTTPServerPort)
    }

    waitGroup.Wait()
}

When I do start the application, I don't see anything printed to the console, where I would like to see:

Logging API Service running @ http://localhost:4000

When I change the port to an invalid one, the following output is printed to the console:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]: main.main() ...app.go:45 +0x107 exit status 2

Could anyone point me in the right direction so that I know what I'm doing wrong with this implementation?

2 Answers

You can't do this unless you change the logic in your code or use Listen and Serve separately. Because ListenAndServe is a blocking function. If there something unexpected happens, it will return you an error. Provided it is not, it will keep blocking running the server. There is neither an event that is triggered whenever a server is started.

Let's run Listen and Serve separately then.

l, err := net.Listen("tcp", ":8080")
if err != nil {
    // handle error
}

// Signal that server is open for business. 

if err := http.Serve(l, rootHandler); err != nil {
    // handle error
}

See https://stackoverflow.com/a/44598343/4792552.

P.S. net.Listen doesn't block because it runs in background. In other means, it simply spawns a socket connection in OS level and returns you with the details/ID of it. Thus, you use that ID to proxy orders to that socket.

The issue is that your if statement will always read from the httpServerError channel. However the only time something writes to that is if the server fails.

Try a select statement:

select{
case err := <-httpServerError
    fmt.Println("The Logging API service could not be started.", err)
default:
    fmt.Println("Logging API Service running @ http://localhost" + HTTPServerPort
}

The default case will be ran if the channel does not have anything on it.

Notice this does not read from the channel twice like your example. Once you read a value from a channel, its gone. Think of it as a queue.

Related