当前位置: > Linux服务器 > 环境配置 >

使用APC加速PHP

时间:2014-12-01 02:06来源:linux.it.net.cn 作者:IT

原文地址:http://devzone.zend.com/1812/using-apc-with-php/

注:打红色的地方是拿不准的地方,读者有懂的可以随便赐教

Cache Cow

缓存牛(原文Cache Cow,醉了,第一个词就不知道几个意思,只能直译了)

 

If you’ve been around PHP for a while, you’ve probably heard about APC, the Alternative PHP Cache. APC is an opcode cache that can significantly speed up your PHP applications, by caching both PHP code and user variables. Adding APC to an application usually results in improved application response times, reduced server load and happier users.

朋友,想来你已走进PHP的江湖有些时日,那么你应该听说过APC(the Alternative PHP Cache)——可选PHP缓存。APC是一个通过缓存PHP 源码和用户变量来高效显著加速你的PHP程序的opcode缓存。在应用中使用APC,通常伴随着提升程序响应时间、减轻服务器负载和愉悦用户的功效。

In this article, I’ll introduce you to APC, guiding you through the process of installing and configuring it and showing you a few examples of how it works. I’ll also walk you through the APC administrator interface, which lets you view APC performance in real time, and show you how you can use it with the Zend Framework. So come on in, and let’s get started!

本文中,我将向你介绍APC,带领你安装和配置APC,并且一起品尝几个例子来看看她是如何风骚的。同时将共你漫步在APC管理界面中,让你实时欣赏APC的性能,最后再向你秀一下如何使用APC与Zend Framwork一起工作。前话点到即止,来吧!英雄!一起上吧!!!

Getting Started

上,用力!

 

First up, a quick description of what APC is and how it works.

第一炮,先飞快地扫射一下APC是啥,咋干活。

As you probably already know, PHP is an interpreted language (unlike, say, Java or C++). Whenever a client requests a PHP page, the server will read in the source code of the page, compile it into bytecode and then execute it. In the normal scenario, this process is performed on every request…although PHP is so speedy that you probably won’t even notice it!

当然了,你也许早已知晓,PHP是一种解释性语言(异于  Java 或者 C++)。不管客官们几时请求一个PHP页面,服务务器都会把该页面的源码取出编译字节码而后执行。正常情况,都是要走这个流程的——尽管PHP已经快到肉眼无法观测!

If you’re running an application or Web site that has many hundreds or thousands of requests coming in per minute, though, you’re going to want to speed things up as much as possible. And that’s where APC comes in. In the words of its Web site, APC is “a free, open, and robust framework for caching and optimizing PHP intermediate code.” Very simply, APC caches the compiled output of each PHP script run and reuses it for subsequent requests. This reduces the time and processing cycles needed to fully satisfy each request, leading to better performance and lower response times.

如果你的应用或者网站每分钟都有成千上万的流量的话,相信你做梦都想着让一切快到无极。很好,宝贝!APC在向你招手。APC是这么自称的“哥是一个免费、开放、鲁棒的用来缓存和优化PHP中间代码的框架”。前人栽树,后人乘凉。APC把PHP脚本编译后的输出缓存起来以供后来的请求复用和执行。这减少了满足每一个请求所需的时间和处理周期,从而带来更高的性能和更少的响应时间。

Does it work? You bet (there are some benchmarks at the end of the article). And it’s easy to set up as well. To install it, use the pecl command, as shown below:

想知道他是怎么干活的?你觉得呢(文件后面会有一些基准测试程序)。另外APC的安装和配置也很简单,来看看使用 pecl 命令安装实例:

shell> pecl install apc-3.1.4

The PECL installer will now download the source code, compile it and install it to the appropriate location on your system.

执行上述命令后,pecl 安装器将会下载源码,编译并且安装到你系统的相关目录下。

Alternatively, manually download the source code archive (v3.1.4 at this time) and compile it into a loadable PHP module with phpize:

无独有偶,手动下载源码包(撰写本文时版本为3.14),使用phpize编译为PHP可加载模块的方法如下:

shell> cd apc-3.1.4

shell# phpize

shell# ./configure

shell# make

shell# make install

 

This procedure should create a loadable PHP module named apc.so in your PHP extension directory. You should now enable the extension in the php.iniconfiguration file, restart your Web server, and check that the extension is enabled with a quick call to phpinfo():

上面几个步骤将生成一个名为apc.so的可加载模块保存于你的PHP扩展目录下,现在,修改你的PHP配置文件php.ini,重启web服务器,就可以使用phpinfo()秒秒钟看出来扩展是否装上,是否可用。

Digging Deeper

深入!深入!

The APC source code archive includes a script named apc.php. This script serves as the administrator interface for the cache, allowing you to look inside the cache at any time to view usage or inspect cached variables. It’s a good idea to get familiar with how this works before starting to write any code.

APC源码包包含了一个名为”apc.php”的脚本,提供了缓存的管理界面,让我们随时查看使用情况和监测缓存里的变量。在写代码之前,先搞清楚他的工作原理绝逼是美妙的。

To begin, extract the script from the source code archive and copy it to your Web server document root. Then, open it in a text editor and set the administrator password (you’ll find it near the top of the script). Once you’ve got that done, try accessing the script through your Web browser. You should see something like this:

来吧,先把这个脚本从源码包里解压出来放到你的web目录下,用文本编辑器打开设置一下密码(在脚本的前面几行)。完事了之后就可以通过浏览器来访问这个脚本了,下面这个界面将登场了:

 

 

As you can see, the script provides a birds-eye view of the current cache status, displaying general cache information, usage and statistics on hits and misses. The information is presented for both the system cache (which handles opcodes) and the user cache (which handles user variables). You’ll also see some interesting data derived from these statistics, such as the number of cache requests per second, the hit rate and miss rate.

正如你所见,这个页面提供一个对当前系统缓存状态、常用缓存信息、缓存使用情况和缓存成功及失败的统计的概览。这些信息同时展示系统缓存(保存着opcodes)和用户缓存(保存着用户变量)。从其余的统计信息中,我们还可以找到一些有趣的数据。例如缓存每秒请求数量,命中和的丢失的概率。

This information is useful to understand how well your cache is working, and identify areas that are under-optimized. In particular, note the cache full count value,which indicates how often the cache has filled up; if this is a high number, it’s an indication of high cache churn and suggests that you should perhaps assign more memory to the cache.

从这些信息中可以了解到你的缓存工作的优良情况,还能看出哪些部分处于优化状态。特别要注意缓存完整计数值它表明了缓存多长时间被填充一次;如果这是一个很大的数值,表明有大量的缓存流失,建议你可能是分配更多内存给缓存用。

 

The “System Cache Entries” tab lists the PHP scripts that are currently being cached, together with their filename, size and number of hits. Note that APC will automatically cache script opcode.

“System Cache Entries” 标签列举了当前被缓存的PHP脚本,包括文件名,大小和缓存被请求次数。注意:APC会自动缓存脚本的opcode。

The “User Cache Entries” tab lists user variables that have been stored in the cache, together with their identifier, size, and creation and modification times. You can select any of these entries to look inside the cache entry and see what it contains. Note that user cache entries must be manually created by a PHP script – you’ll see how on the next page.

“User Cache Entries” 标签页列举了缓存的用户数据,包含了ID、大小、创建和修改时间。你可以选择任何一项去查看整个缓存整体里有些什么内容。但是这些用户数据缓存是必须通过PHP手动创建——下面我们就来讨论这个问题。

 

Remember that you can clear the opcode cache or the user cache at any time with the “Clear cache” buttons at the top right corner of the page.

记得可以随时通过右上的“Clear cache”按钮清除opcode 缓存和用户数据缓存。

A Galaxy Far, Far Away

一个很远很远的星系

Now that you have a better idea of how APC works, let’s write some code. Caching user variables in APC is mainly done through the apc_add() and apc_fetch() methods, which allow variable to added to, and retrieved from, the cache respectively. Here’s a simple example that illustrates:

现在你对APC的工作方式有了一个清晰的概念了,可以开始写点代码了。在APC中缓存用户数据主要通过apc_add() 和 apc_fetch() 两个方法,前者用于添加,后者用于提取。下面我们来举一个简单的示例:

 

  1. <?php  
  2. if ($quote = apc_fetch('starwars')) {  
  3.   echo $quote;  
  4.   echo " [cached]";  
  5. else {   
  6.   $quote = "Do, or do not. There is no try. -- Yoda, Star Wars";    
  7.   echo $quote;   
  8.   apc_add('starwars'$quote, 120);  
  9. }  
  10. ?>  

 

 

Now, the first time, you run this script, the page will be generated from scratch and you’ll see something like this:

现在,运行这个脚本,这个页面将从什么都没有开始生成,你将看到如下画面:

 

Next, try refreshing the page. This time, the output will be retrieved from the cache:

现在试着刷新页面,这一次,输出的变量将从缓存中取出来:

 

The business logic to use a cache is fairly simple. The first step is to check if the required data already exists in the cache. If it doesn’t, it should be generated from the original data source, and a copy saved to the cache for future use. If it does, you can use it right away – write it to a file, pipe it to an external program or output it to the client.

使用缓存的业务逻辑相当简单。第一步就是检查一下需要的数据是否已经存储在缓存中,如果没有,从原生数据中生成,并且保存一份到缓存中以备后用;如果有,那就可以直接用上了——写文件,从管道输出给外部程序或者直接输出到客户端。

In the previous example, checking whether the data already exists in the cache is accomplished with the apc_fetch() method, while writing a fresh snapshot to the cache is done with the apc_add() method. Notice that both methods require an ID; this ID uniquely identifies the object in the cache and serves as a key for saving and retrieving cache data. The apc_add() method additionally lets you specify a duration (in seconds) for which the cache is valid.

上一个例子中,检查数据缓存与否是通过apc_fetch()这个方法来完成的,相应的写一个新的快照到缓存中是通过apc_add()方法来完成的。需要注意的是,这两个方法都需要一个 ID,用来唯一标识一个存储在缓存中的对象,同时作为存取一个数据的键值。apc_add()这个方法还可以任你设置一个键的超时时间(以秒为单位)。

Take a look in the administrator interface, and you should see your cached data, together with statistics on cache hits and misses:

再瞅一眼管理界面,应该能看到缓存的数据了,还有缓存成功与失败的统计:

 

A-rray of Sunshine

数组也有春天

 

In addition to caching strings, APC also allows you to cache arrays, objects, functions and references. Consider the following example, which caches an array of values:

天呐!你以为APC只能缓存字符串吗?no!她还能缓存数组、对象、方法和引用 !!!来思考思考下面这个示例吧,它就缓存了一个数组的值:

 

  1. <?php  
  2. // if available, use cached array  
  3. // if not, create and cache array  
  4. if ($data = apc_fetch('heroes')) {  
  5.   echo 'Cached data: ';  
  6. else {   
  7.   $data = array('Spiderman''Superman''Green Lantern''Human Torch');  
  8.   apc_add('heroes'$data, 120);  
  9. }  
  10. echo $data[1];  // Superman  
  11. ?>  

 

 

You can also cache nested or multi-dimensional arrays, as shown below:

你还可以缓存多维数组哦,看哥给你秀下:

 

  1. <?php  
  2. // if available, use cached data  
  3. // 如果缓存里有数据,就用缓存里的数据  
  4. // if not, create and cache nested array  
  5. // 如果没有,创建一个多维数组,并且缓存起来  
  6. if ($data = apc_fetch('config')) {  
  7.   echo 'Cached data: ';  
  8. else {  
  9.   $data = array(  
  10.     'site1' => array(  
  11.       'smtp' => array(  
  12.         'host' => '192.168.0.1',  
  13.         'user' => 'user1',  
  14.         'pass' => 'guessme'  
  15.       )  
  16.     ),  
  17.     'site2' => array(  
  18.       'smtp' => array(  
  19.         'host' => '192.168.10.10',  
  20.         'user' => 'user2',  
  21.         'pass' => 's3cr3t'  
  22.       )  
  23.     ),      
  24.   );  
  25.   apc_add('config'$data, 120);  
  26. }  
  27.   
  28. // display data  
  29. echo $data['site2']['smtp']['pass']; // s3cr3t  
  30. ?>  


 

An Object Lesson

下一课:缓存对象

In addition to caching arrays, APC also allows you to store objects in the cache. To illustrate, consider the next example, which initializes a simple User object, stores it in the cache, and then retrieves it back from the cache:

除了缓存数组,APC还能缓存对象。为了举例说明,思考一下下面这个示例:初始化一个User对象,你保存到缓存中,再从缓存中取出使用。

 

  1. <?php  
  2. class User {  
  3.     
  4.   private $name;  
  5.   private $location;  
  6.     
  7.   function setName($value) {  
  8.     $this->name = $value;  
  9.   }  
  10.     
  11.   function setLocation($value) {  
  12.     $this->location = $value;  
  13.   }  
  14.     
  15.   function getName() {  
  16.     return $this->name;  
  17.   }  
  18.     
  19.   function getLocation() {  
  20.     return $this->location;  
  21.   }  
  22. }  
  23.   
  24.   
  25. if (apc_exists('user')) {  
  26.   $obj = apc_fetch('user');  
  27.   echo "Cached data: ";    
  28. else {  
  29.   $obj = new User;  
  30.   $obj->setName('John');  
  31.   $obj->setLocation('London');  
  32.   apc_add('user'$obj, 120);  
  33. }  
  34.   
  35. echo 'My name is ' . $obj->getName() . ' and I live in ' . $obj->getLocation();  
  36. ?>  


 

Here’s what the output looks like:

能看来这样的输出:

 

第一次加载页面:

 

第二次加载页面:

 

Another approach to arrive at the same result is to serialize the object into a string, and then store the string in the cache instead of the object. Here’s what that would look like:

另一个异曲同工的方法是把对象序列化为一个字符串之后保存到缓存中,而不是直接缓存对象,示例如下:

 

  1. <?php  
  2. class User {  
  3.     
  4.   private $name;  
  5.   private $location;  
  6.     
  7.   function setName($value) {  
  8.     $this->name = $value;  
  9.   }  
  10.     
  11.   function setLocation($value) {  
  12.     $this->location = $value;  
  13.   }  
  14.     
  15.   function getName() {  
  16.     return $this->name;  
  17.   }  
  18.     
  19.   function getLocation() {  
  20.     return $this->location;  
  21.   }  
  22. }  
  23.   
  24.   
  25. if ($str = apc_fetch('user')) {  
  26.   $obj = unserialize($str);  
  27.   echo "Cached data: ";    
  28. else {   
  29.   $obj = new User;  
  30.   $obj->setName('John');  
  31.   $obj->setLocation('London');  
  32.   $str = serialize($obj);  
  33.   apc_add('user'$str, 120);  
  34. }  
  35. echo 'My name is ' . $obj->getName() . ' and I live in ' . $obj->getLocation();  
  36. ?>  


 

Getting Closure

缓存闭包

You can also use APC to cache references and (with a little tweaking) anonymous functions. Let’s take a look:

你也可以使用APC来缓存引用和匿名函数,看下面:

 

  1. <?php  
  2. class Calendar {  
  3.   public $year = 2001;  
  4.   public function &getYear() {  
  5.     return $this->year;  
  6.   }  
  7. }  
  8.   
  9. $obj = new Calendar;  
  10. $a = &$obj->getYear();  // 2001  
  11. $obj->year = 2010;      // 2010  
  12. apc_add('ref'$a, 60);  
  13. $ref = apc_fetch('ref');  
  14. $ref++;                   
  15. echo $ref;              // 2011  
  16. ?>  


 

Anonymous functions or closures, new in PHP 5.3, offer an easy way to define functions “on the fly”. By default, however, closures cannot be cached with APC, as the Closure class does not implement serialization. Here’s a simple example that illustrates the problem:

匿名函数,或称为闭包,是 PHP5.3 的新特性,提供一种简单的悬空的方式来定义函数。默认情况下,APC无论如何是不能缓存闭包的,因为闭包没有实现序列化。这儿有一个简单的示例说明该问题:

 

  1. <?php  
  2. // check if closure exists in cache  
  3. // if yes, retrieve and use  
  4. // if not, define and add to cache  
  5. if (!apc_exists('area')) {  
  6.   // simple closure  
  7.   // calculates area from length and width  
  8.   $area = function($length$width) {  
  9.       return $length * $width;  
  10.   };  
  11.   apc_store('area'$area);  
  12.   echo 'Added closure to cache.';    
  13. else {  
  14.   $func = apc_fetch('area');  
  15.   echo 'Retrieved closure from cache. ';  
  16.   echo 'The area of a 6x5 polygon is: ' . $func(6,5);    
  17. }  
  18. ?>  


 

When you try accessing this script, you’ll see an error about serialization, as shown below:

执行这个脚本,你会看到下面这个关于序列化的错误:

FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed'

 

What’s the solution? Well, Jeremy Lindblom has extended the Closure class and created a custom SuperClosure class that supports both serialization and reflection. If you implement your closure using this class, you will be able to cache it. Here’s a revision of the previous script that demonstrates:

有什么解决办法呢?有的,Jeremy Lindblom 扩展了增长包类并且创建了 a custom SuperClosure class 来同时支持序列化和反射。如果你是使用这个类实现的闭包,那么就可以缓存它了。下面是对上文脚本的改进:

 

  1. <?php  
  2. // include SuperClosure class  
  3. include 'SuperClosure.class.php';  
  4.   
  5. // check if closure exists in cache  
  6. // if yes, retrieve and use  
  7. // if not, define and add to cache  
  8. if (!apc_exists('area')) {  
  9.   // simple closure  
  10.   // calculates area given length and width  
  11.   $area = new SuperClosure(  
  12.     function($length$width) {  
  13.       return $length * $width;  
  14.     }  
  15.   );  
  16.   apc_store('area'$area);  
  17.   echo 'Added closure to cache.';    
  18. else {  
  19.   $func = apc_fetch('area');  
  20.   echo 'Retrieved closure from cache. ';  
  21.   echo 'The area of a 6x5 polygon is: ' . $func(6,5);    
  22. }  
  23. ?>  


 

Here’s what the output looks like:

显示如下:

第一次浏览:

第二次浏览:

Note that these examples use apc_store() instead of apc_add(). In most cases, you can use these two functions interchangeably. The primary difference lies in how they behave when you attempt to store a value using an identifier that already exists in the cache: apc_add() will return false, while apc_store()will overwrite the previous value with the new value.

注意,这些实例使用apc_store() 代替 apc_add()。大多数情况下,你可以互换着这两个函数使用。主要的不同之处在于,当你使用一个缓存中已有的ID来存储数据时:apc_add()会返回 false,而 apc_store() 会使用新的值覆盖之前的值。

You can download the SuperClosure class definition from Jeremy Lindblom’s Github account.

你可以从 Jeremy Lindblom’s Github 账户上下载SuperClosure 这个类的定义。(注:刚浏览了下这个页面不存在,不过还可以从http://www.htmlist.com/development/extending-php-5-3-closures-with-serialization-and-reflection/上面直接拷贝一份使用)。

Utility Belt

实用技能

The APC extension also comes with a few other methods of note. For example, there’s the apc_inc() and apc_dec() methods, which can be used to increment or decrement cached values respectively:

APC扩展还有一些其余的方法和备注。例如:apc_inc()  和 apc_dec() 方法分别作用缓存值的自增和自减。

 

  1. <?php  
  2. // store a value  
  3. apc_store('a', 20);  
  4.   
  5. // increment and decrement stored value  
  6. apc_inc('a');         // 21  
  7. apc_inc('a');         // 22  
  8. apc_inc('a');         // 23  
  9. apc_dec('a');         // 22  
  10.   
  11. // retrieve final value  
  12. echo apc_fetch('a');  // 22  
  13. ?>  


 

The apc_bin_dump() method dumps the current contents of the cache in binary form, while the apc_bin_load() method loads a binary dump into the cache. Consider the following example, which illustrates:

apc_bin_dump()方法从缓存中把当前内容导出为二进制形式,而apc_bin_load() 方法则反之。仔细考虑以述实例:

 

  1. <?php  
  2. // clear cache  
  3. apc_clear_cache();  
  4. apc_clear_cache('user');  
  5.   
  6. // store some values  
  7. apc_store('a', 20001);  
  8. apc_store('b', 7494);  
  9.   
  10. // dump cache in binary form  
  11. $dump = apc_bin_dump();  
  12.   
  13. // clear cache  
  14. apc_clear_cache();  
  15. apc_clear_cache('user');  
  16.   
  17. // try accessing a stored value  
  18. if (apc_fetch('a')) {  
  19.   echo apc_fetch('a');  
  20. else {  
  21.   echo 'Nothing in cache';   
  22. }  
  23. // reload cache from binary dump  
  24. apc_bin_load($dump);  
  25.   
  26. // try accessing a stored value  
  27. if (apc_fetch('a')) {  
  28.   echo apc_fetch('a');     // 20001  
  29. else {  
  30.   echo 'Nothing in cache';   
  31. }  
  32. ?>  


 

The apc_clear_cache() method can be used to clear the opcode cache or the user cache:

apc_clear_cache() 方法可以用来清除opcode 缓存或者用户缓存:

 

  1. <?php  
  2. // clear opcode cache  
  3. apc_clear_cache();  
  4.   
  5. // clear user cache  
  6. apc_clear_cache('user');  
  7. ?>  

 

 

The apc_cache_info() method presents information on current cache status and memory allocation:

apc_cache_info() 方法能够提供当前缓存状态和内存分配信息

 

  1. <?php  
  2. print_r(apc_cache_info());  
  3. ?>  


 

Here’s what the output looks like:

执行上述脚本能看到:

Tweet Tweet

推倒!推倒!

With all this background information at hand, let’s try APC with a real-world example. This next script uses APC to cache the result of a Twitter search:

Get 到这些新技能之后,让我们来现实世界里走你一个。接下来这个脚本用APC来缓存推特的搜索结果 :

 

  1. <html>  
  2.   <head>  
  3.     <style type="text/css">  
  4.       div.outer {  
  5.       border-bottom: dashed orange 1px;  
  6.       padding: 4px;  
  7.       clear: both;  
  8.       height: 50px;  
  9.       }          
  10.       div.img {  
  11.         float:left;  
  12.         padding-right: 2px;  
  13.       }  
  14.       span.attrib {  
  15.         font-style: italic;  
  16.       }  
  17.     </style>    
  18.   </head>    
  19.   <body>  
  20.     <h2>Twitter Search</h2>  
  21.     <form action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" method="post">  
  22.     Search term: <input type="text" name="q" />  
  23.     <input type="submit" name="submit" />  
  24.     </form>     
  25. <?php  
  26.     // if form submitted  
  27.     if (isset($_POST['submit'])):  
  28.       // sanitize query terms  
  29.       $q = strip_tags($_POST['q']);  
  30.         
  31.       // generate cache id from query term  
  32.       $id = md5($q);  
  33.         
  34.       // check if this search already exists in cache  
  35.       // use if yes, generate fresh results and add to cache if no  
  36.       if (apc_exists($id)) {  
  37.         $records = apc_fetch($id);          
  38.       } else {  
  39.         // search Twitter for query term  
  40.         $result = simplexml_load_file("http://search.twitter.com/search.atom?q=$q&lang=en");  
  41.         
  42.         // process Atom feed of search results  
  43.         $records = array();  
  44.         foreach ($result->entry as $entry) {  
  45.           $item['image'] = (string)$entry->link[1]['href'];   
  46.           $item['owner'] = (string)$entry->author->name;  
  47.           $item['uri'] = (string)$entry->author->uri;  
  48.           $item['tweet'] = (string)$entry->content;  
  49.           $item['time'] = date('d M Y, h:i'strtotime($entry->published));   
  50.           $records[] = $item;  
  51.         }  
  52.           
  53.         // cache for 5 minutes  
  54.         apc_store($id$records, 300);  
  55.       }      
  56.         
  57.       // display search results  
  58. ?>  
  59.     <h2>Twitter Search Results for '<?php echo $q; ?>'</h2>  
  60.       <?php foreach ($records as $r): ?>  
  61.       <div class="outer">  
  62.         <div class="img"><img width=48" height="48" src="<?php echo $r['image']; ?>" /></div>  
  63.         <div><?php echo $r['tweet']; ?><br/>   
  64.         <span class="attrib">By <a href="<?php echo $r['uri']; ?>"><?php echo $r['owner']; ?></a>   
  65.         on <?php echo $r['time']; ?></span></div>  
  66.       </div>   
  67.       <?php endforeach; ?>  
  68.     <?php endif; ?>  
  69.   </body>  
  70. </html>     


 

Despite its length, this is actually a very simple script. It begins by creating a search form for the user to enter search terms into. Once this form is submitted, it connects to the Twitter Search API, retrieves an Atom-formatted list of search results matching the search term, process the Atom feed and render the final output as an HTML table. The results of the search are cached for five minutes, so that they can be used for subsequent searches containing the same search terms. Notice that each search query is assigned a unique identifier in the APC cache, by using its MD5 signature as key.

尽管不算长(估计是这意思),但确实是一个简单的脚本。开始处创建了一个表单给用户输入需要搜索的词条。一旦表单提交之后,连接推特的搜索接口,检索出Atom 格式化过的匹配搜索条目的结果列表,处理 Atom 格式提取出最终数据输出为html表格渲染到页面上。这些结果将被缓存五分钟,以备后续有相同搜索条止的请求使用。注意每一次搜索条件查询都会分配到通过md5签名成键作为唯一的APC缓存ID。

You will realize that there are two levels of caching in this script. First, APC’s opcode cache is automatically caching the compiled bytecode of the script, and using this cached bytecode for subsequent requests instead of recompiling it a new. Second, APC’s user cache is caching the results of each Twitter search, and reusing these results (instead of connecting to Twitter afresh) for subsequent searches containing the same query terms. As a result, subsequent searches for the same term will be served from the cache, leading to a noticeable reduction in load time (try it for yourself and see).

你会发现在这个脚本中做了两层缓存。首先,APC的opcode缓存是自己缓存脚本的编译过的字节码的,并且在后续的请求中使用这些字节码而不是重新编译一份。其次,APC的用户缓存是缓存推特的搜索结果,然而在后续有相同检索条目的请求中使用这些结果(而不是重新连接推特)。这就能使得后续相同的搜索服务于缓存,为我们赢来更少的加载时间(请知行合一)。

Here’s an example of what the output looks like:

请看示例:(刚刚看了下推特的这个api已经停用了,就不管他了)

In The Frame

框架中使用

 

If you’re a fan of the Zend Framework, you’ll be happy to hear thatZend_Cache comes with built-in support for APC, allowing you to begin using it out of the box. To illustrate, consider the following Zend Framework controller, which revises the previous example into a Zend Framework controller:

如果你是Zend Framework 迷。那么你将会喜闻乐见Zend_Cache 已经内建支持APC,你可以开始使用它原装的了。好好研究下面这个使用Zend Framework 控制层的实例,我们将用Zend Framwork 控制层来改进上一个例子的实现向你阐明APC 在Zend Framework 中的使用:

 

  1. <?php  
  2. class IndexController extends Zend_Controller_Action  
  3. {  
  4.     public function indexAction()  
  5.     {  
  6.         // action body  
  7.     }  
  8.   
  9.     public function searchAction()  
  10.     {  
  11.       // initialize cache  
  12.       $cache = Zend_Cache::factory( 'Core',   
  13.                                     'APC',   
  14.                                     array('lifeTime' => 300, 'automatic_serialization' => true));  
  15.   
  16.       // create form and attach to view                                            
  17.       $form = new SearchForm();  
  18.       $this->view->form = $form;         
  19.         
  20.       // validate input        
  21.       if ($this->getRequest()->isPost()) {  
  22.         if ($form->isValid($this->getRequest()->getPost())) {  
  23.           // get sanitized input  
  24.           $values = $form->getValues();          
  25.             
  26.           // calculate MD5 hash  
  27.           $id = md5($values['q']);  
  28.             
  29.           // look for records in cache  
  30.           if (!$records = $cache->load($id) ){                         
  31.             // if not present in cache  
  32.             // search Twitter for query term  
  33.             $result = simplexml_load_file("http://search.twitter.com/search.atom?q=" . $values['q'] . "&lang=en");  
  34.               
  35.             // process Atom feed of search results  
  36.             $records = array();  
  37.             foreach ($result->entry as $entry) {  
  38.               $item['image'] = (string)$entry->link[1]['href'];   
  39.               $item['owner'] = (string)$entry->author->name;  
  40.               $item['uri'] = (string)$entry->author->uri;  
  41.               $item['tweet'] = (string)$entry->content;  
  42.               $item['time'] = date('d M Y, h:i'strtotime($entry->published));   
  43.               $records[] = $item;  
  44.             }  
  45.               
  46.             // save to cache  
  47.             $cache->save($records$id);  
  48.           }  
  49.             
  50.           // assign results to view  
  51.           $this->view->records = $records;  
  52.           $this->view->q = $values['q'];  
  53.         }             
  54.       }         
  55.     }  
  56. }  
  57.   
  58. // search form  
  59. class SearchForm extends Zend_Form  
  60. {  
  61.   public function init()  
  62.   {  
  63.     // initialize form  
  64.     $this->setMethod('post');  
  65.            
  66.     // create text input for search term  
  67.     $q = new Zend_Form_Element_Text('q');  
  68.     $q->setLabel('Search term:')  
  69.          ->setOptions(array('size' => '35'))  
  70.          ->setRequired(true)  
  71.          ->addValidator('NotEmpty', true)  
  72.          ->addFilter('HTMLEntities')              
  73.          ->addFilter('StringTrim');              
  74.       
  75.     // create submit button  
  76.     $submit = new Zend_Form_Element_Submit('submit');  
  77.     $submit->setLabel('Search');  
  78.                   
  79.     // attach elements to form  
  80.     $this->addElement($q)  
  81.          ->addElement($submit);  
  82.   }  
  83. }?>  

 

 

Here, the searchAction() method first sets up the Zend_Cache instance, with the Core frontend and the APC backend. The form object, which extends Zend_Form, is then added to the view, together with all necessary validators and filters, and the view is rendered.

本例中,searchAction() 方法首先生成一个以Core为前端和以APC为后端的Zend_Cache实例,继承自Zend_Form的表单对象,添加到页面中,还带有验证和过滤的功能,接着渲染页面。

When the user submits the form, control transfers back to the action controller, which checks the input and retrieves the filtered values. It then checks the cache to see if a search result already exists for this search term, and uses it if available; if not, it connects to the Twitter Search API, retrieves a result set, and saves it to the cache for future use. The results are then rendered through the view script. On subsequent searches for the same term, the cached result set will be used, producing a much faster response.

用户提交表单后,控制转移到后面的控制层,用以验证输入和提取过滤后的内容。接下来检查一下缓存中是否有相同搜索条目的存储,如果有取出使用,如果没有,连接推特的搜索API,取出结果集,并且保存一份到缓存中以备后用。然后通过脚本把结果渲染到页面上。后来的搜索如果是相同的词条,这些缓存里的结果集就可以用上了,这就大大提升了响应速度。

Here’s the code for the view script:

下面是页面代码:

 

  1. <style type="text/css">  
  2.   div.outer {  
  3.   border-bottom: dashed orange 1px;  
  4.   padding: 4px;  
  5.   clear: both;  
  6.   height: 50px;  
  7.   }          
  8.   div.img {  
  9.     float:left;  
  10.     padding-right: 2px;  
  11.   }  
  12.   span.attrib {  
  13.     font-style: italic;  
  14.   }  
  15. </style>  
  16. <h2>Twitter Search</h2  
  17. <?php echo $this->form; ?>  
  18.   
  19. <?php if ($this->records): ?>  
  20.   <h2>Twitter Search Results for '<?php echo $this->q; ?>'</h2>  
  21.   <?php foreach ($this->records as $r): ?>  
  22.   <div class="outer">  
  23.     <div class="img"><img width=48" height="48" src="<?php echo $r['image']; ?>" /></div>  
  24.     <div><?php echo $r['tweet']; ?><br/>   
  25.     <span class="attrib">By <a href="<?php echo $r['uri']; ?>"><?php echo $r['owner']; ?></a>   
  26.     on <?php echo $r['time']; ?></span></div>  
  27.   </div>   
  28.   <?php endforeach; ?>  
  29. <?php endif; ?>  


 

And here’s a sample of the output:

渲染结果:(由于推特这么接口关了,此处省略一张图)

 

The Need For Speed

速之所需

 

At this point, there’s only one question left to answer: does APC’s opcode caching really deliver the goods and produce a verifiable increase in performance?

到了这个点上,还有最后一个问题了:APC 的opcode 缓存是否真能不负众望,为我们带来可见的性能上的提升?

A good way to test this is by benchmarking a PHP script with and without APC, and evaluating the performance differential if any. ApacheBench (ab) is my tool of choice for this test, and my testbed will be the default welcome page of a new Zend Framework project. You can create this by installing the Zend Framework and then using the zf command-line tool to initialize a new, empty project, like this:

最好的办法就是对同一个脚本使用和不使用APC做基准测试,评估所有性能的差异。我选择ApacheBench(ab)作为测试工具,Zend Framework 项目默认欢迎页为测试脚本。你安装好 Zend Framework 之后,你可以使用它的命令行工具zf来初始化一个新的空项目,例如:

shell> zf create project example

Now, turn off APC, by   the extension in your php.ini configuration file and restarting the Web server. Then, use ab to benchmark the application welcome page by sending it 1000 requests with a concurrency level of 5, as follows:

现在,通过配置php.ini 来禁用APC并且重启web服务器。然后,使用ab做一个基准测试:向该欢迎页做1000次请求,并发级别为5,如下:

 

  1. shell> ab -n 1000 -c 5 http://example.localhost/default/index/index  

 

On my development system, this produces output like the following:

在我的开发系统上,这个命令产生的结果如下:

 

  1. This is ApacheBench, Version 2.3 <$Revision: 655654 $>  
  2. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
  3. Licensed to The Apache Software Foundation, http://www.apache.org/  
  4.   
  5. Benchmarking localhost (be patient)  
  6. Completed 100 requests  
  7. Completed 200 requests  
  8. Completed 300 requests  
  9. Completed 400 requests  
  10. Completed 500 requests  
  11. Completed 600 requests  
  12. Completed 700 requests  
  13. Completed 800 requests  
  14. Completed 900 requests  
  15. Completed 1000 requests  
  16. Finished 1000 requests  
  17.   
  18.   
  19. Server Software:        nginx/1.5.3  
  20. Server Hostname:        localhost  
  21. Server Port:            1234  
  22.   
  23. Document Path:          /php/apc/exam12/public/index.php  
  24. Document Length:        1042 bytes  
  25.   
  26. Concurrency Level:      5  
  27. Time taken for tests:   10.206 seconds  
  28. Complete requests:      1000  
  29. Failed requests:        0  
  30. Write errors:           0  
  31. Total transferred:      1189000 bytes  
  32. HTML transferred:       1042000 bytes  
  33. Requests per second:    97.98 [#/sec] (mean)  
  34. Time per request:       51.031 [ms] (mean)  
  35. Time per request:       10.206 [ms] (mean, across all concurrent requests)  
  36. Transfer rate:          113.77 [Kbytes/sec] received  
  37.   
  38. Connection Times (ms)  
  39.               min  mean[+/-sd] median   max  
  40. Connect:        0    1   1.3      0       9  
  41. Processing:    31   50   8.4     48      96  
  42. Waiting:       31   50   8.4     48      96  
  43. Total:         31   51   8.3     49      96  
  44.   
  45. Percentage of the requests served within a certain time (ms)  
  46.   50%     49  
  47.   66%     52  
  48.   75%     54  
  49.   80%     55  
  50.   90%     63  
  51.   95%     69  
  52.   98%     74  
  53.   99%     79  
  54.  100%     96 (longest request)  

 


The main numbers to look at here are the requests per second and the average time per request. The lower the average time per request, the better the performance. Similarly, the greater the number of requests served, the better the performance.

主要关注的数据是requests per second(每秒处理次数) 和 the average time per request(每个请求平均用时)。平时请求时长越短,性能越好。同理,每秒处理次数越大,性能越好。

Next, re-enable APC, restart the Web server and try the test again. On my development system, this produces output like the following:

下面,打开APC,重启WEB服务器,再试一次,我的开发机上效果是这样子的:

 

  1. This is ApacheBench, Version 2.3 <$Revision: 655654 $>  
  2. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
  3. Licensed to The Apache Software Foundation, http://www.apache.org/  
  4.   
  5. Benchmarking localhost (be patient)  
  6. Completed 100 requests  
  7. Completed 200 requests  
  8. Completed 300 requests  
  9. Completed 400 requests  
  10. Completed 500 requests  
  11. Completed 600 requests  
  12. Completed 700 requests  
  13. Completed 800 requests  
  14. Completed 900 requests  
  15. Completed 1000 requests  
  16. Finished 1000 requests  
  17.   
  18.   
  19. Server Software:        nginx/1.5.3  
  20. Server Hostname:        localhost  
  21. Server Port:            1234  
  22.   
  23. Document Path:          /php/apc/exam12/public/index.php  
  24. Document Length:        0 bytes  
  25.   
  26. Concurrency Level:      5  
  27. Time taken for tests:   3.917 seconds  
  28. Complete requests:      1000  
  29. Failed requests:        996  
  30.    (Connect: 0, Receive: 0, Length: 996, Exceptions: 0)  
  31. Write errors:           0  
  32. Non-2xx responses:      4  
  33. Total transferred:      1184908 bytes  
  34. HTML transferred:       1037832 bytes  
  35. Requests per second:    255.32 [#/sec] (mean)  
  36. Time per request:       19.583 [ms] (mean)  
  37. Time per request:       3.917 [ms] (mean, across all concurrent requests)  
  38. Transfer rate:          295.44 [Kbytes/sec] received  
  39.   
  40. Connection Times (ms)  
  41.               min  mean[+/-sd] median   max  
  42. Connect:        0    0   0.7      0       7  
  43. Processing:     4   19   5.2     18      42  
  44. Waiting:        4   19   5.2     18      42  
  45. Total:          5   20   5.2     18      43  
  46.   
  47. Percentage of the requests served within a certain time (ms)  
  48.   50%     18  
  49.   66%     22  
  50.   75%     23  
  51.   80%     24  
  52.   90%     26  
  53.   95%     28  
  54.   98%     33  
  55.   99%     36  
  56.   
  57.  100%     43 (longest request)  

 

 

As you can see, enabling APC has resulted in an almost 185% increase in performance, with the server now being able to handle 71 requests per second (up from 25 earlier) and the average time per request coming down to 69 ms (from 194 ms earlier).

看见没有!开启APC的结果就是提升200%多的性能!看看吧,现在每秒请求次数和平时请求时长分别优化了多少?自!已!!算!!!

The above test was run with APC’s default settings. However, APC comes with a number of configuration settings that you can tweak further to squeeze even better performance from it. Here are some of the important ones:

上述测试都是基于APC默认配置的。但是,APC是有着大把专属的配置的,你可以慢慢调整来压榨出更高的性能。现在先来看看几个比较重置的配置项:

  • ‘apc.shm_size’ controls the size of the APC memory cache;
  • ‘apc.shm_size’ 控制APC缓存的容量;
  • ‘apc.stat’ controls whether APC checks each script to see if it has been modified and needs to be recompiled and recached;
  • ‘apc.stat’ 控制APC是否挨个脚本检查变更以否和是否需要重新编译重新缓存;
  • ‘apc.optimization’ determines the degree of optimization to apply;
  • ‘apc.optimization’ 决定应用的优化程度;
  • ‘apc.filters’ specifies which files should be cached;
  • ‘apc.filters’ 指定哪些文件需要缓存;
  • ‘apc.write_lock’ places an exclusive lock for caching compiled script bytecode;
  • ‘apc.write_lock’  置入脚本编译的字节码的独占锁;
  • ‘apc.lazy_functions’ and ‘apc.lazy_classes’ enables lazy loading for functions and classes.
  • ‘apc.lazy_functions’ 和 ‘apc.lazy_classes’ 开启函数和类的延迟加载。

You can read more about these and other configuration directives here.

猛戳这里获取更多配置信息。

That’s about all I have for the moment. I hope this tutorial has given you some insight into how APC works, and how you can use it to improve the performance of your PHP applications. Try it out the next time you have a performance optimization problem, and see what you think!

这些就是哥目前所知所识了。希望这个教程能让你看清APC脱下裤子是怎么干活滴,你怎么样可以使用它来提升你的PHP应用的性能。下次再有性能优化难题的时间,好好试试,你就能融会贯通,达到知行合一的境界。

Copyright Melonfire, 2010. All rights reserved.

版权归哥所有。哥保留所有权利。2010年书。



(责任编辑:IT)
------分隔线----------------------------