APC is one of PHP Opcode Cache on the « market ». It is free and should be bundled inside the next revision of PHP, version 6.

I won’t go deep into how OpCode caches work, you will find a lot of docs, just google for APC, Xcache, eAccelerator… What I can say is that APC (as other caches do) will « save » binary parts of your PHP code into memory and use it when you call for the same PHP function again. This way you save all the PHP file opening, parsing, etc.

Maybe you noticed I said « into memory ». Yes. When you start APC it take a small amount (64 or 32Mo standard) of memory and store the binary parts in it.
This is fine but what if you have a really big website with lots of functions, classes, includes… like when you’re using Typo3 and tons of extensions ?

One thing you won’t come accross often on internet is « How (the hell) do I know if APC is performing well ? »

First, most of the time, the PHP page generation time is halved when you activate APC. This is a good clue. Great.
But what about memory consumption ?

During my tests, I found that APC completly flush the memory if it cannot add a new object. I don’t know if it’s a normal behaviour but it’s what I noted. This is why you have to extensively do your testing and tune the memory size before going to production.

This can be done really easyly with the apc.php page provided in the  APC package. You may not have it if you used some Linux package installer like yum or aptitude. If so, you’ll have to download the APC source from their website and copy apc.php to your web DocumentRoot or wherever you want it. As this is giving sensible informations, I would recomment to put it in a secured place.
We are using Typo3 here so I put the page in the /typo3 folder, which is protected and only accessible by the backend users. This is secure enough for now. Else, use the default login process, user « apc » and password « password ». This can be changed in the page. Disable it if you put the file inside an already protected location.

Browse to your apc.php page and see what happen. First, you’ll get a nasty PHP error if you don’t have APC PHP module enabled. Ensure it is enables in /etc/php.d/apc.conf (on CentOS, can be somewhere else on other distro).

What you see here is, on the left, some metrics and informations of the versions, uptime…
On the right you have the memory utilisation and the cache hit/miss representation. Also, you can see the fragmentation of the memory. You don’t want that but actualy I’m not sure you have a way to reduce it…

The things you need to check are on the left side, in the panel « File Cache Information ».

Hits : how many objects were already in the cache and were used

Miss : how many objects were NOT in the cache, for whatever reason : first request for it, memory full, object can’t be cached…

Cache Full Count : how many times the cache (memory) was full and flushed

This last one is one of the most important and need to be checked first when you website start to slow.

Monitoring

Now you want to monitor this with your Nagios/Centreon or whatever… Well.
I found a « APC monitoring project » out there, APC-PHP-MONITOR on GitHub. This was my starting point as this one is really basic.

As they do, I made a PHP script you have to put in your PHP website somewhere. I used a basic check to ensure only the right IP can access this script. In my case, 10.1.1.88.

  1. <!–?php if ($_SERVER["REMOTE_ADDR"] == "10.1.1.88") { print(serialize(apc_cache_info( »,true))); } ?–>

Then I modified the PHP script so I can use it from Nagios or whatever. I named the file check_php_apc_cache.php. It’s not a superbe PHP script. It’s just dirty working fine for now.

  1. <!–?php <br ?–> # check_php_apc_cache.php
  2. # Modification by Sebastien Prune THOMAS
  3. # v1 – 20111003
  4. # creation
  5. #
  6. ####################################
  7.  
  8. $options = getopt("H:p:w:c:d::");
  9.  
  10. # variables
  11. $output = "OK";
  12. $port=80;
  13. $warning=30;
  14. $critical=10;
  15. $debug = 0;
  16. # code return : 0=OK, 1=WARN, 2=CRIT, 3=UNKNOWN
  17. $retcode=3;
  18.  
  19. if(count($argv) &lt; 2 or $argv[1]=="help" ){ print("usage: php apc_stats.php -H hostname -p [port] -w [warning] -c [critical] [-d]\n"); print(" hostname will be changed in a URL like http://hostname:port/apc_mon.php\n"); print(" port is the HTTP port to use, default to 80\n"); print(" warning : low level of cache use that will raise a warning. default 30\n"); print(" critical : low level of cache use that will raise a critical. default 10\n"); print(" -d enables debug mode\n"); print("\n"); exit; } if (isset($options["H"]) and !is_null($options["H"])) $host = $options["H"]; if (isset($options["p"]) and !is_null($options["p"])) $port = $options["p"]; if (isset($options["w"]) and !is_null($options["w"])) $warning = $options["w"]; if (isset($options["c"]) and !is_null($options["c"])) if (isset($options["d"])) $debug = 1; #ensure critical is lower than warning if ($critical &gt;= $warning) {
  20. print "Error : critical value must be lower than warning\n";
  21. }
  22.  
  23. # get the answer from the php APC page
  24. $url = "http://" . $host . ":" . $port . "/apc_mon.php";
  25. $results = file_get_contents($url) or die("server is not responding");
  26. if ($results) $results = unserialize($results);
  27. else {
  28. print("ERROR/n");
  29. }
  30.  
  31. # debug
  32. if ( $debug == 1)
  33. print_r($results);
  34.  
  35. # compute ratio
  36. if ($results["num_hits"] &gt; 0)
  37. $hit_ratio=($results["num_hits"]/($results["num_hits"]+$results["num_misses"]))*100;
  38. else
  39. $hit_ratio=0;
  40.  
  41. if ($hit_ratio &gt;= $warning){
  42. $output = "OK";
  43. $retcode=0;
  44. }
  45. else if ($critical

You can then create a Nagios check or Template. Mine looks like this :

  1. define command{
  2. command_name check_PHP_APC_cache
  3. command_line php $USER1$/check_php_apc_cache.php -H $HOSTADDRESS$ -p $ARG1$ -w $ARG2$ -c $ARG3$
  4. ;$ARG1$ TCP port (80)
  5. ;$ARG2$ warning level (30)
  6. ;$ARG3$ critical level (10)
  7. }

Finaly, the result :