9.2. Scaling the Network
Networks are typically a trivial element to scale out for large applications, as networking technologies and protocols are well designed from the outset for scaling. Regular networking technologies like gigabit Ethernet provide so much bandwidth that web applications will never touch the limits, unless you're doing some very specialized work. Throwing around a lot of huge data can suck up network bandwidth, but if the traffic is running between two nodes on a switched network, then it won't interrupt other traffic.
A simple switched network can support a whole lot of machines. By chaining switches together, we can support thousands of hosts; a Cisco Catalyst 6500 series can support over 550 gigabit Ethernet ports in a single 20U chassis. Above that scale, adding aggregating switches or routers can grow your network to support tens of thousands of hosts. For redundancy, we can connect switches together in loops or meshes and use variations of the spanning tree protocol (Cisco Per VLAN spanning tree (PVST), rapid spanning tree protocol (RSTP), and multiservice protocol transport (MSTP)) to avoid traffic looping around forever.
One big network doesn't suit every application; however, if your application produces a constant stream of noncritical data and occasional bursts of very important data, you'll want the very important data to get through. Because Ethernet makes no QoS guarantees, we can't just dump both kinds of traffic onto one Ethernet segment. If we split the network into two distinct subnets, then we can split the types of traffic, leaving one network to only carry the mission-critical data bursts.
Higher-end switches support the creation of VLANs (virtual LANs), which allow you to arbitrarily carve up the device into multiple distinct networks that don't overlap. Thus, to create two subnets, we don't have to buy an additional device (assuming we have enough free ports) but simply dedicate half of the ports to one network and half to another. For machines that need to talk on both networks, we simply connect a second network card to the second network and give it two IP addresses. Such a process on the host side is called multihoming.
If you need to throw around huge swathes of data, then gigabit Ethernet isn't the last word in high-speed data exchange (or even 10 GbE, if it ever comes out). High capacity dedicated communications channels, such as InfiniBand, allow you to move data between two hosts at even higher speeds. InfiniBand allows a data exchange of up to 100 Gb (a quad-rate 12X link) and can perform seamless RDMA to use memory on a remote machine as if it were local.
9.2.1. Scaling PHP
As PHP has gained more acceptance as a serious language, there's still been a significant camp of people claiming that it can't scale. Clearly, this isn't the casemany of the biggest web applications on the Internet are built using it. The reason that PHP can scale harks back to what scalability really is. The criticisms leveled at PHP are often because of raw performance issues that people mistake for scalability, but we know this isn't the case.
So how does PHP meet the three criteria of a scalable system? The third of the three, maintainability, is easy to understand. Any language can be used to create both easy and hard to maintain code. While some languages lend themselves to being able to write extremely hard to maintain code (Perl and, to an extent, C), very few make it difficult to write maintaible code (with the exception of languages designed to be that way, such as Intercal or Malbolge). By having strict structural and style guidelines and adding a liberal sprinkling of comments, PHP applications can be easy to maintain.
Dataset growth is the second criteria for a scalable system. With a PHP application, the data store is completely decoupled from the processing. We don't care how much data is stored in the databasewe'll still query it in exactly the same way regardless. PHP pushes the responsibility of dataset growth down to the storage layer beneath it, which allows us to scale as small or as large as we like.
The last of the criteria is to allow for traffic growth. PHP easily allows this through what Rasmus Lerdorf (PHP's creator) describes as the shared nothing architecture. PHP acts as stateless as HTTP by pushing all state down to a lower level. A PHP process executes only one request at a time and can't talk to other processes serving other requests. Processes can't remember things between requests. This effectively isolates every request into a sterile silo, not sharing anything between requests or processes. If we need to receive user data from one request and show it in the next request, then we use the data store layer beneath to keep track of it. Because of this separation between processes and requests, we don't actually have to serve subsequent requests using the same server. To handle more requests, we simply add more web servers. They can all service requests simultaneously, sharing no data inter-process. A user's first request can go through one machine, while the next goes through another, and so on. This also allows for seamless failover, assuming we can avoid directing traffic to a dead machine. This is not specific to PHP alone, but is one of the founding principles of REST. With the right application design, this principle can be applied regardless of the implementation language.
There are some cases, however, when this is not true. PHP has several extensions that are not stateless and don't allow you to direct users to any random server. The sessions extension stores user session data locally on disk, requiring a user to be pointed at the same web server for each request. To get around this, we can either use the msession extension that stores session data using a central network daemon or store session data ourselves manually, using either the database or an in-memory cache. Some PHP extensions allow your PHP processes to map a region of shared memory for IPC. Because your processes won't necessarily be on the same machine, IPC as-is is impossible. Instead, we can just push that behavior down to a lower layer, storing shared data in a database or memory cache.
If we avoid using some specific extensions, then PHP meets our three criteria for building a scalable system; it allows traffic growth, allows dataset growth, and can be used to create maintainable systems.
Of course, PHP isn't the only language you can create scalable systems in. Perl, Python, and Ruby are all good candidates for exactly the same reason as PHPthey push responsibility for scaling down to the storage layers by being stateless, and they allow the creation of maintainable code. Other languages and technologies, such as the often-used J2EE, can meet these same criteria, although avoiding shared data and persistent state at the language level are often avoided for large applications. Any reasonable language can be used to build a scalable system.