Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to modify the value of nginx-unit backlog parameter? I want to increase it to meet my performance needs. It's time to change the default value, 511 is no longer applicable #1384

Closed
oopsoop2 opened this issue Aug 13, 2024 · 26 comments

Comments

@oopsoop2
Copy link

image
Reasons for asking this question: I designed a web server test that server answers as fast as possible But Windows was dropping a few requests even if server answers as fast as possible. Because Clients disconnected when backlog was full , This is because modern servers fetch more than 511 resources at a time and nginx/php-fpm backlog ( #define NGX_LISTEN_BACKLOG 511) constant was hard coded to 511. Then I Calling listen(2)'s backlog parameter in server with a queue size larger than 511 solved the issue
And The rate at which new connections can be accepted is equal to the number of entries which can fit on the listen queue divided by the average length of time each entry spends on the queue. Therefore, the larger the queue, the greater the rate at which new connection requests can be accepted. so how to Adjusting the connection backlog size? like php-fpm (listen.backlog or nginx ip.80 backlog=xxx) can be setting, i canot find this setting in nginx united configure

@oopsoop2
Copy link
Author

Before adjustment:
wrk -t1 -c2500 -d10s --latency http://127.0.0.1:8001/
Running 10s test @ http://127.0.0.1:8001/
1 threads and 2500 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 181.19ms 23.27ms 1.63s 94.81%
Req/Sec 4.82k 537.57 7.80k 92.86%
Latency Distribution
50% 181.75ms
75% 185.59ms
90% 190.95ms
99% 229.68ms
47011 requests in 10.05s, 161.98MB read
Socket errors: connect 0, read 2038, write 598, timeout 28
Requests/sec: 4676.27
Transfer/sec: 16.11MB
image


After adjustment:
wrk -t1 -c2500 -d10s --latency http://127.0.0.1:8787/
Running 10s test @ http://127.0.0.1:8787/
1 threads and 2500 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 17.44ms 7.58ms 48.34ms 59.73%
Req/Sec 73.81k 14.25k 99.75k 69.41%
Latency Distribution
50% 17.43ms
75% 23.72ms
90% 27.35ms
99% 33.11ms
667467 requests in 10.07s, 1.18GB read
Requests/sec: 66292.37
Transfer/sec: 119.68MB
image
image

@oopsoop2
Copy link
Author

oopsoop2 commented Aug 13, 2024

The above is my test. Backlog is very important, but I couldn't find where to configure in NGINX Unit ?

@ac000
Copy link
Member

ac000 commented Aug 13, 2024

Just to check, are you asking about NGINX Unit or nginx the web server?

@oopsoop2
Copy link
Author

Just to check, are you asking about NGINX Unit or nginx the web server?

NGINX Unit

@ac000
Copy link
Member

ac000 commented Aug 13, 2024

I take it you're using Unit on Linux?. As you see we hardcode the listen(2) backlog to 511, also on Linux the maximum you can set this to is controlled via /proc/sys/net/core/somaxconn which on my system is 4096, so quite a bit room to spare...

Currently there is no way to adjust this other than changing

#define NXT_LISTEN_BACKLOG    511                                               

in src/nxt_listen_socket.h.

However I see no real reason why we couldn't make it configurable...

@oopsoop2
Copy link
Author

Are you referring to the inability to configure through listeners, routes, and applications?Can we only manually recompile src/nxt_listen_stocket. h, right

@oopsoop2
Copy link
Author

sublime_text_WNnPc3zh54
Can we add such a configuration?

@hongzhidao
Copy link
Contributor

Can we add such a configuration?

We'll discuss it with the team. "backlog" looks like more concise.

{
    "*:8001": {
          "pass": "routes",
          "backlog": 4096
   }
}

Btw, how does "backlog" apply to "processes"?

@oopsoop2
Copy link
Author

Thank you for the team's efforts. I am a developer from China. There are 700M people using nginx products here. if ok I will promote it.

@oopsoop2
Copy link
Author

processes
The above is a conjecture,I think this should be bound to listener

@ac000
Copy link
Member

ac000 commented Aug 13, 2024

sublime_text_WNnPc3zh54 Can we add such a configuration?

I don't think the listen backlog has any relevance to "applications". The router process does the listen(2) not the application processes...

@ac000
Copy link
Member

ac000 commented Aug 13, 2024

Btw, how does "backlog" apply to "processes"?

Not sure I understand, the listen backlog is the second parameter to the listen(2) system-call.

SYNOPSIS
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);

It's a per-socket thing.

@ac000
Copy link
Member

ac000 commented Aug 13, 2024

Feel free to give the following patch a spin... (note: It doesn't effect the control socket)

diff --git ./src/nxt_conf_validation.c ./src/nxt_conf_validation.c
index 04091745..267a897d 100644
--- ./src/nxt_conf_validation.c
+++ ./src/nxt_conf_validation.c
@@ -176,6 +176,8 @@ static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
     nxt_conf_value_t *value, void *data);
 static nxt_int_t nxt_conf_vldt_forwarded(nxt_conf_validation_t *vldt,
     nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_listen_backlog(nxt_conf_validation_t *vldt,
+    nxt_conf_value_t *value, void *data);
 static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
     nxt_str_t *name, nxt_conf_value_t *value);
 static nxt_int_t nxt_conf_vldt_object(nxt_conf_validation_t *vldt,
@@ -424,6 +426,10 @@ static nxt_conf_vldt_object_t  nxt_conf_vldt_listener_members[] = {
         .type       = NXT_CONF_VLDT_OBJECT,
         .validator  = nxt_conf_vldt_object,
         .u.members  = nxt_conf_vldt_client_ip_members
+    }, {
+        .name       = nxt_string("backlog"),
+        .type       = NXT_CONF_VLDT_NUMBER,
+        .validator  = nxt_conf_vldt_listen_backlog,
     },
 
 #if (NXT_TLS)
@@ -2677,6 +2683,28 @@ nxt_conf_vldt_forwarded(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
 }
 
 
+static nxt_int_t
+nxt_conf_vldt_listen_backlog(nxt_conf_validation_t *vldt,
+    nxt_conf_value_t *value, void *data)
+{
+    int64_t  backlog;
+
+    backlog = nxt_conf_get_number(value);
+
+    if (backlog < 1) {
+        return nxt_conf_vldt_error(vldt, "The \"backlog\" number must be "
+                                   "equal to or greater than 1.");
+    }
+
+    if (backlog > NXT_INT32_T_MAX) {
+        return nxt_conf_vldt_error(vldt, "The \"backlog\" number must "
+                                   "not exceed %d.", NXT_INT32_T_MAX);
+    }
+
+    return NXT_OK;
+}
+
+
 static nxt_int_t
 nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
     nxt_conf_value_t *value)
diff --git ./src/nxt_router.c ./src/nxt_router.c
index 43209451..5842bcff 100644
--- ./src/nxt_router.c
+++ ./src/nxt_router.c
@@ -166,7 +166,7 @@ static void nxt_router_app_prefork_ready(nxt_task_t *task,
 static void nxt_router_app_prefork_error(nxt_task_t *task,
     nxt_port_recv_msg_t *msg, void *data);
 static nxt_socket_conf_t *nxt_router_socket_conf(nxt_task_t *task,
-    nxt_router_temp_conf_t *tmcf, nxt_str_t *name);
+    nxt_router_temp_conf_t *tmcf, nxt_str_t *name, int backlog);
 static nxt_int_t nxt_router_listen_socket_find(nxt_router_temp_conf_t *tmcf,
     nxt_socket_conf_t *nskcf, nxt_sockaddr_t *sa);
 
@@ -1959,12 +1959,22 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
         next = 0;
 
         for ( ;; ) {
+            int               listen_backlog = 0;
+            nxt_conf_value_t  *backlog;
+
+            static const nxt_str_t  backlog_path = nxt_string("backlog");
+
             listener = nxt_conf_next_object_member(listeners, &name, &next);
             if (listener == NULL) {
                 break;
             }
 
-            skcf = nxt_router_socket_conf(task, tmcf, &name);
+            backlog = nxt_conf_get_object_member(listener, &backlog_path, NULL);
+            if (backlog != NULL) {
+                listen_backlog = nxt_conf_get_number(backlog);
+            }
+
+            skcf = nxt_router_socket_conf(task, tmcf, &name, listen_backlog);
             if (skcf == NULL) {
                 goto fail;
             }
@@ -2684,7 +2694,7 @@ nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name,
 
 static nxt_socket_conf_t *
 nxt_router_socket_conf(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
-    nxt_str_t *name)
+    nxt_str_t *name, int backlog)
 {
     size_t               size;
     nxt_int_t            ret;
@@ -2728,7 +2738,7 @@ nxt_router_socket_conf(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
         nxt_listen_socket_remote_size(ls);
 
         ls->socket = -1;
-        ls->backlog = NXT_LISTEN_BACKLOG;
+        ls->backlog = backlog > 0 ? backlog : NXT_LISTEN_BACKLOG;
         ls->flags = NXT_NONBLOCK;
         ls->read_after_accept = 1;
     }
@@ -2875,7 +2885,7 @@ nxt_router_listen_socket_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
 
     nxt_socket_defer_accept(task, s, rpc->socket_conf->listen->sockaddr);
 
-    ret = nxt_listen_socket(task, s, NXT_LISTEN_BACKLOG);
+    ret = nxt_listen_socket(task, s, rpc->socket_conf->listen->backlog);
     if (nxt_slow_path(ret != NXT_OK)) {
         goto fail;
     }

E.g

    "listeners": {                                                              
        "[::1]:8080": {                                                         
            "pass": "routes",                                                   
            "backlog": 1024                                                     
        },                                                                      
                                                                                
        "127.0.0.1:8080": {                                                     
            "pass": "routes"                                                    
        }                                                                       
    },
$ ss -tunxlp | grep unit
u_str LISTEN 0      511                                   /opt/unit/control.unit.sock.tmp 4302333            * 0    users:(("unitd",pid=18290,fd=6),("unitd",pid=18289,fd=6),("unitd",pid=18287,fd=6))
tcp   LISTEN 0      511                                                         127.0.0.1:8080         0.0.0.0:*    users:(("unitd",pid=18290,fd=12))                                                 
tcp   LISTEN 0      1024                                                            [::1]:8080            [::]:*    users:(("unitd",pid=18290,fd=11))

One socket "::1" set to a backlog of 1024 the other "127.0.0.1" not set, using the default and the control socket just using the default.

@oopsoop2
Copy link
Author

Great, really amazing brother, but I don't know C syntax and how to recompile and build nginx unit. Can you release the entire new and complete version? I will continue to test it

@oopsoop2
Copy link
Author

How to provide a video tutorial and guidance so that people who don't know C language can build and compile their own nginx unit projects? It's definitely the most perfect. By the way, I am a PHP developer, and it's very practical but the threshold is too low, so many people don't know how to use C at a lower level (including me). However, I believe PHP is the best web language, and 80% of Chinese developers also think so. There are about 2 million PHP developers in China

@ac000
Copy link
Member

ac000 commented Aug 14, 2024

OK, no problem. Hopefully we can get this into the next release which is happening Real Soon Now(tm).

@ac000
Copy link
Member

ac000 commented Aug 14, 2024

Hmm, just something interesting...

In src/nxt_listen_socket.h we have

#if (NXT_FREEBSD || NXT_MACOSX || NXT_OPENBSD)                                  
/*                                                                                                              
 * A backlog is limited by system-wide sysctl kern.ipc.somaxconn.               
 * This is supported by FreeBSD 2.2, OpenBSD 2.0, and MacOSX.                   
 */                                                                             
#define NXT_LISTEN_BACKLOG    -1                                                
                                                                                
#else                                                                           
/*                                                                              
 * Linux, Solaris, and NetBSD treat negative value as 0.                        
 * 511 is a safe default.                                                       
 */                                                                             
#define NXT_LISTEN_BACKLOG    511                                                
#endif 

However on at least Linux 2.6.12-rc5 (as far back the main repo goes and I very much doubt we care about anything even remotely that old), Linux does not treat a negative value as 0, it does the same as the BSDs and uses the setting of /proc/sys/net/core/somaxconn with this Kernel code in net/socket.c

int __sys_listen(int fd, int backlog)                                           
{
        ...
                if ((unsigned int)backlog > somaxconn)                          
                        backlog = somaxconn; 
        ...
}

We pass in an int (which is signed). We cast it to an unsigned int, unsigned integer over/underflow is defined behaviour and so a value of -1 for example would wrap around to UINT_MAX when cast to an (unsigned int) making that above check true and so setting the backlog to /proc/sys/net/core/somaxconn.

So a first step could be to adjust the above to include Linux in setting the default backlog to -1. Maybe. FreeBSD seems to default to a value 128, on Linux since 5.4 it defaults to 4096 (previously 128)... this would also change it for the control socket...

Then again https://lore.kernel.org/netdev/[email protected]/ it sounds good!

Fun, fun, fun in the sun, sun sun...

@oopsoop2
Copy link
Author

Yes, 511 is outdated and limits server capabilities. Modern servers and new versions of PHP are becoming more and more powerful. It is time to adjust the backlog default value to maximize performance.

@ac000
Copy link
Member

ac000 commented Aug 14, 2024

@oopsoop2 Out of interest, what Linux Kernel version are you running Unit under?

@oopsoop2
Copy link
Author

@ac000 5.15.153.1-microsoft-standard-WSL2 #1 SMP Fri Mar 29 23:14:13 UTC 2024 x86_64 GNU/Linux
Debian GNU/Linux 12 (bookworm)

@oopsoop2

This comment was marked as resolved.

@ac000
Copy link
Member

ac000 commented Aug 14, 2024

Ok, good, if we change the default on Linux to use the OS's default, then you shouldn't actually need to do anything (unless you need to go higher than 4096...)

@oopsoop2
Copy link
Author

ok, i got it

ac000 added a commit to ac000/unit that referenced this issue Aug 14, 2024
@oopsoop2 on GitHub reported a performance issue related to the default
listen(2) backlog size of 511 on nginx. They found that increasing it
helped, nginx has a config option to configure this.

They would like to be able to do the same on Unit (which also defaults
to 511 on some systems, incl Linux). This seems reasonable.

This adds a new per-listener 'backlog' config option, e.g

  {
      "listeners": {
          "[::1]:8080": {
              "pass": "routes",
              "backlog": 1024
          },
      }

      ...
  }

This doesn't effect the control socket.

Closes: nginx#1384
Reported-by: <https://github.com/oopsoop2>
Signed-off-by: Andrew Clayton <[email protected]>
ac000 added a commit to ac000/unit that referenced this issue Aug 14, 2024
@oopsoop2 on GitHub reported a performance issue related to the default
listen(2) backlog size of 511 on nginx. They found that increasing it
helped, nginx has a config option to configure this.

They would like to be able to do the same on Unit (which also defaults
to 511 on some systems, incl Linux). This seems reasonable.

This adds a new per-listener 'backlog' config option, e.g

  {
      "listeners": {
          "[::1]:8080": {
              "pass": "routes",
              "backlog": 1024
          },
      }

      ...
  }

This doesn't effect the control socket.

Closes: nginx#1384
Reported-by: <https://github.com/oopsoop2>
Signed-off-by: Andrew Clayton <[email protected]>
@callahad
Copy link
Collaborator

callahad commented Aug 15, 2024

RHEL 8 still ships with a 4.x series kernel.

Can we handle that by only setting it to -1 if the kernel version is >= 5.4? ...Or maybe it'd be easier to just unconditionally define NXT_LISTEN_BACKLOG as 4096 in all cases? If you need to go back down to 511 or 128, you can use the configuration option introduced in #1388 ?

@ac000
Copy link
Member

ac000 commented Aug 15, 2024

I actually cover that here

Unless they are on an old Kernel, in which case it's worse, but then the
plan is to also make this configurable. This would effect RHEL 8, which
is based on 4.10, however they seem to set somaxconn to 2048, so that's
fine.

Also

Another advantage of using -1 is that we will automatically keep up to
date with the kernels default value.

@callahad
Copy link
Collaborator

Oh yep, you do. Fire away.

ac000 added a commit to ac000/unit that referenced this issue Aug 15, 2024
@oopsoop2 on GitHub reported a performance issue related to the default
listen(2) backlog size of 511 on nginx. They found that increasing it
helped, nginx has a config option to configure this.

They would like to be able to do the same on Unit (which also defaults
to 511 on some systems, incl Linux). This seems reasonable.

This adds a new per-listener 'backlog' config option, e.g

  {
      "listeners": {
          "[::1]:8080": {
              "pass": "routes",
              "backlog": 1024
          },
      }

      ...
  }

This doesn't effect the control socket.

Closes: nginx#1384
Reported-by: <https://github.com/oopsoop2>
Signed-off-by: Andrew Clayton <[email protected]>
ac000 added a commit to ac000/unit that referenced this issue Aug 15, 2024
@oopsoop2 on GitHub reported a performance issue related to the default
listen(2) backlog size of 511 on nginx. They found that increasing it
helped, nginx has a config option to configure this.

They would like to be able to do the same on Unit (which also defaults
to 511 on some systems, incl Linux). This seems reasonable.

This adds a new per-listener 'backlog' config option, e.g

  {
      "listeners": {
          "[::1]:8080": {
              "pass": "routes",
              "backlog": 1024
          },
      }

      ...
  }

This doesn't effect the control socket.

Closes: nginx#1384
Reported-by: <https://github.com/oopsoop2>
Signed-off-by: Andrew Clayton <[email protected]>
ac000 added a commit to ac000/unit that referenced this issue Aug 15, 2024
On FreeBSD, OpenBSD & macOS we use a default listen(2) backlog of -1
which means use the OS's default value.

On Linux (and others) we used a hard coded value of 511, presumably due
to this comment

  /* Linux, Solaris, and NetBSD treat negative value as 0. */

On Linux (at least since 2.4), this is wrong, Linux treats -1 (and so
on) as use the OS's default (net.core.somaxconn). See this code in
net/socket.c::__sys_listen()

                if ((unsigned int)backlog > somaxconn)
                        backlog = somaxconn;

On Linux prior to 5.4 somaxconn defaulted to 128, since 5.4 it defaults
to 4096.

We've had complaints that a listen backlog of 511 is too small. This
would help in those cases.

Unless they are on an old Kernel, in which case it's worse, but then the
plan is to also make this configurable. This would effect RHEL 8, which
is based on 4.10, however they seem to set somaxconn to 2048, so that's
fine.

Another advantage of using -1 is that we will automatically keep up to
date with the kernels default value.

Before this change

  $ ss -tunxlp | grep unit
  Netid State  Recv-Q Send-Q                                                  Local Address:Port     Peer Address:Port           Process
  u_str LISTEN 0      511                                   /opt/unit/control.unit.sock.tmp 4302333            * 0    users:(("unitd",pid=18290,fd=6),("unitd",pid=18289,fd=6),("unitd",pid=18287,fd=6))
  tcp   LISTEN 0      511                                                         127.0.0.1:8080         0.0.0.0:*    users:(("unitd",pid=18290,fd=12))
  tcp   LISTEN 0      511                                                             [::1]:8080            [::]:*    users:(("unitd",pid=18290,fd=11))

After

  $ ss -tunxlp | grep unit
  Netid State  Recv-Q Send-Q                                                  Local Address:Port     Peer Address:Port           Process
  u_str LISTEN 0      4096                                  /opt/unit/control.unit.sock.tmp 5408464            * 0    users:(("unitd",pid=132442,fd=6),("unitd",pid=132441,fd=6),("unitd",pid=132439,fd=6))
  tcp   LISTEN 0      4096                                                        127.0.0.1:8080         0.0.0.0:*    users:(("unitd",pid=132442,fd=12))
  tcp   LISTEN 0      4096                                                            [::1]:8080            [::]:*    users:(("unitd",pid=132442,fd=11))

Link: <nginx#1384>
Link: <https://lore.kernel.org/netdev/[email protected]/>
Signed-off-by: Andrew Clayton <[email protected]>
ac000 added a commit to ac000/unit that referenced this issue Aug 19, 2024
On FreeBSD, OpenBSD & macOS we use a default listen(2) backlog of -1
which means use the OS's default value.

On Linux (and others) we used a hard coded value of 511, presumably due
to this comment

  /* Linux, Solaris, and NetBSD treat negative value as 0. */

On Linux (at least since 2.4), this is wrong, Linux treats -1 (and so
on) as use the OS's default (net.core.somaxconn). See this code in
net/socket.c::__sys_listen()

                if ((unsigned int)backlog > somaxconn)
                        backlog = somaxconn;

On Linux prior to 5.4 somaxconn defaulted to 128, since 5.4 it defaults
to 4096.

We've had complaints that a listen backlog of 511 is too small. This
would help in those cases.

Unless they are on an old Kernel, in which case it's worse, but then the
plan is to also make this configurable. This would effect RHEL 8, which
is based on 4.10, however they seem to set somaxconn to 2048, so that's
fine.

Another advantage of using -1 is that we will automatically keep up to
date with the kernels default value.

Before this change

  $ ss -tunxlp | grep unit
  Netid State  Recv-Q Send-Q                                                  Local Address:Port     Peer Address:Port           Process
  u_str LISTEN 0      511                                   /opt/unit/control.unit.sock.tmp 4302333            * 0    users:(("unitd",pid=18290,fd=6),("unitd",pid=18289,fd=6),("unitd",pid=18287,fd=6))
  tcp   LISTEN 0      511                                                         127.0.0.1:8080         0.0.0.0:*    users:(("unitd",pid=18290,fd=12))
  tcp   LISTEN 0      511                                                             [::1]:8080            [::]:*    users:(("unitd",pid=18290,fd=11))

After

  $ ss -tunxlp | grep unit
  Netid State  Recv-Q Send-Q                                                  Local Address:Port     Peer Address:Port           Process
  u_str LISTEN 0      4096                                  /opt/unit/control.unit.sock.tmp 5408464            * 0    users:(("unitd",pid=132442,fd=6),("unitd",pid=132441,fd=6),("unitd",pid=132439,fd=6))
  tcp   LISTEN 0      4096                                                        127.0.0.1:8080         0.0.0.0:*    users:(("unitd",pid=132442,fd=12))
  tcp   LISTEN 0      4096                                                            [::1]:8080            [::]:*    users:(("unitd",pid=132442,fd=11))

Link: <nginx#1384>
Link: <https://lore.kernel.org/netdev/[email protected]/>
Signed-off-by: Andrew Clayton <[email protected]>
ac000 added a commit to ac000/unit that referenced this issue Aug 19, 2024
@oopsoop2 on GitHub reported a performance issue related to the default
listen(2) backlog size of 511 on nginx. They found that increasing it
helped, nginx has a config option to configure this.

They would like to be able to do the same on Unit (which also defaults
to 511 on some systems). This seems reasonable.

NOTE: On Linux before commit 97c15fa ("socket: Use a default listen
backlog of -1 on Linux") we defaulted to 511. Since that commit we
default to the Kernels default, which before 5.4 is 128 and after is
4096.

This adds a new per-listener 'backlog' config option, e.g

  {
      "listeners": {
          "[::1]:8080": {
              "pass": "routes",
              "backlog": 1024
          },
      }

      ...
  }

This doesn't effect the control socket.

Closes: nginx#1384
Reported-by: <https://github.com/oopsoop2>
Signed-off-by: Andrew Clayton <[email protected]>
@ac000 ac000 closed this as completed in 76489fb Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants