Home >Backend Development >Golang >How to Gracefully Shut Down a Go Listening Server?

How to Gracefully Shut Down a Go Listening Server?

Patricia Arquette
Patricia ArquetteOriginal
2024-12-24 03:15:12654browse

How to Gracefully Shut Down a Go Listening Server?

How to Gracefully Stop a Listening Server in Go

In Go, the listen.Accept function blocks execution, making it challenging to terminate a listening server gracefully. To determine when to terminate the server, one approach is to close the listening socket and detect the specific error indicating a closed network connection. However, this error is not exported by the net package, leaving developers to resort to awkward error handling.

Fortunately, there is a more elegant solution. By utilizing a done channel, you can signal the server to stop before closing the connection. Here's how it can be implemented with sample code:

package main

import (
    "io"
    "log"
    "net"
    "sync"
    "time"
)

// Echo server struct
type EchoServer struct {
    listen net.Listener
    done   sync.WaitGroup
}

// Respond to incoming connection
//
// Write the address connected to then echo
func (es *EchoServer) respond(remote *net.TCPConn) {
    defer remote.Close()
    _, err := io.Copy(remote, remote)
    if err != nil {
        log.Printf("Error: %s", err)
    }
}

// Listen for incoming connections
func (es *EchoServer) serve() {
    for {
        conn, err := es.listen.Accept()
        if err != nil {
            select {
            case <-es.done:
                // Server has been stopped, so we can exit without showing the error.
            default:
                log.Printf("Accept failed: %v", err)
            }
            return
        }
        es.done.Add(1) // Increment the waitgroup for each incoming connection
        go func() {
            es.respond(conn.(*net.TCPConn))
            es.done.Done() // Decrement the waitgroup when done handling the connection
        }()
    }
}

// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
    es.done.Wait() // Wait for all outstanding connections to finish handling
    es.listen.Close() // Now it the Accept will have an error above
}

// Make a new echo server
func NewEchoServer(address string) *EchoServer {
    listen, err := net.Listen("tcp", address)
    if err != nil {
        log.Fatalf("Failed to open listening socket: %s", err)
    }
    es := &amp;EchoServer{
        listen: listen,
    }
    es.done.Add(1)
    go es.serve()
    return es
}

// Main
func main() {
    log.Println("Starting echo server")
    es := NewEchoServer("127.0.0.1:18081")
    // Run the server for 1 second
    time.Sleep(1 * time.Second)
    // Close the server
    log.Println("Stopping echo server")
    es.stop()
}

In this code, the serve function gracefully terminates the server by returning when a value is received on the done channel. The main function demonstrates how to start the server, wait for connections, and then terminate it gracefully. By utilizing a done channel, the error handling is cleanly separated from the shutdown logic, resulting in a more maintainable and error-free server.

The above is the detailed content of How to Gracefully Shut Down a Go Listening Server?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn