ok = ranch:suspend_listener(Ref), ok = ranch:wait_for_connections(Ref, '==', 0), ok = ranch:stop_listener(Ref).
Stopping a Ranch listener via ranch:stop_listener/1
will invariably kill all connection processes the listener hosts. However, you may want to stop a listener in a graceful fashion, ie by not accepting any new connections, but allowing the existing connection processes to exit by themselves instead of being killed.
For this purpose, you should first suspend the listener you wish to stop gracefully, and then wait for its connection count to drop to zero.
ok = ranch:suspend_listener(Ref), ok = ranch:wait_for_connections(Ref, '==', 0), ok = ranch:stop_listener(Ref).
If you want to drain more than just one listener, it may be important to first suspend them all before beginning to wait for their connection counts to reach zero. Otherwise, the not yet suspended listeners will still be accepting connections while you wait for the suspended ones to be drained.
lists:foreach( fun (Ref) -> ok = ranch:suspend_listener(Ref) end, Refs ), lists:foreach( fun (Ref) -> ok = ranch:wait_for_connections(Ref, '==', 0), ok = ranch:stop_listener(Ref) end, Refs ).
If you have long-running connection processes hosted by the listener you want to stop gracefully, draining may take a long time, possibly forever. If you just want to give the connection processes a chance to finish, but are not willing to wait for infinity, the waiting part could be handled in a separate process.
ok = ranch:suspend_listener(Ref), {DrainPid, DrainRef} = spawn_monitor( fun () -> ok = ranch:wait_for_connections(Ref, '==', 0) end ), receive {'DOWN', DrainRef, process, DrainPid, _} -> ok after DrainTimeout -> exit(DrainPid, kill), ok end, ok = ranch:stop_listener(Ref).
To drain listeners automatically as part of your application shutdown routine, use the prep_stop/1
function of your application module.
-module(my_app). -behavior(application). -export([start/2]). -export([prep_stop/1]). -export([stop/1]). start(_StartType, _StartArgs) -> {ok, _} = ranch:start_listener(my_listener, ranch_tcp, #{}, my_protocol, []), my_app_sup:start_link(). prep_stop(State) -> ok = ranch:suspend_listener(my_listener), ok = ranch:wait_for_connections(my_listener, '==', 0), ok = ranch:stop_listener(my_listener), State. stop(_State) -> ok.
Donate to Loïc Hoguin because his work on Cowboy, Ranch, Gun and Erlang.mk is fantastic:
Recurring payment options are also available via GitHub Sponsors. These funds are used to cover the recurring expenses like food, dedicated servers or domain names.