changeset 7992:e2d07e4ec636

Events: fixed balancing between workers with EPOLLEXCLUSIVE. Linux with EPOLLEXCLUSIVE usually notifies only the process which was first to add the listening socket to the epoll instance. As a result most of the connections are handled by the first worker process (ticket #2285). To fix this, we re-add the socket periodically, so other workers will get a chance to accept connections.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 30 Dec 2021 01:08:46 +0300
parents 57581198e51e
children 96ae8e57b3dd
files src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_accept.c
diffstat 3 files changed, 64 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -55,6 +55,7 @@ ngx_uint_t            ngx_accept_events;
 ngx_uint_t            ngx_accept_mutex_held;
 ngx_msec_t            ngx_accept_mutex_delay;
 ngx_int_t             ngx_accept_disabled;
+ngx_uint_t            ngx_use_exclusive_accept;
 
 
 #if (NGX_STAT_STUB)
@@ -644,6 +645,8 @@ ngx_event_process_init(ngx_cycle_t *cycl
 
 #endif
 
+    ngx_use_exclusive_accept = 0;
+
     ngx_queue_init(&ngx_posted_accept_events);
     ngx_queue_init(&ngx_posted_next_events);
     ngx_queue_init(&ngx_posted_events);
@@ -889,6 +892,8 @@ ngx_event_process_init(ngx_cycle_t *cycl
         if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
             && ccf->worker_processes > 1)
         {
+            ngx_use_exclusive_accept = 1;
+
             if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
                 == NGX_ERROR)
             {
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -462,6 +462,7 @@ extern ngx_uint_t             ngx_accept
 extern ngx_uint_t             ngx_accept_mutex_held;
 extern ngx_msec_t             ngx_accept_mutex_delay;
 extern ngx_int_t              ngx_accept_disabled;
+extern ngx_uint_t             ngx_use_exclusive_accept;
 
 
 #if (NGX_STAT_STUB)
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -11,6 +11,9 @@
 
 
 static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+static void ngx_reorder_accept_events(ngx_listening_t *ls);
+#endif
 static void ngx_close_accepted_connection(ngx_connection_t *c);
 
 
@@ -314,6 +317,10 @@ ngx_event_accept(ngx_event_t *ev)
         }
 
     } while (ev->available);
+
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+    ngx_reorder_accept_events(ls);
+#endif
 }
 
 
@@ -420,6 +427,57 @@ ngx_disable_accept_events(ngx_cycle_t *c
 }
 
 
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+
+static void
+ngx_reorder_accept_events(ngx_listening_t *ls)
+{
+    ngx_connection_t  *c;
+
+    /*
+     * Linux with EPOLLEXCLUSIVE usually notifies only the process which
+     * was first to add the listening socket to the epoll instance.  As
+     * a result most of the connections are handled by the first worker
+     * process.  To fix this, we re-add the socket periodically, so other
+     * workers will get a chance to accept connections.
+     */
+
+    if (!ngx_use_exclusive_accept) {
+        return;
+    }
+
+#if (NGX_HAVE_REUSEPORT)
+
+    if (ls->reuseport) {
+        return;
+    }
+
+#endif
+
+    c = ls->connection;
+
+    if (c->requests++ % 16 != 0
+        && ngx_accept_disabled <= 0)
+    {
+        return;
+    }
+
+    if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
+        == NGX_ERROR)
+    {
+        return;
+    }
+
+    if (ngx_add_event(c->read, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
+        == NGX_ERROR)
+    {
+        return;
+    }
+}
+
+#endif
+
+
 static void
 ngx_close_accepted_connection(ngx_connection_t *c)
 {