Giter VIP home page Giter VIP logo

Comments (7)

valyala avatar valyala commented on August 29, 2024 21

@mtojek , just wrap any listener into 'graceful shutdown' listener. Something like the following (I didn't test it, but you should get the idea):

type GracefulListener struct {
    // inner listener
    ln net.Listener

    // maximum wait time for graceful shutdown
    maxWaitTime time.Duration

    // this channel is closed during graceful shutdown on zero open connections.
    done chan struct{}

    // the number of open connections
    connsCount uint64

    // becomes non-zero when graceful shutdown starts
    shutdown uint64
}

// NewGracefulListener wraps the given listener into 'graceful shutdown' listener.
func NewGracefulListener(ln net.Listener, maxWaitTime time.Duration) net.Listener {
    return &GracefulListener{
        ln: ln,
        maxWaitTime: maxWaitTime,
        done: make(chan struct{}),
    }
}

func (ln *GracefulListener) Accept() (net.Conn, error) {
    c, err := ln.ln.Accept()
    if err != nil {
        return nil, err
    }
    atomic.AddUint64(&ln.connsCount, 1)
    return &gracefulConn{
        Conn: c,
        ln: ln,
    }, nil
}

// Close closes the inner listener and waits until all the pending open connections
// are closed before returning.
func (ln *GracefulListener) Close() error {
    err := ln.ln.Close()
    if err != nil {
        return nil
    }
    return ln.waitForZeroConns()
}

func (ln *GracefulListener) waitForZeroConns() error {
    atomic.AddUint64(&ln.shutdown, 1)
    select {
    case <-ln.done:
        return nil
    case <-time.After(ln.maxWaitTime):
        return fmt.Errorf("cannot complete graceful shutdown in %s", ln.maxWaitTime)
    }
}

func (ln *GracefulListener) closeConn() {
    connsCount := atomic.AddUint64(&ln.connsCount, ^uint64(0))
    if atomic.LoadUint64(&ln.shutdown) != 0 && connsCount == 0 {
        close(ln.done)
    }
}

type gracefulConn struct {
    net.Conn
    ln *GracefulListener
}

func (c *gracefulConn) Close() error {
    err := c.Conn.Close()
    if err != nil {
        return err
    }
    c.ln.closeConn()
}

from fasthttp.

derekstavis avatar derekstavis commented on August 29, 2024 19

Here's @valyala snippet with a couple of fixes that were need to make it work:

type GracefulListener struct {
	// inner listener
	ln net.Listener

	// maximum wait time for graceful shutdown
	maxWaitTime time.Duration

	// this channel is closed during graceful shutdown on zero open connections.
	done chan struct{}

	// the number of open connections
	connsCount uint64

	// becomes non-zero when graceful shutdown starts
	shutdown uint64
}

// NewGracefulListener wraps the given listener into 'graceful shutdown' listener.
func newGracefulListener(ln net.Listener, maxWaitTime time.Duration) net.Listener {
	return &GracefulListener{
		ln:          ln,
		maxWaitTime: maxWaitTime,
		done:        make(chan struct{}),
	}
}

func (ln *GracefulListener) Accept() (net.Conn, error) {
	c, err := ln.ln.Accept()

	if err != nil {
		return nil, err
	}

	atomic.AddUint64(&ln.connsCount, 1)

	return &gracefulConn{
		Conn: c,
		ln:   ln,
	}, nil
}

func (ln *GracefulListener) Addr() net.Addr {
	return ln.ln.Addr()
}

// Close closes the inner listener and waits until all the pending open connections
// are closed before returning.
func (ln *GracefulListener) Close() error {
	err := ln.ln.Close()

	if err != nil {
		return nil
	}

	return ln.waitForZeroConns()
}

func (ln *GracefulListener) waitForZeroConns() error {
	atomic.AddUint64(&ln.shutdown, 1)

	if atomic.LoadUint64(&ln.connsCount) == 0 {
		close(ln.done)
		return nil
	}

	select {
	case <-ln.done:
		return nil
	case <-time.After(ln.maxWaitTime):
		return fmt.Errorf("cannot complete graceful shutdown in %s", ln.maxWaitTime)
	}

	return nil
}

func (ln *GracefulListener) closeConn() {
	connsCount := atomic.AddUint64(&ln.connsCount, ^uint64(0))

	if atomic.LoadUint64(&ln.shutdown) != 0 && connsCount == 0 {
		close(ln.done)
	}
}

type gracefulConn struct {
	net.Conn
	ln *GracefulListener
}

func (c *gracefulConn) Close() error {
	err := c.Conn.Close()

	if err != nil {
		return err
	}

	c.ln.closeConn()

	return nil
}

from fasthttp.

valyala avatar valyala commented on August 29, 2024 5

Hi Marcin,
Yo can implement custom net.Listener, which will wrap accepted connections into a custom net.Conn with overridden Close method, which decrements the number of open connections. This way you may keep track on the number of open connections through custom listener and wait until this number drops to zero before returning from overridden net.Listener.Close. I'd suggest using non-zero timeout when waiting for pending connections, since certain clients may keep connections open for extended periods of time, so the graceful shutdown may never finish.

from fasthttp.

mtojek avatar mtojek commented on August 29, 2024

Thank you @valyala for the proposed solution, but it may be more difficult if I had already used another listener like e.g. https://github.com/kavu/go_reuseport.

from fasthttp.

mtojek avatar mtojek commented on August 29, 2024

Even though I already solved the problem, Big Up for responding after one year, thanks! :)

from fasthttp.

jeffrand avatar jeffrand commented on August 29, 2024

Thanks for writing this! I added it to a repo so we can use it in a few projects

https://github.com/seatgeek/graceful_listener

from fasthttp.

dstroot avatar dstroot commented on August 29, 2024

I wrote a full server example. I wanted a fully working sample for future reference. It is pretty limited but I like the structure and layout. I didn't write any tests yet. Feedback welcome and encouraged. Cheers!

https://github.com/dstroot/fasthttp-graceful-shutdown

from fasthttp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.