Loading...
Real Time Concepts

Nginx Performance Tuning

worker_processes and worker_connections

Nginx Event Model

On Linux, Nginx uses the efficient epoll event model. This involves a child worker process with a single thread responding to I/O events (network, disk, etc.) that the kernel sends to it. Each worker process can handle a large number of simultaneous connections, so you can’t think of Nginx worker processes in the same way you think of Apache child processes in the commonly used Prefork MPM

Nginx has two settings governing limits for its event model:

worker_processes

The number of child processes nginx will spawn at startup. Remember that each of these processes will be able to handle multiple simultaneous requests (up to worker_connections).

worker_connections

The maximum number of simultaneous connections that a single worker_process will handle.

Tuning

While Apache tends to have default settings that need a lot of tuning, Nginx default settings work well in most cases and rarely need tuning.

worker_processes should be set as auto (likely pre-filled in) which will use the total number of CPU cores on the server. Setting this higher than the CPU/vCPU count is not advised and likely will offer no advantage.

worker_connections should left at the pre-filled value of 1024 in most cases (though higher values might be needed in VERY high traffic scenarios).

Calculating the Maximum Concurrent Requests (eg. “MaxClients/MaxRequestWorkers” on Apache prefork)

max clients = worker_processes * worker_connections
In a reverse proxy situation, max clients becomes
max clients = worker_processes * worker_connections/4

Gzip Compression

These settings will generally go within the http block as sensible “globals”, though to keep things tidy should be placed in the default included folder, eg. at /etc/nginx/conf.d/gzip.conf:

/etc/nginx/conf.d/gzip.conf

# Enable Gzip compressed.
gzip on;

# Enable compression both for HTTP/1.0 and HTTP/1.1 (required for CloudFront).

gzip_http_version 1.0;

# Compression level (1-9).
# 5 is a perfect compromise between size and cpu usage, offering about
# 75% reduction for most ascii files (almost identical to level 9).
gzip_comp_level 5;

# Don’t compress anything that’s already small and unlikely to shrink much
# if at all (the default is 20 bytes, which is bad as that usually leads to
# larger files after gzipping). If the original size is less than a packet,
# compressing may not be any quicker either.
gzip_min_length 1024;

# Compress data even for clients that are connecting to us via proxies,
# identified by the “Via” header (required for CloudFront).
gzip_proxied any;

# Based on the User-Agent, disable Gzip for the builtin regex of IE 6 (RIP).
gzip_disable msie6;

# Tell proxies to cache both the gzipped and regular version of a resource
# whenever the client’s Accept-Encoding capabilities header varies;
# Avoids the issue where a non-gzip capable client (which is extremely rare
# today) would display gibberish if their proxy gave them the gzipped version.
gzip_vary on;

# Compress all output labeled with one of the following MIME-types.
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy
text/xml;

# text/html is always compressed by HttpGzipModule
# This should be turned on if you are going to have pre-compressed copies (.gz) of
# static files available. If not it should be left off as it will cause extra I/O
# for the check. It is best if you enable this in a location{} block for
# a specific directory, or on an individual server{} level.
# gzip_static on;

Keepalive

Keepalive allows a HTTP/S client to retain the TCP connection for a period of time, allowing them to reuse it for subsequent HTTP requests. The advantage being that the roundtrip(s) or processing required to recreate the HTTP/S connection are saved which can be very beneficial for sites with a large number of resources per page or where the clients may experience higher latencies.

The balance here though (and why setting too high is problematic) is that each client connected is holding open a connection on your server. Generally 15-45 seconds is a suitable value where benefit is seen for concurrent requests, but between page loads the connection will close. `curl` can be used to test by supplying 2 URLs and with the right verbosity, will show a line like “Connection #0 to host example.com left ” and then ” Re-using existing connection! (#0) with host example.com

keepalive_timeout 15s;

$ curl -svo /dev/null -o /dev/null https://www.google.com/ https://www.google.com/
* About to connect() to
www.google.com
* Trying 216.58.213.4…

* Connected to
port 443 (#0)
(216.58.213.4) port 443 (#0)
www.google.com
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=
,O=Google LLC,L=Mountain View,ST=California,C=US
www.google.com
* start date: May 21 20:36:27 2019 GMT
* expire date: Aug 13 20:31:00 2019 GMT
* common name: www.google.com
* issuer: CN=Google Internet Authority G3,O=Google Trust Services,C=US
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.google.com
> Accept: */*
[…]

* Connection #0 to host
* Found bundle for host
www.google.com
left intact
: 0x2079f30
www.google.com
* Re-using existing connection! (#0) with host www.google.com
* Connected to
www.google.com
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.google.com
> Accept: */*
[…]

* Connection #0 to host
(216.58.213.4) port 443 (#0)
www.google.com

HTTP Expires

You can take advantage of client/browser caching with the HTTP responses. Typically, this is done in location expires
directive which will add both the Expires and Cache-Control blocks. Here is an example which will cache static files for 30 days:

location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ {
expires 30d;
}

As with any client side caching, you lose control of that object the moment it is no longer being requested from the server. Modern sites will “version” their files so that which each site upate, they change the resource name (or add a query string) and due to this the client will request it fresh from the server.

sendfile

sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.

For the best performance, make sure this is set to NFS seems to be due to issues in how the ON in /etc/nginx/nginx.conf, unless the site is pulling content from an NFS mount. The issue with sendfile() call works on these filesystems. Some report that files are slow to update after changes or bad data is instead served (it “remembers” the original file size after a change). A much better solution would have a web server running on that node and reverse proxy to it (vs. a hanging nginx process in D state waiting for IO on a dead NFS server).

If you are not serving any static files (or you are simply proxying everything), then this will not provide a huge impact for you.

http {

sendfile on;

}

tcp_nopush

This option tells nginx to optimize the way it sends packets when using sendfile() and is recommended for use together.

http {

tcp_nopush on;

}

Leave a Reply

Your email address will not be published. Required fields are marked *