NGINX Full Version

借助 NGINX 最大限度地提高 PHP 7 的性能,第 1 部分:Web 服务和缓存

Introduction – How NGINX is Used with PHP

PHP is the most popular way to create a server‑side Web application, with roughly 80% market share. (ASP.net is a distant second, and Java an even more distant third.)

The PHP universe includes a multitude of PHP frameworks; the most popular include Laravel, Phalcon, and Symfony 2. PHP is also the basis for popular content management systems (CMSs) such as WordPress and Drupal. (The most recent Drupal release, Drupal 8, includes significant Symfony 2 integration.

Now the PHP team is releasing a new version, PHP 7  – more than a decade after the introduction of PHP 5. During this time, usage of the web and the demands on websites have both increased exponentially. PHP has contributed to that rapid growth  – but PHP’s limitations are also highlighted by the very growth that it has enabled.

PHP is commonly seen as powerful and flexible, but subject to performance problems. PHP‑based sites can easily “hit a wall” after just a few doublings in traffic numbers. Just as a site begins to meet its business or operational goals, it starts to crash whenever traffic volume rises.

For thousands and thousands of PHP‑based applications, some relatively simple changes have been enough to improve performance. These include caching with tools such as memcached, tuning databases, and reverse proxy and load balancing with NGINX Open Source and NGINX Plus. NGINX has greatly improved app responsiveness, supporting order‑of‑magnitude increases in user and traffic numbers.

This blog post is the first in a two‑part series about maximizing the performance of your websites that use PHP 7. Here we focus on upgrading to PHP 7, implementing NGINX Open Source or NGINX Plus as your web server software, rewriting URLs (necessary for requests to be handled properly), caching static files, and caching dynamic files (also called application caching or microcaching).

In the next blog post, we focus on steps you can take with additional servers: adding a reverse proxy server, moving to multiple application servers, load balancing among multiple servers, supporting session persistence while load balancing, and terminating security protocols, such as SSL/TLS and the related HTTP/2 protocol.

Why PHP Hits a Wall

Why do PHP applications hit a wall? For the same reason any application server software hits a wall. When a user request comes in, PHP – and the web server software it runs on top of – have to do several things:

That’s a lot for a physical server, virtual machine, or cloud server instance to handle for every request. Performance tends to bog down when physical memory on the server machine – whether physical or virtual – is exhausted. The server then starts paging current sessions to disk as new requests come in. Waiting for file requests to fulfill also introduces wait states that contribute to paging as well. Past a (very limited) point, paging operations and data requests overwhelm processing operations, and performance enters a death spiral that causes long waits or outright session termination for frustrated users.

Addressing Performance Issues

Overcoming PHP’s performance barriers is certainly possible, and it takes several complementary steps. Each step can be combined with others. Roughly, they include:

You don’t have to implement these steps in any particular order; for instance, even if you keep Apache as your web server and don’t upgrade your app server to PHP 7, just implementing NGINX as a reverse proxy “in front of” your existing servers improves performance and enables you to implement multiple application servers in parallel.

However you go about things, the key fact to keep in mind is that you can obtain multiple factors of increased performance, even an order of magnitude in capacity, with little or no change to your current application code. Read on to see how people have already achieved, or are in the process of achieving, these extraordinary performance gains.

Note: There’s a multiserver optimization that we’ll ignore somewhat in these blog posts. A separate database server and a content delivery network (CDN) can offload your application server and improve performance greatly; these kinds of changes are separate from, and parallel to, the application and implementation improvements described here.

Tip 1 – Upgrade to PHP 7

The main reason for upgrading to PHP 7, sooner rather than later, is simple: application speed (significantly enabled by memory savings). PHP 7 is said to be twice as fast as previous versions of PHP, and to use considerably less memory. (Your mileage will doubtless vary, in both respects.)

Response time is, quite simply, critical for web applications. A faster web app – which also uses less memory, reducing the likelihood of page swapping and resulting performance problems – accomplishes three things:

  1. Makes users happier and more likely to visit and complete tasks on your site, such as reading articles, getting product information, hailing a jitney, renting a spare room, or buying things. That is, the reasons you created the site or app in the first place.
  2. Enables a given server to support more users without running the risk that it slows down or even keels over from additional users. Postponing doom is always a good thing.
  3. Makes your server less vulnerable to hacker attacks that overload your server to drive it out of production. Everyone gets attacked today, but the weak get attacked more, and more aggressively. So less vulnerability can be exponentially better than more.

These are all excellent reasons to upgrade; taken together, the case for upgrading seems almost overwhelming. And “upgrade to the latest version” is always the first recommendation for fixing many problems, even when there isn’t such a clear performance benefit. So why doesn’t everyone upgrade right away?

Updates according to xkcd

Simple: people hate to mess with old code, and for good reason. If an old app is working well enough, and developers get better results from creating new apps than from upgrading old ones, the old app can hang around, unchanged, for a very long time. (See the second blog post in this series for information on how to use NGINX to improve your app’s performance without any changes to your current web server and app.)

But the more efficient thing to do, if at all possible, is to begin your quest for greater performance by upgrading to PHP 7. Don’t start, though, until you have enough time to finish, especially without skimping on testing.

Let’s take a look at what it takes to upgrade to PHP 7:

This blog at Engine Yard has good examples of most of these issues.

If you decide to go ahead and upgrade to PHP 7, consider doing a full performance review and revision of your code, or at least of its critical features, taking advantage of the new features in PHP 7. There’s no better way for you and your team, to skill up, and the changes you make, review, and test today are likely to serve you for many years to come. You’ll also get the most out of the other performance suggestions in this blog post, because optimized code benefits strongly from running in an optimized environment.

So, despite the fact that sites often deploy NGINX to get better performance without touching application code, we recommend that you take the bit in your teeth and charge forward. There’s a lot of help out there for making the move, or you can just roll up your sleeves and do it yourself. There’s a PHP 7 migration guide at the official PHP site, and a PHP 7 upgrade how‑to book from O’Reilly.

Tip 2 – Choose NGINX Open Source or NGINX Plus

NGINX is the software that runs more than 140 million websites, including half of the 10,000 busiest websites. (These measurements detect the web server at single‑server sites and the reverse proxy server at multiserver sites.) As a web server, both offer an immediate performance boost – in some cases, where a server running other software has been overloaded and thrashing, up to 10 times. As a reverse proxy server, both enable the use of multiple dedicated servers to scale out a deployment as extensively as needed.

Both PHP and Apache assign resources to every open request; if either or both have to load a number of libraries, the start‑up time per request and the memory footprint can be considerable. Moving to NGINX as your web server software removes this problem at the server level. Using the functionality of NGINX to offload work to the web server (such as serving static files), or to a reverse proxy server (all kinds of caching, protocol termination, load balancing, etc.) minimizes what PHP has to do, simplifying and speeding application processing.

If you have custom or proprietary Apache modules for your site, you might not be able to replace Apache with NGINX until you replace the modules. Check with NGINX to see if there are easy workarounds; if not, size the time and effort required to make the change.

Once you decide to go with NGINX, you have a choice between NGINX Open Source and NGINX Plus. Some of the most the most prominent features of NGINX Plus over NGINX Open Source are:

As a reverse proxy server, NGINX Plus has additional advantages:

Both NGINX Open Source and NGINX Plus support content caching and microcaching (also called application caching). Caching is helpful in the web server context, as it offloads the application server, but both functions are still sharing a single machine or virtual machine instance. On a reverse proxy server, caching can offload a significant amount of work from the application server device, offering greater performance advantages.

You can download NGINX Open Source software directly from nginx.org, where you’ll also find community support. To start a single NGINX Plus subscription, register for a free 30-day trial or purchase online. For multiinstance bundles, contact NGINX Sales.

Tip 3 – Convert Apache Configuration to NGINX Syntax

When you move from Apache to NGINX as your web server software, there are a few changes you need to make – detailed in an excellent article on sitepoint.com:

Making these changes familiarizes you with NGINX and equips you for optimizing more complex sites, as we describe in Part 2 of this blog post. If making these configuration changes poses an unacceptable amount of work or degree of risk to your site operations, however, never fear – you can implement the multiserver architectures described in Part 2 without upgrading the core web server software from Apache, and therefore without changing your web server configuration files.

Tip 4 – Implement Static File Caching

Static files are simply files that don’t change often, at least in web‑server terms. Static files usually include graphics files such as JPEGs and PNGs and code files such as CSS and JavaScript files. If you put these files on your application server, or on a separate database server, requests for them have to be processed by the application code, with all the overhead required for making and fulfilling a request. This “distracts” the application server from more important work and can bring it closer to the point where physical memory is overloaded and new requests cause current requests to page out to disk.

Static file caching is a core function of NGINX. You can implement it on a web server or a reverse proxy server:

There are three overall steps to implementing static file caching on NGINX:

On an NGINX web server, with no reverse proxy server involved, you don’t cache in the usual sense. You simply redirect inquiries for static files to the web server, using the X-Accel-Redirect header. The application server never sees the request and can devote all its resources to application requests. With a reverse proxy server, you do use static file caching – and the physical server or virtual server instance that runs the application doesn’t have any part in answering the static file requests.

As an example of optimizing response speed, the following configuration snippet enables NGINX to use the operating system’s sendfile system call, which saves a step in file transmission by not copying the file to an intermediate buffer:

location /mp3 {
    sendfile           on;
    sendfile_max_chunk 1m;
    # ...
}

For specifics on configuring NGINX for static file caching, see the NGINX Plus Admin Guide.

Tip 5 – Implement Microcaching

Microcaching, confusingly, goes by many names, which also include application caching and just plain caching. Here at NGINX, we use the term microcaching to emphasize the brief time such files are valid.

Let’s say that you have a blog post page that provides a mechanism for user comments. You want to include the latest and greatest comments anytime a new visitor arrives at the page – or anytime existing users refresh the page to see their own, or someone else’s, new comment. In this case, it seems like a good idea to generate the page anew every time anyone visits it.

However, “every time” can become burdensome. If a page is getting one visit per second, generating it anew for every visit makes sense. But if the page is getting ten, or a hundred, or a thousand visits per second, along with all the other pages on the site, the application server can get overloaded. Meeting the goal of serving people fresh content means no one gets any content quickly.

Microcaching means generating a page once marking the cached version as valid for a brief period of time – say, one or a few seconds. When the cached version expires, the next request prompts generation of a fresh page, and requests right after get its cached version. This is the same behavior as for a static file, but on much shorter timeframes.

This image indicates where to look on your site for content that you can microcache. It’s from a microcaching blog post by our own Owen Garrett.

Much dynamic content is suitable for microcaching

Microcaching is awesome because it removes work from the application server just when that is what’s needed most, and with very little to no detriment to the user. It’s so awesome that it’s built into some systems. Drupal considers its robust, built‑in microcaching capabilities so essential that, in Drupal world, microcaching is simply called “caching”.

But the Drupal solution is a bit lacking, and so is any similar solution. The application server does less work, but it’s still Drupal (or, more generally, PHP) that’s having to manage the configuration, implementation, and file serving. By using NGINX for microcaching, the application server is totally offloaded from anything except generating a fresh page at whatever frequency is specified by the microcache. It doesn’t even see the other requests, let alone have to store or retrieve anything for cache hits.

Using NGINX Plus, or other tools, you can monitor your site and see which pages will benefit from microcaching. The following configuration snippet implements a 1‑second caching period for responses with a 200 OK status code.

proxy_cache_path /tmp/cache keys_zone=cache:10m levels=1:2 inactive=600s max_size=100m;
server {
    proxy_cache cache;
    proxy_cache_valid 200 1s;
    # ...
}

Conclusion to Part 1

This first part of our PHP blog post is focused on single‑server solutions, plus caching, which is effective in single‑server implementations – but even more so when a reverse proxy server is in the mix. Part 2 describes the benefits of a reverse proxy server, multiserver implementation around your PHP application.

To try NGINX Plus, start your free 30-day trial today or contact us to discuss your use cases.