现代化PHP编程语言学习-部署、测试、调优
PHP
主机
- 存储PHP应用四种方式:共享服务器,虚拟私有服务器,专用服务器,和平台即服务。
- Apache Web服务器会为每个Http请求派生一个专门的子进程,Apache的mod_php模块会在派生的每个子进程中嵌入专门的PHP解释器,即使进程只是用来伺服静态资源的,比如图像和css表,这么做消耗很大,浪费系统资源。
- Nginx服务器位于一系列PHP-FPM进程的前面,会把PHP请求转发给这些进程处理。
- SSH秘钥对认证,使用SSH秘钥对登陆远程设备时,远程设备会随机创建一个消息,使用公钥加密,然后把密文发给本地设备。本地设备收到密文后使用私钥解密,然后把解密后的消息发给远程服务器。远程服务器验证解密后的消息之后,在赋予你访问的服务器的权限。
ssh密钥对认证
- ssh-keygen 创建秘钥对:
1、~/.ssh/id_rsa.pub(公钥), ~/.ssh/id_ras 私钥
2、scp ~/.ssh/id_ras.pub user@IP地址:,末尾加上:符号,复制到用户的家目录。
3、创建~/.ssh目录,mkdir ~/.ssh
4、touch ~/.ssh/authorized_keys 创建这个文件存放一系列允许登陆这台服务器的公钥。
5、cat ~/id_rsa.pub >> ~/.ssh/authorized_keys 把公钥添加到authorized_keys文件中
6、最后更改几个目录和文件的访问权限,只让用户访问~/.ssh目录和~/.ssh/authorized_keys文件: chown -R user:usergroup ~/.ssh chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys;
禁用密码,禁止根用户登陆
- SSH服务器软件的配置文件:/etc/ssh/sshd_config, PasswordAuthentication,设置为no,禁止密码登陆;PermitRootLogin,设置为no,禁止root用户登陆,保存改动。
- 重启服务器命令,sudo systemctl restart sshd.service
1、部署
1、PHP-FPM 安装 -centos7.3
- PHP-FPM(PHP FastCGI php fastcgi进程管理器)适用于管理PHP进程池的软件,用于接收和处理来自WEb服务器(例如nginx)的请求。PHP-FPM软件会创建一个主进程(通常以操作系统中根用户身份运行),控制何时以及如何把HTTP请求转发给一个或者多个子进程处理。PHP-FPM主进程还控制着什么时候创建(处理Web应用更多的流量)和销毁(子进程运行时间太久了不再需要了)PHP子进程。PHP-FPM进程池中每个进程存在的时间都比单个HTTP请求长,可以处理10,50,100,500或更多的HTTP请求。
- 为了安装最新版的PHP,我们需要使用EPEL(Extra Packages Enterprise Linux),企业版的Linux的额外包。添加EPEL仓库的命令,
sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-10.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
这两个命令把第三方软件仓库EPEL和remi添加CentOS/RHEL系统中。/etc/yum.repos.d/有epel.repo 和remi.repo两个文件。
- 从epel仓库中安装php及PHP的相关扩展
sudo yum -y --enablerepo=elpe,rmei,remi-php56 install php-fpm php-cli php-gd php-mbstring php-mcypt php-mysqlnd php-opcache php-pdo php-devel
--enablerepo选项的作用是告诉yum从EPEL,remi,和remi-php56仓库安装指定的软件包。没有这个选项的话,yum只使用默认的软件源。
全局配置信息
- CentOS中PHP-FPM的主配置文件是/etc/php-fpm.conf。
- emergency_restart_threshold = 10 在指定的一段时间内,如果失效的PHP-FPM子进程数超过这个值,PHP-FPM主进程就优雅重启。
- emergency_restart_interval = 1m 设定
emergency_restart_threshold
设置采用的时间跨度。
配置进程池
配置进程池:
php-fpm.conf 包含 include=/etc/php-fpm.d/*.conf
作用是让PHP-FPM加载 /etc/php-fpm.d/
- 目录中各个进程池定义文件。以非根用户运行各个PHP-FPM进程池,这样在使用top或ps aux命令便于识别每个php应用的PHP-FPM进程池。这样的话每个PHP-FPM进程池中的进程都受相应的操作系统用户和用户组的权限限制在沙盒中。
- sudo vim /etc/php-fpm.d/www.conf 编辑进程池文件。每个php-fpm进程池配置文件都是[进程池名称],例如[www]。
- user=liguanh: 拥有这个PHP-FPM进程池中子进程的系统用户组,设置为非根用户。
- group=liguanh: 拥有这个PHP-FPM进程池的系统用户组,设置为非根用户组。
- listen=127.0.0.1:9000 PHP-FPM进程池监听的IP地址跟端口号,让PHP-FPM只接受nginx从这里传入的请求。
- listen.allowed_clients = 127.0.0.1 可以向这个PHP-FPM进程池发送请求的IP地址(one or more).默认只有当前设备。
- pm.max_children = 51 设置任何时间点进程池最多拥有的子进程。根据你分配给php-fpm的内存/每个进程内存使用(10M大概)= num.
- pm.start_servers = 3 PHP-FPM启动时PHP-FPM进程池中立即可用的进程数。
- pm.min_spare_servers = 2 PHP应用空闲时进程池中可以存在的进程数最小值。用于确保新进入的请求无需等待进程初始化过程。
- pm.max_spare_servers = 4 PHP应用空闲时进程池中可以存在的进程数最大值
- pm.max_requests = 1000 回收进程之前,PHP-FPM进程池中各个进程最多能处理的HTTP请求数量。有助于避免PHP扩展或库因便携拙劣导致内存不断泄露等。建议是1000。
- slowlog = /path/to/slowlog.log 这个设置的是一个日志文件的在文件系统中的绝对路径。这个日志文件用于记录处理时间超过n秒的HTTP请求信息,以便找出PHP应用的瓶颈,进行调试。而且PHP-FPM进程池所属的用户必须得有这个文件的写权限。
- request_slowlog_timeout = 8s 如果当前HTTP请求的请求处理时间超过指定的值,就把请求的回溯信息写入slowlog设置指定的日志文件。
- 编辑保存PHP-FPM的配置文件后,要执行下面的命令重启PHP-FPM主进程
sudo systemctl restart php-fpm.service
2、nginx安装
server{
listen:80;
server_name test.knowshare.com;
index index.php;
client_max_body_size 50M;
error_log /home/liguanh/apps/logs/knowshare.error.log;
access_log /home/liguanh/apps/logs/knowshare.access.log;
root /home/liguanh/apps/www.knowshare.com/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
}
}
- listen 设置nginx监听哪个端口进入的HTTP请求。
- server_name 识别虚拟主机的域名。设置为你要使用的应用域名,而且域名要指向服务器的ip地址。如果HTTP请求中host首部的值和虚拟主机中的server_name的值匹配,nginx会把这个请求发给虚拟主机。
- index HTTP请求中url未指定时伺服的默认文件。
- client_max_body_size 对这个虚拟主机来说,nginx接受HTTP请求主体长度的最大值。如果请求长度超过这个值,nginx会返回HTTP 4xx响应。
- error_log 这个虚拟主机错误日志位置。
- access_log 虚拟主机访问日志文件位置。
- root 文档的根目录路径。
location \ { }
块使用try_files 指令查找匹配请求的URL的文件;如果未找到,在查找请求URL的目录;如果未查到目录,那么就把HTTP请求的URL重写为index.php,然后把查询字符附加到URL的末尾。这个重写的URL,以及所有的以.php结尾的URL,都由location ~ .php {}块管理。
location ~ \.php {}
块把HTTP请求转发给PHP-FPM进程池处理,把PHP请求转发给端口9000,交给PHP-FPM进程池处理。其他的作用是避免潜在的远程代码攻击。
- commands:
sudo systemctl restart nginx.service
- nginx配置学习:
- http://nginx.org/
- https://github.com/h5bp/server-configs-nginx
- https://serversforhackers.com/editions/2014/03/25/nginx/
- 系统管理学习资源: https://book.serversforhackers.com/
2、调优
php调优
- php解释器在php.ini文件中配置和调优。
- memory_limit: 用于设定单个PHP进程可以使用的系统内存最大值。默认值是128M,这对大多数的中小型PHP应用来说合适。
- 决定给PHP分配多少内存:1、一共能给PHP分多少内存 2、单个PHP进程会消耗多少内存 3、能负担多少个PHP-FPM的进程 4、系统资源是否足够
- 调优PHP不是提升应用性能的通用措施,拙劣的代码依然拙劣。
Zend OPcache 操作码缓存
- 每次HTTP请求处理php脚本的过程: 首先,nginx吧http请求转发给php-fpm,php-fpm再把请求交给php子进程处理。PHP进程找到相应的php脚本后,读取脚本,把脚本编译成操作码格式,然后执行编译得到的操作码,生成响应。最后把HTTP响应发给nginx,nginx再把响应发给HTTP客户端。
- 我们可以缓存编译每个PHP脚本得到的操作码,加速处理的过程。缓存后,我们可以从缓存中直接读取并执行预先编译好的操作码,避免每次处理Http请求都查找,读取和编译PHP的脚本。
- opcache.memeory_consumption=64 为操作码缓存分配的内存量。分配的内存量应该够保存应用中所有PHP脚本编译得到的操作码。
- opcache.interned_strings_buffer = 16 用来存储驻留字符串的内存量。
- 驻留字符串: PHP解释器在背后会找到相同字符串的多个实例,把这个字符串保存在内存中,如果再次使用相同的字符串,PHP解释器会使用指针。这么做能够节省内存,默认PHP驻留字符串会隔离在各个PHP进程中。这个设置能让PHP-FPM进程池中的所有进程把驻留字符串存储到共享的缓冲区中,以便在PHP-FPM进程池中的多个进程中引用。
- opcache.max_accelerated_files = 4000 操作码中最多能存储多少个PHP脚本。这个值一定要逼PHP应用中文件数量大。
- opcache.validate_timestamps = 1 这个设置为1,过段时间PHP会检查PHP脚本的内容是否有变化。设置为0,PHP不会检查PHP脚本内容变化,必须手动清楚缓存的操作码。
- opcache.revalidate_freq = 0 设置PHP多久检查php的脚本内容是否有变化,如果有变化,重新编译缓存。
- opcache.fast_shutdown = 1 设置让操作码使用更快的停机步骤,把对象西沟和内存释放交给Zend Engine的内存管理器完成。
- max_execution_time 设置单个PHP进程在终止之前最长可以运行多少时间。
- PHP的exec()函数调用bash的at命令,作用是派生单独的非阻塞的进程,不耽误当前PHP进程。
- PHP作业队列PHPResque (https://github.com/chrisboulton/php-resque)
处理会话
- PHP默认的会话处理会拖慢大型应用,因为这个处理程序会把会话数据放在硬盘中,需要创建不必要的文件I/O,浪费时间。我们应该把会话数据放在内存中,例如memcached或redis。这么做还便于伸缩。如果会话数据存放在硬盘里面,不便于扩展服务器。如果放在memcached或redis的中央数据存储里,任何一台分布式PHP-FPM服务器都能访问会话数据。
- 如果想再PHP中访问Memcached中存储的数据,要安装链接memcached的PECL扩展。
- PHP会话处理配置 session.save_handler = files
缓冲输出
- 如果在较少的块中发送更多的数据,而不是在较多的块中发送较少的数据,那么网络的效率会更高。在较少的片段中把内容传递给访问者的浏览器,能减少HTTP请求总数。因此我们要让PHP缓冲输出。
- outing_buffering=4096 必须是4(32位系统)和8(64位系统)的倍数
- implicit_flush = false
真实路径缓存
- PHP会缓存应用使用的文件路径,这样每次包含或导入文件时就无需不断搜索包含路径了,这个缓存叫做真实路径缓存(realpaht cache). realpath_cache_size()获取真实路径的真正大小,可以根据这个值设置路径缓存的值。
- realpath_cache_size = 64k
部署
1、版本控制
版本控制能记录代码基的变化,可以把一个时间点的代码标记为发布版,可以回滚到之前的状态,还可以在单独的分支中开发新功能,不影响生产环境的代码。
2、自动部署,简单部署,部署结果可知,部署可逆。
3、Capistrano(http://capistranorb.com)用于自动部署应用,运行在本地设备,通过ssh与远程服务器通信。
4、Capistrano会在远程服务器保存之前部署的应用,而且每次部署的版本放在各自的目录中。可以维护5个或更多之前的部署的应用,防止要回滚。创建一个current/目录,通过符号链接指向当前部署的应用所在的目录。部署代码时,Capistrano首先从应用的git仓库获取最新代码,然后把应用的代码放到releases/目录中的一个新目录,最后把current/符号链接指向这个目录。回滚版本时,Capistrano会把current/目录的符号链接指向releases/目录中存放之前版本的子目录。
5、 OS X系统使用gem install capistrano命令安装,在项目的最顶层目录使用cap install命令;
6、cap production deploy 部署应用 ,cap production deploy:rollback 回滚应用。
3、测试
1、测试很重要,开发之前,开发的过程中,和开发完成之后都要关注测试。
2、在开发的过程中测试能增强自信,写出稳定的代码,而且还能帮助我们快速找出并重构破坏现有功能的新代码。
3、我们应该测试应用的最小组成部分。
- 从微观角度来看,应用由PHP类,方法,和函数组成。因此,我们应该隔离测试每个公开的类,方法和函数,确保表现符合预期。如果我们知道各个部分能单独正常运行,可以确信集成在一起组成整个应用时也能正常运行,这种测试叫做单元测试。
- 可是单独测试每个部分,不能保证各个部分在整个应用中正常运行。因此我们还要使用自动化测试从宏观上验证应用的整体行为。这种测试叫功能测试。
4、 单元测试,大多数流行的PHP框架都使用PHPunit测试。
测试驱动开发(TDD):测试驱动开发的意思是在编写应用代码之前先写测试。我们故意让测试失败,一次描述应用应该具有怎样的表现。开发好应用的功能后,最终测试会成功通过。TDD能帮助我们按照目标开发应用。TDD是一种迭代开发方式,小步向前,知道开发完成整个应用。
行为驱动开发(BDD): 行为驱动开发的意思是编写故事,描述应用的表现;分两种类型:SpecBDD和StoryBDD。
SpecBDD是一种单元测试,使用人类能读懂的流畅语言描述应用的实现方式。StoryBDD更多的是关注整体行为,描述业务的逻辑,而不是底层实现。StoryBDD测试类似于项目经理的需求(生成测试报告,发送电子邮件),而SpecBDD类似于开发者的需求(例如类方法接收一个数据数组,把数据写入PDF文件).他们不是互斥的,常常需要结合使用,编写更全面的测试。
5、PHPUnit在一起组成测试用例,测试用例在一起组成测试组件。测试运行程序的作用是PHPUnit运行测试组件并输出结果的工具。
6、一个测试用例是一个PHP类,这个类扩展自PHPUnit_Framework_TestCase类。测试用例有一些以test开头的公开方法,一个方法是一个测试,在方法中我们断言会发生特定的事情。断言可能通过,也可能失败。我们的目标是让所有的断言都通过。测试用例的类名必须以Test结尾,而且所在的文件名必须Test.php结尾。
7、PHPUnit的网站(http://bit.ly/php-unit)中列出了一些断言。有些断言没有文档,我们可以在gitHub中查看源代码(http://bit.ly/phpu-gh),找到所有可用的断言。
8、phpUnit提供的断言方法assertAttributeEquals()接收。第一个是期望值,第二个是参数的属性,第三个是要检查的对象。他可以使用PHP的反射功能
检查并验证受保护的属性。这个断言方法能检查某个对象的内部状态,而且不用依赖某个未测试的方法。
Travis CI持续测试
1、每次把代码推送到gitHub仓库中,Travis都会自动运行应用的测试,而且会通过电子邮件把测试结果发给你。详细配置PHP的Travis CI环境,详细信息参见Travis CI的网站(http://bit.ly/build-php).
2、延伸学习PHP的链接