PHP浅析

PHP(PHP: Hypertext Preprocessor)即“超文本预处理器”,是在服务器端执行的脚本语言,尤其适用于Web开发并可嵌入HTML中。PHP语法学习了C语言,吸纳Java和Perl多个语言的特色发展出自己的特色语法,并根据它们的长项持续改进提升自己,例如java的面向对象编程,该语言当初创建的主要目标是让开发人员快速编写出优质的web网站。 [1-2] PHP同时支持面向对象和面向过程的开发,使用上非常灵活。...

PHP结构

Zend引擎

Zend 引擎是 PHP4 以后加入 PHP 的,是对原有PHP解释器的重写,整体使用 C 语言进行开发,也就是说可以把PHP理解成用C写的一个编程语言软件,引擎的作用是将PHP代码翻译为一种叫opcode的中间语言,它类似于JAVA的ByteCode(字节码)实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕zend实现。 引擎对PHP代码会执行四个步骤:

词法分析 Scanning(Lexing),将 PHP 代码转换为语言片段(Tokens)。

解析 Parsing, 将 Tokens 转换成简单而有意义的表达式。

编译 Compilation,将表达式编译成Opcode。

执行 Execution,顺序执行Opcode,每次一条,以实现PHP代码所表达的功能。

APC、Opchche 这些扩展可以将Opcode缓存以加速PHP应用的运行速度,使用它们就可以在请求再次来临时省略前三步。

引擎也实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕Zend实现。

Extensions

围绕着zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的php中间层、富文本解析就是extension的典型应用)。

Sapi

SAPI 是 Server Application Programming Interface 的缩写,也就是服务端应用编程接口,中文为服务端应用编程接口,它通过一系列钩子函数使得PHP可以和外围交换数据,这是php非常优雅和成功的一个设计,通过sapi成功的将php本身和上层应用解耦隔离,php可以不再考虑如何针对不同应用进行兼容。SAPI 就是 PHP 和外部环境的代理器,它把外部环境抽象后,为内部的PHP提供一套固定的,统一的接口,使得 PHP自身实现能够不受错综复杂的外部环境影响,保持一定的独立性。

通过 SAPI 的解耦,PHP可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。

如前所述,sapi通过通过一系列的接口,使得外部应用可以和PHP交换数据,并可以根据不同应用特点实现特定的处理方法,我们常见的一些sapi有:

apache2handler:这是以apache作为webserver,采用mod_PHP模式运行时候的处理方式。

cgi:这是webserver和PHP之间的另一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP得到越来越多的应用,也是异步webserver所唯一支持的方式。

cli:命令行调用的应用模式

Application

程序员编写的PHP程序,无论是 Web 应用还是 Cli 方式运行的应用都是上层应用,PHP 程序员主要工作就是编写它们。

这就是我们平时编写的php程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。

如果PHP是一辆车,那么车的框架就是PHP本身,Zend是车的引擎(发动机),Ext下面的各种组件就是车的轮子,Sapi可以看做是公路,车可以跑在不同类型的公路上,而一次PHP程序的执行就是汽车跑在公路上。因此,我们需要:性能优异的引擎+合适的车轮+正确的跑道。

PHP-FPM

PHP-FPM (PHP FastCGI Process Manager的简称,意思是"PHP FastCGI进程管理器")是用于管理PHP进程池的软件,用于接收和处理来自web服务器(例如nginx)的请求。PHP-FPM软件会创建一个主进程(通常以操作系统中根用户的身份运行),控制何时以及如何把HTTP请求转发给一个或多个子进程处理。PHP-FPM主进程还控制着什么时候创建(处理web应用更多的流量)和销毁(子进程运行时间太久或不再需要了) PHP子进程。PHP-FPM进程池中的每个进程存在的时间都比单个HTTP请求长,可以处理 0、50、100、500或更多的HTTP请求。

安装

安装PHP-FPM最简单的方式是使用操作系统原生的包管理器,如下述命令所示。

sudo apt-get install python-software-properties;

sudo add-apt-repository ppa:ondrej/php5-5.6;

sudo apt-get update;

sudo apt-get instali php5-fpm php5-cli php5-curl \

php5-gd php5-json php5-mcrypt php5-mysq1nd;

Centos


sudo rpm -Uvh \ http://dl.fedoraproject.org/publepel/7/x86_64/e/epel-release-i7-5.noarch.rpm;

sudo rpm -Uvh \

http://rpms.famillecollet.com/enterprise/remi-release-7.rpm;

sudo yum -y --enablerepo=epel,remi,remi-php56 install php-fpm php-cli php-gd php-mbstring php-mcrypt php-mysqlnd php-opcache php-pdo php-devel;

全局配置

在Ubuntu中,PHP斗PM的主配置文件是:/usr/local/php732/etc/php-fpm.conf。

下面是PHP-FPM最重要的全局设置,建议把默认值改为下面列出的值。默认情况下,这两个设置可能被注释掉了,如果需要,去掉注释。这两个设置的作用是,如果在指定的一段时间内有指定个子进程失效了,让PHP-FPM主进程重启。这是PHP-FPM进程的基本安全保障,能解决简单的问题,但是不能解决由拙劣的PHP代码引起的重大问题。

emergency_restart_threshold=10

在指定的一段时间内,如果失效的PHP-FPM子进程数超过这个值,PHP-FPM主进程就优雅重启。

emergency_restart_interval=1m

img设定emergency_restart_threshold设置采用的时间跨度。

注意:PHP-FPM全局设置的详细信息参见方即:https://www.php.net/manual/zh/install.fpm.php

腾讯文档:https://cloud.tencent.com/developer/article/1909959

配置进程池

PHP-FPM配置文件其余的内容是一个名为poo1 Definitions的区域。这个区域里的配置用于设置每个PHP-FPM进程池。PHP-FPM进程池中是一系列相关的PHP子进程。通常,一个PHP应用有自己的一个PHP-FPM进程池。

#在Ubuntu中,pool Definitions区域只有下面这一行内容:

include=/etc/php5/fpm/poo1.d/*.conf

#Cent0S则在PHP-FPM主配置文件的顶部使用下面这行代码引人进程池定义文件:

include=/etc/php-fpm.d/*.conf

这行代码的作用是让PHP-FPM加载include=/etc/php5/fpm/poo1.d/目录(Ubuntu)或/etc/php-fpm.d/目录(Cent0S)中的各个进程池定义文件。进人这个目录,应该会看到一个名为www.conf的文件。这是名为www的默认PHP-FPM进程池的配置文件。

注意:每个PHP-FPM进程池的配置文件开头都是[符号,后跟进程池的名称,然后是]符号。例如,在默认的PHP-FPM进程池的配置文件中,开头是[www]。

各个PHP-FPM进程池都以指定的操作系统用户和用户组的身份运行。以单独的非img根用户身份运行各个PHP-FPM进程池,这样在命令行中使用top或ps aux命令时便于识别每个PHP应用的PHP-FPM进程池。这是个好习惯,因为每个PHP士PM进程池中的进程都受相应的操作系统用户和用户组的权限限制在沙盒中。

我们要配置默认的www PHP-FPM进程池,让它以deploy用户和用户组的身份运行。如果还没打开www PHP-FPM进程池的配置文件。

user :deploy(www)

拥有这个PHP-FPM进程池中子进程的系统用户。要把这个设置的值设为运行PHP应用的非根用户的用户名。

group:deploy(www)

拥有这个PHP-FPM进程池中子进程的系统用户组。要把这个设置的值设为运行PHP 应用的非根用户所属的用户组名。

listen 127.0.0.1:9000(9732)

PHP-FPM进程池监听的IP地址和端口号,让PHP-FPM只接受nginx从这里传人的请求。127.0.0.1:9000让指定的PHP PM进程池监听从本地端口9000进人的连接。

我使用的端口是9000,不过你可以使用任何不需要特殊权限(大于1024)且没被其他系统进程占用的端口号。配置nginx虚拟主机时会再次讨论这个设置。

listen.allowed_clients=127.0.0.1

可以向这个PHP-FPM进程池发送请求的IP地址(一个或多个)。为了安全,我把这个设置设为127.0.0.1,即只有当前设备能把请求转发给这个PHP-FPM进程池。默认情况下,这个设置可能被注释掉了,如果需要,去掉这个设置的注释。

pm.max_children = 51(200)

这个设置设定任何时间点PHP-FPM进程池中最多能有多少个进程。这个设置没有绝对正确的值,你应该测试你的PHP应用,确定每个PHP进程需要使用多少内存,然后把这个设置设为设备可用内存能容纳的PHP进程总数。对大多数中小型PHP应用来说,每个PHP进程要使用5一巧MB内存(具体用量可能有差异)。假设我们使用的设备为这个PHP-FPM进程池分配了512MB可用内存,那么我们可以把这个设置的值设为(512MB总内存)/(每个进程使用10MB):51个进程。

pm.start_servers = 3(100)

PHP-FPM启动时PHP-FPM进程池中立即可用的进程数。同样地,这个设置也没有绝对正确的值。对大多数中小型PHP应用来说,我建议设为2或3。这么做是为了先准备好两到三个进程,等待请求进人,不让PHP应用的前几个HTTP请求等待PHP-FPM初始化进程池中的进程。

pm.min_spare_servers = 2(50)

PHP应用空闲时PHP-FPM进程池中可以存在的进程数量最小值。这个设置的值一般与pm.start-servers设置的值一样,用于确保新进人的HTTP请求无需等待PHP-FPM在进程池中重新初始化进程。

pm.max_spare_servers:4(150)

PHP应用空闲时PHP-FPM进程池中可以存在的进程数量最大值。这个设置的值一般比pm.start_servers设置的值大一点,用于确保新进人的HTTP请求无需等待PHP-FPM在进程池中重新初始化进程。

pm.max_requests = 1000

img

回收进程之前,PHP-FPM进程池中各个进程最多能处理的HTTP请求数量。这个设置有助于避免PHP扩展或库因编写拙劣而导致不断泄露内存。我建议设为1000,不过你应该根据应用的需求做调整。

slowlog = /path/to/slowlog.log

(/alidata0/logs/php732/$pool.log.slow)1574442 2G

这个设置的值是一个日志文件在文件系统中的绝对路径。这个日志文件用于记录处理时间超过”秒的HTTP请求信息,以便找出PHP应用的瓶颈,进行调试。记img住,PHP-FPM进程池所属的用户和用户组必须有这个文件的写权限。/path/to/ slowlog.log只是示例,请替换成真正的文件路径。

request_slowlog_timeout= 5s(10s)

如果当前HTTP请求的处理时间超过指定的值,就把请求的回溯信息写人slowlog设置指定的日志文件。把这个设置的值设为多少,取决于你认为多长时间算久。一开始可以设为5s。

编辑并保存PHP-FPM的配置文件后,要执行下述命令重启PHP-FPM主进程:

# Ubuntu

sudo service php5-fpm restart

# Cent0S

sudo systemctl restart php-fpm.service

注意:PHP-FPM进程池的配置详情参见:https://www.php.net/manual/zh/install.fpm.configuration.php

Nginx

nginx是web服务器,类似Apache,不过更容易配置,而且使用的系统内存通常更少。我没有时间详细介绍nginx,不过我想告诉你如何在你的服务器中安装 nginx,以及如何把相应的请求转发给PHP-FPM进程池。

安装

安装nginx最简单的方式是使用操作系统原生的包管理器。

#Ubuntu 在Ubuntu中可以使用PPA包安装nginxo PPA是Ubuntu专用的术语,是指由nginx社区维护且预先打包好的档案。

sudo add-apt-repository ppa:nginx/stable;

 sudo apt-get update; 

sudo apt-get install nginx;

#CentOS 在Cent0S中可以使用前面添加的第三方软件仓库EPEL安装nginx。CentOS默认使用的软件仓库可能没有最新版nginx。

sudo yum install nginx; 

sudo systemctl enable nginx.service;

sudo systemctl start nginx.service;

虚拟主机

我们要为PHP应用配置一个nginx虚拟主机。虚拟主机是一系列设置,用于告诉 nginx应用的域名、PHP应用在文件系统的什么地方,以及如何把HTTP请求转发给PHP-FPM进程池。

首先,我们必须决定把应用放在文件系统的什么位置。非根用户deploy必须拥有PHP 应用所在文件系统目录的读写权限。这里,我要把应用的文件放在/home/deploy/apps/example.com/current/目录中。我们还需要一个保存应用日志文件的目录。我把日志文件放在/home/deploy/apps/logs目录中。执行下述命令,创建所需的目录,并赋予正确的权限:

mkdir -p /home/deploy/apps/example.com/current/public;

mkdir -p /home/deploy/apps/logs;

chmod -R +rx /home/deploy;

然后把PHP应用放到/home/deploy/apps/example.com/current目录中。nginx虚拟主机假设 PHP应用有个public/目录,这是虚拟主机的文档根目录。

每个虚拟主机都有各自的配置文件。如果使用Ubuntu,请创建/etc/nginx/sites-availabe/example.conf配置文件;如果使用CentOS,请创建/etc/nginx/conf.d/example.conf文件。然后在你喜欢的文本编辑器中打开example.conf配置文件。

nginx虚拟主机的设置在server { }块中。下面是虚拟主机配置文件的完整内容:

server {

 listen 80;
 server_name example.com;
 index index.php;
 client max body size 50M; 
 error log/home/deploy/apps/logs/example.error.log; 
 access log /home/deploy/apps/logs/example.access.log;
 root /home/deploy/apps/example.com/current/public;

 Location / { 
	try_files $uri $uri/ /index.php$is_args$args;
 }

 location ~ \.php {
	try_files $uri = 404;
	fastcgi_split_path_info  ^(.+\.php)(/.+)$;
 	include fastcgi_params;
 	fastcgi param SCRIPT FILENAME $document root$fastcgi script_name;
 	fastcgi_param SCRIPT NAME $fastcgi script_name;
 	fastcgi index index.php;
 	fastcgi_pass 127.0.0.1:9000;
 }
}

复制上述代码,粘贴到example.conf虚拟主机配置文件中。记得要修改server name设置,还要把error_log、access_log和roo t改为适当的路径。下面简要说明每个虚拟主机设置:

listen

设置nginx监听哪个端口进人的HTTP请求。大多数情况下,HTTP流量从80端口进人,HTTPS流量从443端口进人。img

server_name

用于识别虚拟主机的域名。这个设置要设为你的应用使用的域名,而且域名要指向服务器的IP地址。如果HTTP请求中Host首部的值和虚拟主机中server name的值img匹配,nginx就会把这个HTTP请求发给这个虚拟主机。

index

HTTP请求URI没指定文件时伺服的默认文件。

client_max_body_size

对这个虚拟主机来说,nginx接受HTTP请求主体长度的最大值。如果请求主体的长度超过这个值,nginx会返回HTTP 4××响应。

error_log

这个虚拟主机错误日志文件在文件系统中的路径

access_log

这个虚拟主机访问日志文件在文件系统中的路径。

root

文档根目录的路径。

除了上述设置之外,server { }块中还有两个location块。这两个location块的作用是,告诉nginx如何处理匹配指定URL模式的FITTP请求。location / {}块使用try_files 指令查找匹配所请求URI的文件;如果未找到相应的文件,再查找匹配所请求URI的目录;如果也未找到相应的目录,把HTTP请求的URI重写为/index.php,如果有查询字符串的话,还会把查询字符附加到URI的末尾。这个重写的URL,以及所有以结尾的 URI,都由location ~.php { }块管理。

location ~.php { }块把HTTP请求转发给PHP-FPM进程池处理。前面我们设置PHP-FPM进程池监听端口9000。在这个块中,我们把PHP请求转发到端口9000,交给PHP-FPM进程池处理。

注意:location .php { }块中其他几行的作用是避免潜在的远程代码执行攻击(http://bit.ly/remote-ex)。

在Ubuntu中,我们必须执行下述命令,在/etc/nginx/sites-enabled目录中创建虚拟主机配置文件的符号链接:

sudo In -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf;

最后,执行下述命令,重启nginx:

# Ubuntu
sudo service ngxnx restart

# CentOS
sudo systemctl restart nginx.service

现在服务器可以运行PHP应用了!

调优

img现在,你的PHP应用应该在nginx和相应的PHP-FPM进程池中运行着了。不过,我们的工作还没做完。我们要调优PHP的配置,使用适用于应用和生产服务器的设置。调优的PHP相当于定做的套装,完全匹配你的尺寸。

别太高兴,调优PHP不是提升应用性能的通用措施。调优PHP是提升PHP效率和应用性能的简单措施。

php.ini文件

PHP解释器在php.ini文件中配置和调优。这个文件在操作系统中的位置有所不同,如果像我前面演示的那样使用PHP-FPM运行PHP,那么php.ini配置文件在/etc/php5/cli目录中。说来也怪,在命令行中执行php命令时使用的PHP解释器不受这个/etc/php5/cli文件的控制,而是由专门的php.ini文件控制。这个尸php.ini文件通常在/etc/php5/cli目录中。如果从源码构建PHP,php.ini文件很可能在配置PHP源码文件时指定的$PREFIX目录中。我假设你按照前文所述,使用PHP-FPM运行PHP。不过,我要讲的优化措施适用于所有php.ini文件。

注意:我们应该使用克里斯.科耐特开发的PHP Iniscan工具(http://github.com/psecio/iniscan)扫描劢加文件,检查是否使用了安全方面的最佳实践。

php.ini而文件使用INI格式,这个格式的详情参见维基百科https://en.wikipedia.org/wiki/INI_file)。

内存

运行PHP时我最关心的是每个PHP进程要使用多少内存。php.ini文件中的memory_limit设置用于设定单个PHP进程可以使用的系统内存最大值。

这个设置的默认值是128M,这对大多数中小型PHP应用来说或许合适。可是,如果运行的是微型PHP应用,可以降低这个值,例如设为64M,节省系统资源。如果运行的是内存集中型PHP应用(例如使用Drupal搭建的网站),可以增加这个值,例如设为512M,提升性能。这个设置的值由可用的系统内存决定。确定给PHP分配多少值是一门艺术,而不是学科。决定给PHP分配多少内存,以及能负担得起多少个PHP-FPM进程时,我会问自己以下几个问题:

一共能分配给PHP多少内存?

首先,我会确定能分配给PHP多少系统内存。例如,我可能会使用一个Linode虚拟设备,这个设备一共有2GB内存。可是,这台设备中可能还有其他进程(例如, nginx、MYSQL或memcache),而这些进程也要消耗内存。我觉得留512MB给PHP 就足够了。

单个PHP进程平均消耗多少内存?425184

然后,我会确定单个PHP进程平均消耗多少内存。为此,我要监控进程的内存使用量。如果使用命令行,可以执行top命令,查看运行中的进程的实时统计数据。除此之外,还可以在PHP脚本的最后调用memory_get_peak_usage()函数,输出当前脚本消耗的最大内存量。不管使用哪种方式,都要多次运行同一个PHP脚本,然后取内存消耗量的平均值。我发现PHP进程一般会消耗5~20MB内存(具体消耗可能有差异)。如果要上传文件、处理图像,或者运行的是内存集中型应用,得到的平均值显然会高些。

能负担得起多少个PHP-FPM进程?

假设我给PHP分配了512MB内存,每个PHP进程平均消耗巧MB内存,我拿内存总量除以每个PHP进程消耗的内存量,从而确定我能负担得起34个PHP-FPM进程。这是个估值,应该再做实验,得到精确值。

有足够的系统资源吗?

最后我会问自己,确信有足够的系统资源运行PHP应用并处理预期的流量吗?如果答案是肯定的,那太好了。如果答案是否定的,就需要升级服务器,添加更多的内存,然后再回到第一个问题。

注意. 我们应该使用Apache Bench(http://bit.ly/apache-bench)或Seige(http://www.joedog.org/siege-home/),在类似生产环境的条件下对PHP应用做压力测试,因为最好在把应用部署到生产环境之前确定是否有足够的资源可用。

Zend OPcache

配置PHP的Zend OPcache扩展。这个扩展用于缓存操作码。我们先来分析每次HTTP请求时通常是如何处理PHP脚本的。首先nginx把HTTP请求转发给PHP-FPM, PHP-FPM再把请求交给某个PHP子进程处理。

PHP进程找到相应的PHP脚本后,读取脚本,把PHP脚本编译成操作码(或字节码)格式,然后执行编译得到的操作码,生成响应。最后,把HTTP响应发给nginx,nginx再把响应发给HTTP客户端。可以看出,每次HTTP请求都要消耗很多资源。

我们可以缓存编译每个PHP脚本得到的操作码,加速这个处理过程。缓存后,我们可以从缓存中直接读取并执行预先编译好的操作码,不用每次处理HTTP请求时都查找、读取和编译PHP脚本。PHP中内置了Zend OPcache扩展。下面是我在劢文件中配置和优化Zend OPcache扩展所用的设置:

opcache.memory_consumption=64

opcache.interned_strings_buffer=16 

opcache.max_accelerated_files=4000 

opcache.validate_timestamps=1 

opcache.revalidate_freq=0 

opcache.fast_shutdown=1

opcache.memory_consumption = 64 (默认128)

为操作码缓存分配的内存量(单位是MB)。分配的内存量应该够保存应用中所有 PHP脚本编译得到的操作码。如果是小型PHP应用,脚本数较少,可以设为较低的值,例如16MB;如果是大型PHP应用,有很多脚本,那就使用较大的值,例如64MB。

opcache.interned_strings_buffer = 16 (默认8)

用来存储驻留字符串(interned string)的内存量(单位是MB)。那么驻留字符串是什么呢?我首先也会想到这个问题。PHP解释器在背后会找到相同字符串的多个实例,把这个字符串保存在内存中,如果再次使用相同的字符串,PHP解释器会使用指针。这么做能节省内存。默认情况下,PHP驻留的字符串会隔离在各个PHP进程中。这个设置能让PHP-FPM进程池中的所有进程把驻留字符串存储到共享的缓冲区中,以便在PHP-FPM进程池中的多个进程之间引用驻留字符串。这样能节省更多内存。这个设置的默认值是4MB,不过我喜欢设为16MB。

opcache.max_accelerated_files = 4000 (默认1000)

操作码缓存中最多能存储多少个PHP脚本。这个设置的值可以是200到100000之间的任何数。我使用的是4000。这个值一定要比PHP应用中的文件数量大。

opcache.validate_timestamps = 1 (默认1)

这个设置的值为1时,经过一段时间后PHP会检查PHP脚本的内容是否有变化。检查的时间间隔由opcache.revalidate_freq设置指定。如果这个设置的值为0,PHP不会检查PHP脚本的内容是否有变化,我们必须自己动手清除缓存的操作码。我建议在开发环境中设为1,在生产环境中设为0。

opcache.revalidate_freq = 0 (默认2)

设置PHP多久(单位是秒)检查一次PHP脚本的内容是否有变化。缓存的好处是,不用每次请求都重新编译PHP脚本。这个设置用于确定在多长时间内认为操作码缓存是新的。在这段时间之后,PHP会检查PHP脚本的内容是否有变化。如果有变化,PHP会重新编译脚本,再次缓存。我使用的值是0秒。仅当opcache.validate_timestamps设置的值为1时,这么设置会在每次请求时都重新验证PHP文件。因此,在开发环境中,每次请求都会重新验证PHP文件(这是好事)。这个设置在生产环境中没有任何意义,因为生产环境中opcache.validate_timestamps的值始终为0。

opcache.fast_shutdown = 1

这么设置能让操作码使用更快的停机步骤,把对象析构和内存释放交给zend Engine的内存管理器完成。这个设置缺少文档,你只需知道要把它设为1。

文件上传

如果你的PHP应用允许上传文件,最好设置最大能上传的文件大小。除此之外,最好还要设置最多能同时上传多少个文件。下面是我在力五文件中为我的应用所做的设置:

file_uploads=1

upload_max_filesize = 10M

max_file_uploads=3

默认情况下,PHP允许在单次请求中上传20个文件,上传的每个文件最大为2MB。你可能不想允许同时上传20个文件,我只允许单次请求上传3个文件。不过,你应该设为对你的应用来说合理的值。

如果我的PHP应用允许上传文件,通常都会允许上传大于2MB的文件。我把upload_max_filesize设置的值增加到了10MB,或许要根据应用的需求设为更高的值。但是别设为太大的值,如果这个值太大,web服务器会抱怨HTTP请求的主体太大,或者请求会超时。

注意:如果需要上传非常大的文件,web服务器的配置要做相应调整。除了在php.ini文件中设置之外,可能还要调整nginx虚拟主机配置中的client max_body_size设置(http://bit/max-body-size) img

最长执行时间

php.ini文件中的max_execution_time设置用于设定单个PHP进程在终止之前最长可以运行多少时间。这个设置的默认值是30秒。我们可不想让PHP进程运行30秒,因为我们想让应用运行得特别快(以毫秒计)。我建议把这个设置改为5秒:

max_execution_time = 5

你可能会问,如果PHP脚本需要运行更长的时间怎么办?答案是,PHP脚本不能长时间img运行。PHP运行的时间越长,web应用的访问者等待响应的时间就会越长。如果有长时间运行的任务,要在单独的职程中运行。

注意:在PHP脚本中可以调用set_time_limit()函数。(http://php.net/manual/function.set-time-limit.php)覆盖这个设置。

建议:我会使用PHP中的exec()函数调用bash的at命令。这个命令的作用是派生单独的非阻塞进程,不耽误当前的PHP进程。使用PHP中的exex()函数时,要使用escapeshellarg()函数(http://php.net/manual/function.escapeshellarg.php)转义shell参数。

假设我们要生成报告,并把结果制作成PDF文件。这个任务可能要花10分钟才能完成,而我们肯定不想让PHP请求等待10分钟。我们应该单独编写一个PHP文件,假如将其命名为create-report.php,让这个文件运行10分钟,最后生成报告。其实,web应用只需几毫秒就能派生一个单独的后台进程,然后返回HTTP响应,如下所示:

<?Php

exec( ‘echo "create-report.php"丨at now ');

echo ' Report pending...;

create-report.php脚本在单独的后台进程中运行,运行完毕后可以更新数据库,或者通过电子邮件把报告发给收件人。可以看出,我们完全没有理由让长时间运行的任务拖延 PHP主脚本,影响用户体验。

建议: 如果发现自己派生了很多后台进程,或许最好使用专门的作业队列。PHPResque(http://github.com/chrisboulton/php-resque)是个不错的作业队列管理器,它是基于GitHub的作业队列管理器Resque(https://github/blog/542-introducing-resque)开发的。

处理会话

PHP默认的会话处理程序会拖慢大型应用,因为这个处理程序把会话数据存储在硬盘中,需要创建不必要的文件I/O,浪费时间。我们应该把会话数据保存在内存中,例如可以使用Memcached (http://memcached.org)或Redis(http://:reids.io)。这么做还有个额外好处,以后便于伸缩。如果会话数据存储在硬盘中,不便于增加额外的服务器。如果把会话数据存储在Memcached或Redis中央数据存储区里,任何一台分布式PHP-FPM服务器都能访问会话数据。

若想在PHP中访问Memcached存储的数据,要安装连接Memcached的PECL扩展。然后再把下面两行添加到php.ini文件中,把PHP默认的会话存储方式改为Memcached:

session.save_handler = memcached

session.save_path =’127.0.0.2:11211 ’

缓冲输出

如果在较少的块中发送更多的数据,而不是在较多的块中发送较少的数据,那么网络的效率会更高。也就是说,在较少的片段中把内容传递给访问者的浏览器,能减少HTTP 请求总数。

因此,我们要让PHP缓冲输出。默认情况下,PHP已经启用了输出缓冲功能(不过没在命令行中启用)。PHP缓冲4096字节的输出之后才会把其中的内容发给web服务器。下面是我推荐在劢加文件中使用的设置:

output_buffering = 4096

implicit flush = false

建议:如果想修改输出缓冲区的大小,确保使用的值是4(32位系统)或8(64位系统)的倍数。

真实路径缓存

PHP会缓存应用使用的文件路径,这样每次包含或导人文件时就无需不断搜索包含路径了。这个缓存叫真实路径缓存(realpath cache)。如果运行的是大型PHP文件(例如Drupal和Composer组件等),使用了大量文件,增加PHP真实路径缓存的大小能得到更好的性能。

真实路径缓存的默认大小为16k。这个缓存所需的准确大小不容易确定,不过可以使用一个小技巧。首先,增加真实路径缓存的大小,设为特别大的直,例如256k。然后,在一个PHP脚本的末尾加上print_r(realpath_cache_size());‘输出真实路径缓存的真正大小。最后,把真实路径缓存的大小改为这个真正的值。我们可以在php.ini文件中设置真实路径缓存的大小:

realpath_cache_size:64k

Licensed under 京ICP备17003353号-3