Category: Development Tools

Is there a speed gain when moving from Apache Mod PHP to Nginx PHP-FPM?

I had a chance to deploy one of my running websites on another virtual machine.
I wanted to improve performance as customers are paying for the product and wanted to give a faster experience.

On the old site I used Apache with PHP mod apache to run the site. On the new site I went with Nginx and PHP-FPM.

The Server Setups

Both websites use the Yii Framework on PHP with a MySQL database. There has been some performance tweaks on the Old Site. The new site I left everything standard.

Old Site:

  • 2GB RAM (free 222MB)
  • CPU(s): 2
  • Site shared - vhosts with a few other sites
  • HTTPS Enabled (letencrypt)
    * PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
  • Server hosted in Nederlands (Testing from South Africa)

New Site:

  • 2GB RAM (Free 1161MB)
  • CPU(s): 2
  • Site dedicated, not other site on server
  • No HTTPS
  • PRETTY_NAME="Ubuntu 18.04.4 LTS"
  • Server hosted in South Africa (Testing from South Africa)

Method

The method for the performance test is as follows.

  1. Enable response time logging in the access logs of both apache and nginx - I wrote a post on this with apache and there are docs online for nginx
  2. Browsing Test - I will browse as a non logged in and logged in user on both sites in isolation. The statistics of response times will be recorded from the user's perspective in the browser and from the log response times.
  3. WebPage Test - I will use Web Page Test to compare both sites for a few pages.
  4. Load Test - I will test concurrent load with locustio
  5. Sitespeed.io Test - Test using sitespeed.io open source sitespeed testing utility

This will not be a scientific comparison - purely anecdotal

Browsing Test

PageNginx + PHP-FPM (ms)Apache + ModPHP (ms)Difference
Home Page1380166020%
Contact Us1060131024%
About Us997128028%
Login (POST)14107550435%
Portfolio (Db intentensive)19206960263%
Calculator946131038%
Chart (TTFB)105348231%

From the chart above it is safe to say that without a shadow of a doubt, the new site is faster.

Naturally the server being much closer helps. Instead of 9354km the new server is about 50km away. The average latency on a ping is 187 ms to the old server and about 12ms to the new one.

WebPage Test

I tested both sites from south africa, here are the screenshots and relevant info below:

speed-test-nginx-php-fpm
Speed Test of the New Nginx PHP-FPM website
speed-test-apache-php
Speed Test of the Old Apache ModPHP website
WebPageTest MetricNginx + PHP-FPMApache + ModPHP
First Byte102895
Speed Index7691660
Document Complete Time38503353
Document Complete Requests3633
Fully Loaded Time47464087
Fully Loaded Requests4846

Surprisingly the new website performed worse (in total). It was faster to first byte but full load was worse. Furthermore no caching and webpagetest does not like that.

WebPageTest MetricNginx + PHP-FPMApache + ModPHP
First Byte126913
Speed Index8001681
Document Complete Time68252989
Document Complete Requests1816
Fully Loaded Time68693215
Fully Loaded Requests1917

The results of this were also pretty annoying. It seems that webpagetest wants me to cache static content, gzip assets and use a CDN. Then it will be happy.

Let me add gzip and static caching to nginx and see.
Just uncomment the gzip section in the default nginx.conf.

After adding updaing it is looking a bit better:

add-gzip-and-static-caching-nginx
After updating the new site enabling gzip compression and browser caching

I then removed the twitter feed and things were better:

Old Site:

New Site:

all-a-web-page-test

Load Test

I created a test to make some GET requests against the server - while not logged in. The test has users spam at 1 a second.

The new site performed as follows

number-of-users-nginx-php-fpm
N umber of users nginx php-fpm
response-times-(ms)-php-fpm-nginx
Response times (ms) php-fpm nginx
total-requests-per-second-nginx-php-fpm
Total requests per second nginx php-fpm

So it can run stably from 80 to 100 RPS.

The old site performed terribly. When I got up to 2 RPS all the other sites monitoring was saying it was down. It was weird that the RPS didn't grow according to users as fast with the old site - perhaps locust knows it couldn't handle that spawn rate.

load-test-apapche-mod-php-total-requests

apache-mod-php-load-test-response-time

user-growth-apache-modphp-old-site-loadstest

Sitespeed.io

To do a more comprehensive test I employed sitespeed.io. I then ran the test against both sites are here are the results...

The Old Mod-PHP and Apache site

sitespeed-io-for-old-apache-mod-php-site

The New PHP-FPM and Nginx site

sitespeed-io-for-new-nginx-php-fpm-site

Update Adding HTTP/2

I was using the default nginx config that is HTTP/1.1, so I updated it to serve with HTTP/2

Now, I have switched over the performance site to be the current site.

how-to-trade-http2-speed-test

  • First bytes is a bit slower 0.192s vs 0.095s on the HTTP/1.1 version
  • Start render is also about 100ms faster at 0.400s vs 0.500s on the HTTP/2 site
  • Document complete and fully loaded is however much faster on HTTP/2 - even with the 100ms handicap. It is about 300ms faster - probably due to HTTP/2 multiplexing of asset acquisition.

Conclusion

Some tests were conclustive - others were still in the balance.
From a load testing and user initial response view - the new site clearly wins. The biggest gain comes more from concurrent users and handling load. Another significant bit was moving the server closer to the users.

The PHP-FPM with Nginx site can handle 40 or more times more load than the other site and has a faster response even with the 200ms handicap.

Next Steps

The next steps to take would be to look at how to maximise performance with nginx and php-fpm

Git Github Quick Reference Tutorial

What is Git Github?

Git Github is distributed version control. It is free and open source, made by Linus Torvalds.

It is different from previous version control techniques:

  • Different users maintain their own repositories.
  • There is no single failure point.
  • Changes are stored as change set.
  • Change sets can be exchanged between repositories.
  • Branches can be used, with fast context switching, to control your working development directory.
  • Much faster than any other version control system.
  • No network access required.

Git is for anyone who wants to track changes, it was designed by developers for developers but anyone wanting to track changes can use it. However Git is no good for:

  • Images
  • Movies
  • Fonts
  • Music
  • Microsoft Office (Word, Excel, etc)
  • PDF

Git and Github Quick Reference

Download Git

go to: http://git-scm.com
download and install with Git Bash

Now open Git Bash and go to the folder where you want a new repository:

Initialise a project Git:

git init

Staging Changes Git:

git add .

Commiting Changes Git:

git commit -m "Message"

How to write commit messages Git:

  • Substitute "Message" above with:
  • A short single line description of your changes or actions
  • Optionally followed by a blank line and a more complete description
  • Each line should be less than 72 characters
  • Present Tense
  • Asterix, Hyphen or '>' as bullets
  • It is not email, no dates or times

View change log Git:

git log

Change log additional commands Git:

#single line per commit
git log --oneline
#show just 3
git log --oneline -3
#since
git log --since="2012-02-20"
#until
git log --until="2 weeks ago"
#author
git log --author="Name"
#Searching
git log --grep="test"
#difference of log
git log -p
#statistics
git log --stat --summary
#email format
git log --format="email" 
#graph
git log --graph
#well formatted (works better on unix)
git log --oneline --graph --all --decorate

Architectural differences between Git and other Version Control Git:

Other version control:

  • Two-tree Architecture
  • Repository -> checkout -> sandbox -> commit -> repository

Git:

  • Three-tree Architecture
  • Repository -> checkout -> sandbox -> staging index -> commit -> repository

Git changes and Checksums Git:

  • When anything is changes in Git, a change set is created.
  • Git converts the change set into a SHA-1 checksum
  • Every change including the change message has a unique checksum
  • Therefore is maintains the integrity of changes

Head in Git:

head is a pointer to the tip of the current branch in the repository

Git Status:

Shows current branch and all untracked files (changes not yet staged)

git status

 Differences in Git:

#working directory
git diff

#staged differences
git diff --

#differences between branches
git diff master..sandbox

Deleting in Git:

#Unstaged
#just delete the file

#staged differences
git rm <filename>
git commit -m "Message Delete"

Renaming in Git:

git add <renamed>
git rm <old file>
git commit -m "Rename Message"

Moving Files in Git:

git mv <onefile.txt> <otherfile.txt>

Converting a Real Project to a repository (Skipping Staging) iwth Git:

git init
#skip add just commit
git commit -a

Undo Unstaged Changes with Git:

git checkout -- <Filename>

Undo Staged Changes with Git:

git reset HEAD <Filename>

Undo Commits with Git:

  • Changing the latest commit forces all other commits down the line to change due to checksums
  • You can change the last commit (at HEAD)
  • Below can be used just to change the last commits message
git commit --amend -m "Amend Message"

Undo Commits of Specific Commit with Git:

git checkout <CommitSHA/Checksum> -- <Filename>
#eg.
git checkout 4d5shd6a -- test.php
#or
git checkout b47hsdf4

#Use: git log to get the SHA/Checksum

Revert to Specific Commit with Git:

#stage and commit
git revert <Commit SHA / Checksum>
#no commit no stage
git revert -n <Commit SHA / Checksum>

Reset in Git:

#USE WITH CAUTION - Changes position of HEAD
git reset --soft
git reset --mixed (default)
git reset --hard
  • Soft: Moves Head Pointer, No staging, No Changes in Working Directory.
  • Mixed: Moves head pointer, changes staging directory to match repository, No Changes in Working Directory.
  • Hard: Changes Staging and Working Directory.

Remove Untracked Files from Working Directory in Git:

#test run
git clean -n
#force to run
git clean -f

Ignoring Files in Git:

#in project/ .gitignore
#if not there create
#basic regs: * ? [aeiou] [0-9]
*.php

Typical things to ignore with .Gitignore:

  • Compiled Source Code
  • packages
  • Compressed Files
  • Logs
  • Databases
  • Operating System Generated Files
  • user-Uploaded Assets (Images, PDF, Videos)

Global Ignores in Git:

#personal preferences specific to OS
git config --global core.excludesfile /users/Username/.gitignore_global

Remove just from Staging index in Git:

git rm --cached <Filename>

Tracking an Empty Directory Git:

#Place a small file in the directory, Git only tracks files not directories

Treeish - what is it with Git:

Treeish: References part of a tree

Can Use:

  • full SHA-1 Hash
  • short SHA-1 Hash (4 to 15 characters)
  • HEAD pointer
  • branch reference
  • parent commit: -HEAD^ or -HEAD~2
  • tree is a directory, blob is a file
git ls-tree <treeish>
git ls-tree HEAD

Branching in Git:

  • Don't use much resources
  • Easy to Create
  • Fast
  • Little space required
  • Ability to try new ideas and test
  • Can create for specific parts of project
  • One working Directory
  • Fast context switching
#check branches
git branch

#new branch
git branch sandbox

#switching between branches
git checkout master

#create and switch branch
git checkout -b master

Checkout Files in Git:

git checkout -- <Filename>

Finding if branch which has all commits within:

git branch --merged

Delete branch in Git:

git branch -d <branchname>

Merging branches with Git:

git merge <branchname>

#fast forwardn is not a true merge
#no fast forward
git merge --no-ff <branchname>
#fast forward only
git merge --ff-only <branchname>

Merge Conflicts in Gits:

  • Go through and decide which changes you want
  • abort merge
  • Resolve Conflicts manually
  • use merge Tool

Avoiding Merge Conflicts with Git:

  • Keep lines short
  • No unnecessary whitespace edits
  • Keep focused do not wander around the project
  • Merge Often

Stashing uncommited Changes in Git:

git stash save "Stash Message"
git stash list
git stash show -p stash{0}

Stash brought into any Repository:

#default puts latest one in
git stash pop
#put specific
git stash pop stash@{2}
#pops but keeps in stash
git stash apply

Delete Items from Stash in Git:

git stash list
git stash drop stash@{1}
#clear stash
git stash clear

Remote Servers and Github:

  • Used as convention Remote not necessarily required
  • Git makes an origin/master referencing remote server

List of remotes:

git remote

Telling Git where the remote repository is, usually Github:

git remote add <name usually origin> <https address or ssl>

Pushing an Existing Repository or github:

#push masterbranch to github
git push -u origin master

Getting a from a Git Github:

#find project on git
#go to folder you want it to reside
git clone <https/ssl of project> <project name>

Pushing changes to Remote or Github:

git push origin master

#if tracking branch
git push

Fetching from remote repository or Github:

git fetch origin
#fetch and merge
git pull origin

Fetching Tips for Github and Remote Servers:

  • Fetch before you work
  • Fetch before you push
  • Fetch often

Delete a remote branch from Github:

#push nothing to origin
git push :<branch>
#new way
git push origin --delete <branchname>

Using an IDE, Git and Github:

many IDE's have this functionality built in to make it much easier

More Info Git Github:

Git Official Tutorial

Git-Github-tutorial

Reconciling a Detached Head with Master/origin