Skip to content

Commit f33785f

Browse files
author
aszubarev
committed
Prevent gthread connection reset on max-requests restart
1 parent bacbf8a commit f33785f

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

gunicorn/config.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ def worker_class(self):
125125
worker_class.setup()
126126
return worker_class
127127

128+
@property
129+
def worker_connections_enqueue_async(self):
130+
return self.settings['worker_connections_enqueue_async'].get()
131+
128132
@property
129133
def address(self):
130134
s = self.settings['bind'].get()
@@ -737,6 +741,30 @@ class WorkerConnections(Setting):
737741
"""
738742

739743

744+
class WorkerConnectionsEnqueueAsync(Setting):
745+
name = "worker_connections_enqueue_async"
746+
section = "Worker Processes"
747+
cli = ["--worker-connections-enqueue-async"]
748+
validator = validate_bool
749+
action = 'store_true'
750+
default = False
751+
desc = """\
752+
Enqueue accepted connections to worker asynchronously.
753+
754+
It helps to use Gunicorn without reverse proxy.
755+
When clients connect to Gunicorn, the server will start read data only when clients sent data.
756+
If clients keep connections without sending data, server only accept the connections and no block the worker;
757+
758+
.. note::
759+
If you're enable ``max-requests`` and it option, the worker can restart with accepted connections.
760+
761+
When clients send data via connections after restart the worker,
762+
clients may have error ``Connection reset by peer``
763+
764+
This setting only affects the ``gthread`` worker types.
765+
"""
766+
767+
740768
class MaxRequests(Setting):
741769
name = "max_requests"
742770
section = "Worker Processes"

gunicorn/workers/gthread.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,18 @@ def accept(self, server, listener):
123123
conn = TConn(self.cfg, sock, client, server)
124124

125125
self.nr_conns += 1
126-
# wait until socket is readable
127-
with self._lock:
128-
self.poller.register(conn.sock, selectors.EVENT_READ,
129-
partial(self.on_client_socket_readable, conn))
126+
127+
if not self.cfg.worker_connections_enqueue_async:
128+
# submit the connection to a worker
129+
self.enqueue_req(conn)
130+
else:
131+
# wait until socket is readable
132+
with self._lock:
133+
self.poller.register(
134+
conn.sock,
135+
selectors.EVENT_READ,
136+
partial(self.on_client_socket_readable, conn),
137+
)
130138
except OSError as e:
131139
if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
132140
errno.EWOULDBLOCK):
@@ -137,7 +145,7 @@ def on_client_socket_readable(self, conn, client):
137145
# unregister the client from the poller
138146
self.poller.unregister(client)
139147

140-
if conn.initialized:
148+
if not self.cfg.worker_connections_enqueue_async or conn.initialized:
141149
# remove the connection from keepalive
142150
try:
143151
self._keep.remove(conn)

0 commit comments

Comments
 (0)