当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP Opcode缓存加速组件:APC模块安装使用

发布:smiling 来源: PHP粉丝网  添加日期:2018-09-17 09:29:00 浏览: 评论:0 

什么是opcode

也许你曾经尝试过用C/C++编写动态内容,虽然开发过程极其繁琐,但为了获得性能提升,这样做或许是值得的,它们可以将动态内容编译成二进制可执行文件,也就是目标代码,由操作系统进程直接装载运行。如今已经很少有人使用C/C++编写动态内容了,绝大多数的Web开发人员享受着当下的幸福时光,很多优秀的脚本语言可供选择,比如PHP,Ruby,Python,它们都属于解释型语言,所以用它们编写的动态内容都需要依赖响应的解释器程序来运行。

解释器程序也是一个二进制可执行文件,比如/bin/ruby,它同样可以直接在进程中运行,在运行过程中,解释器程序需要对输入的脚本代码进行分析,领会它们的旨意,然后执行它们。比如下面我们调用PHP的解释器,让它执行一段简单的脚本代码:

~ zfs$ php -r 'print 1+1;'

很好,它很快的输出了正确的结果,也许你觉的1+1的计算太小儿科,的确,在人类的大脑中计算1+1是很简单,几乎不用思考,但解释器的工作方式可不是你想象的那样,1+1和100+1000对它来说几乎没有什么区别,因为解释器核心引擎根本看不懂这些脚本代码,无法直接执行,所以需要进行一系列的代码分析工作,当解释器完成对脚本代码的分析后,便将它们生成可以直接运行的中间代码,也称为操作码(Operate Code,opcode)。

从程序代码到中间代码的这个过程,我们称为解释(parse),它由解释器来完成。如此相似的是,编译型语言中的编译器(如C语言的编译器gcc),也要将程序代码生成中间代码,这个过程我们称为编译(compile)。编译器和解释器的一个本质不同在于,解释器在生成中间代码后,便直接执行它,所以运行时的控制权在解释器;而编译器则将中间代码进一步优化,生成可以直接运行的目标程序,但不执行它,用户可以在随后的任意时间执行它,这时的控制权在目标程序,和编译器没有任何关系。

事实上,就解释和编译本身而言,它们的原理是相似的,都包括词法分析,语法分析,语义分析等,所以,有些时候将解释型语言中生成opcode的过程也称为"编译",需要你根据上下文来理解。

那么,opcode究竟是什么样的呢? 它又是如何解释生成的呢? 我们以PHP为例,来寻找这些答案。

PHP解释器的核心引擎为Zend Engine,可以很容易的查看它的版本:

  1. ~ zfs$ php -v 
  2. PHP 5.5.14 (cli) (built: Sep  9 2014 19:09:25)  
  3. Copyright (c) 1997-2014 The PHP Group 
  4. Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies 

还记得前面我们曾经用PHP计算1+1的脚本代码吗? 我们来看看这段代码的opcode。在此之前,需要安装PHP的Parsekit扩展,它是一个用C编写的二进制扩展,由PECL来维护。有了Parsekit扩展后,我们就可以通过它提供的运行时API来查看任何PHP文件或者代码段的opcode。我们直接在命令行中调用parsekit_compile_string(),如下所示:

/usr/local/php/bin/php -r "var_dump(parsekit_compile_string('print 1+1;'));"

这样一来,我们便获得了这段代码的opcode,返回的是数组形式,结果如下所示:

  1. array(20) { 
  2.   ["type"]=> 
  3.   int(4) 
  4.   ["type_name"]=> 
  5.   string(14) "ZEND_EVAL_CODE" 
  6.   ["fn_flags"]=> 
  7.   int(0) 
  8.   ["num_args"]=> 
  9.   int(0) 
  10.   ["required_num_args"]=> 
  11.   int(0) 
  12.   ["pass_rest_by_reference"]=> 
  13.   bool(false) 
  14.   ["uses_this"]=> 
  15.   bool(false) 
  16.   ["line_start"]=> 
  17.   int(0) 
  18.   ["line_end"]=> 
  19.   int(0) 
  20.   ["return_reference"]=> 
  21.   bool(false) 
  22.   ["refcount"]=> 
  23.   int(1) 
  24.   ["last"]=> 
  25.   int(5) 
  26.   ["size"]=> 
  27.   int(5) 
  28.   ["T"]=> 
  29.   int(2) 
  30.   ["last_brk_cont"]=> 
  31.   int(0) 
  32.   ["current_brk_cont"]=> 
  33.   int(4294967295) 
  34.   ["backpatch_count"]=> 
  35.   int(0) 
  36.   ["done_pass_two"]=> 
  37.   bool(true) 
  38.   ["filename"]=> 
  39.   string(17) "Parsekit Compiler" 
  40.   ["opcodes"]=> 
  41.   array(5) { 
  42.     [0]=> 
  43.     array(8) { 
  44.       ["address"]=> 
  45.       int(33847532) 
  46.       ["opcode"]=> 
  47.       int(1) 
  48.       ["opcode_name"]=> 
  49.       string(8) "ZEND_ADD" 
  50.       ["flags"]=> 
  51.       int(197378) 
  52.       ["result"]=> 
  53.       array(3) { 
  54.         ["type"]=> 
  55.         int(2) 
  56.         ["type_name"]=> 
  57.         string(10) "IS_TMP_VAR" 
  58.         ["var"]=> 
  59.         int(0) 
  60.       } 
  61.       ["op1"]=> 
  62.       array(3) { 
  63.         ["type"]=> 
  64.         int(1) 
  65.         ["type_name"]=> 
  66.         string(8) "IS_CONST" 
  67.         ["constant"]=> 
  68.         &int(1) 
  69.       } 
  70.       ["op2"]=> 
  71.       array(3) { 
  72.         ["type"]=> 
  73.         int(1) 
  74.         ["type_name"]=> 
  75.         string(8) "IS_CONST" 
  76.         ["constant"]=> 
  77.         &int(1) 
  78.       } 
  79.       ["lineno"]=> 
  80.       int(1) 
  81.     } 
  82.     [1]=> 
  83.     array(7) { 
  84.       ["address"]=> 
  85.       int(33847652) 
  86.       ["opcode"]=> 
  87.       int(41) 
  88.       ["opcode_name"]=> 
  89.       string(10) "ZEND_PRINT" 
  90.       ["flags"]=> 
  91.       int(770) 
  92.       ["result"]=> 
  93.       array(3) { 
  94.         ["type"]=> 
  95.         int(2) 
  96.         ["type_name"]=> 
  97.         string(10) "IS_TMP_VAR" 
  98.         ["var"]=> 
  99.         int(1) 
  100.       } 
  101.       ["op1"]=> 
  102.       array(3) { 
  103.         ["type"]=> 
  104.         int(2) 
  105.         ["type_name"]=> 
  106.         string(10) "IS_TMP_VAR" 
  107.         ["var"]=> 
  108.         int(0) 
  109.       } 
  110.       ["lineno"]=> 
  111.       int(1) 
  112.     } 
  113.     [2]=> 
  114.     array(7) { 
  115.       ["address"]=> 
  116.       int(33847772) 
  117.       ["opcode"]=> 
  118.       int(70) 
  119.       ["opcode_name"]=> 
  120.       string(9) "ZEND_FREE" 
  121.       ["flags"]=> 
  122.       int(271104) 
  123.       ["op1"]=> 
  124.       array(4) { 
  125.         ["type"]=> 
  126.         int(2) 
  127.         ["type_name"]=> 
  128.         string(10) "IS_TMP_VAR" 
  129.         ["var"]=> 
  130.         int(1) 
  131.         ["EA.type"]=> 
  132.         int(0) 
  133.       } 
  134.       ["op2"]=> 
  135.       array(3) { 
  136.         ["type"]=> 
  137.         int(8) 
  138.         ["type_name"]=> 
  139.         string(9) "IS_UNUSED" 
  140.         ["opline_num"]=> 
  141.         string(1) "0" 
  142.       } 
  143.       ["lineno"]=> 
  144.       int(1) 
  145.     } 
  146.     [3]=> 
  147.     array(7) { 
  148.       ["address"]=> 
  149.       int(33847892) 
  150.       ["opcode"]=> 
  151.       int(62) 
  152.       ["opcode_name"]=> 
  153.       string(11) "ZEND_RETURN" 
  154.       ["flags"]=> 
  155.       int(16777984) 
  156.       ["op1"]=> 
  157.       array(3) { 
  158.         ["type"]=> 
  159.         int(1) 
  160.         ["type_name"]=> 
  161.         string(8) "IS_CONST" 
  162.         ["constant"]=> 
  163.         &NULL 
  164.       } 
  165.       ["extended_value"]=> 
  166.       int(0) 
  167.       ["lineno"]=> 
  168.       int(1) 
  169.     } 
  170.     [4]=> 
  171.     array(5) { 
  172.       ["address"]=> 
  173.       int(33848012) 
  174.       ["opcode"]=> 
  175.       int(149) 
  176.       ["opcode_name"]=> 
  177.       string(21) "ZEND_HANDLE_EXCEPTION" 
  178.       ["flags"]=> 
  179.       int(0) 
  180.       ["lineno"]=> 
  181.       int(1) 
  182.     } 
  183.   } 

parsekit扩展的安装请参见这篇文章

系统缓存

它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存3600s(一小时)。但是这样仍会浪费大量CPU时间。因此可以在php.ini中设置system缓存为永不过期(apc.ttl=0)。不过如果这样设置,改运php代码后需要重启WEB服务器。目前使用较多的是指此类缓存。

用户数据缓存

缓存由用户在编写PHP代码时用apc_store和apc_fetch函数操作读取、写入的。如果数据量不大的话,可以一试。如果数据量大,使用类似memcache此类的更加专著的内存缓存方案会更好。

APC模块的安装

最简单的方法是直接使用pecl,在命令行下输入:/usr/local/php/bin/pecl install apc

然后按照提示一步步完成即可,示例如下:

  1. [root@iZ23bm1tc0pZ ~]# /usr/local/php/bin/pecl install apc 
  2. downloading APC-3.1.13.tgz ... 
  3. Starting to download APC-3.1.13.tgz (171,591 bytes) 
  4. .....................................done: 171,591 bytes 
  5. 55 source files, building 
  6. running: phpize 
  7. Configuring for
  8. PHP Api Version:         20100412 
  9. Zend Module Api No:      20100525 
  10. Zend Extension Api No:   220100525 
  11. Enable internal debugging in APC [no] : no 
  12. Enable per request file info about files used from the APC cache [no] : no 
  13. Enable spin locks (EXPERIMENTAL) [no] : no 
  14. Enable memory protection (EXPERIMENTAL) [no] : no 
  15. Enable pthread mutexes (default) [no] : no 
  16. Enable pthread read/write locks (EXPERIMENTAL) [yes] : yes 

然后重启服务器即可:

lnmp nginx restart

先看一下没有使用apc情况下的压测结果:

  1. [root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://phpfensi.com/index.php 
  2. This is ApacheBench, Version 2.3 <$Revision: 1706008 $> 
  3. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.phpfensi.com/ 
  4. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  5. Benchmarking zfsphp.cn (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. Server Software:        nginx 
  18. Server Hostname:        zfsphp.cn 
  19. Server Port:            80 
  20. Document Path:          /index.php 
  21. Document Length:        14341 bytes 
  22. Concurrency Level:      100 
  23. Time taken for tests:   15.517 seconds 
  24. Complete requests:      1000 
  25. Failed requests:        0 
  26. Total transferred:      14544000 bytes 
  27. HTML transferred:       14341000 bytes 
  28. Requests per second:    64.45 [#/sec] (mean) 
  29. Time per request:       1551.671 [ms] (mean) 
  30. Time per request:       15.517 [ms] (mean, across all concurrent requests) 
  31. Transfer rate:          915.34 [Kbytes/sec] received 
  32. Connection Times (ms) 
  33.               min  mean[+/-sd] median   max 
  34. Connect:        0    2   4.8      0      17 
  35. Processing:    46 1481 277.0   1560    1638 
  36. Waiting:       42 1481 277.1   1560    1638 
  37. Total:         58 1482 272.8   1560    1638 
  38. Percentage of the requests served within a certain time (ms) 
  39.   50%   1560 
  40.   66%   1576 
  41.   75%   1582 
  42.   80%   1587 
  43.   90%   1602 
  44.   95%   1612 
  45.   98%   1622 
  46.   99%   1629 
  47.  100%   1638 (longest request) 

可见最大吞吐率只有64.45reqs/s,然后我们开启apc,测试结果如下:

  1. [root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://phpfensi.com/index.php 
  2. This is ApacheBench, Version 2.3 <$Revision: 1706008 $> 
  3. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.phpfensi.com/ 
  4. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  5. Benchmarking zfsphp.cn (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. Server Software:        nginx 
  18. Server Hostname:        phpfensi.com 
  19. Server Port:            80 
  20. Document Path:          /index.php 
  21. Document Length:        14341 bytes 
  22. Concurrency Level:      100 
  23. Time taken for tests:   7.122 seconds 
  24. Complete requests:      1000 
  25. Failed requests:        0 
  26. Total transferred:      14544000 bytes 
  27. HTML transferred:       14341000 bytes 
  28. Requests per second:    140.41 [#/sec] (mean) 
  29. Time per request:       712.189 [ms] (mean) 
  30. Time per request:       7.122 [ms] (mean, across all concurrent requests) 
  31. Transfer rate:          1994.29 [Kbytes/sec] received 
  32. Connection Times (ms) 
  33.               min  mean[+/-sd] median   max 
  34. Connect:        0    1   2.4      0      10 
  35. Processing:    23  677 125.3    705     775 
  36. Waiting:       22  677 125.4    705     775 
  37. Total:         30  678 123.1    705     775 
  38. Percentage of the requests served within a certain time (ms) 
  39.   50%    705 
  40.   66%    719 
  41.   75%    726 
  42.   80%    730 
  43.   90%    742 
  44.   95%    750 
  45.   98%    760 
  46.   99%    765 
  47.  100%    775 (longest request) 
 可见吞吐率提高了一倍多,达到140.41reqs/s,然后,我们在开启动态内容缓存(楼主的博客用的是Smarty缓存),测试结果如下:
 [root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://phpfensi.com/index.php 
  1. This is ApacheBench, Version 2.3 <$Revision: 1706008 $> 
  2. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.phpfensi.com/ 
  3. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  4. Benchmarking zfsphp.cn (be patient) 
  5. Completed 100 requests 
  6. Completed 200 requests 
  7. Completed 300 requests 
  8. Completed 400 requests 
  9. Completed 500 requests 
  10. Completed 600 requests 
  11. Completed 700 requests 
  12. Completed 800 requests 
  13. Completed 900 requests 
  14. Completed 1000 requests 
  15. Finished 1000 requests 
  16. Server Software:        nginx 
  17. Server Hostname:        phpfensi.com 
  18. Server Port:            80 
  19. Document Path:          /index.php 
  20. Document Length:        14341 bytes 
  21. Concurrency Level:      100 
  22. Time taken for tests:   2.263 seconds 
  23. Complete requests:      1000 
  24. Failed requests:        0 
  25. Total transferred:      14544000 bytes 
  26. HTML transferred:       14341000 bytes 
  27. Requests per second:    441.98 [#/sec] (mean) 
  28. Time per request:       226.255 [ms] (mean) 
  29. Time per request:       2.263 [ms] (mean, across all concurrent requests) 
  30. Transfer rate:          6277.49 [Kbytes/sec] received 
  31. Connection Times (ms) 
  32.               min  mean[+/-sd] median   max 
  33. Connect:        0    1   3.1      0      12 
  34. Processing:    18  215  38.1    222     255 
  35. Waiting:       18  215  38.3    222     255 
  36. Total:         26  216  35.6    223     255 
  37. Percentage of the requests served within a certain time (ms) 
  38.   50%    223 
  39.   66%    230 
  40.   75%    232 
  41.   80%    234 
  42.   90%    237 
  43.   95%    239 
  44.   98%    240 
  45.   99%    243 
  46.  100%    255 (longest request) 

这一次吞吐率居然达到441.98reqs/s,提高了三倍多,相比最初的64.45reqs/s提高了近7倍,可见使用apc的opcode缓存配合Smarty缓存,对网站性能的优化效果还是相当明显的。

Tags: 缓存 组件 模块

分享到: