分类目录归档:WEB服务

fredzneg与你一起对apache,nginx,squid,trafficserver学习相关知识及探讨!

记一次 Laravel 应用性能调优经历

这是一份事后的总结。在经历了调优过程踩的很多坑之后,我们最终完善并实施了初步的性能测试方案,通过真实的测试数据归纳出了 Laravel 开发过程中的一些实践技巧。

0x00 源起

最近有同事反馈 Laravel 写的应用程序响应有点慢、20几个并发把 CPU 跑满… 为了解决慢的问题,甚至一部分接口用 nodejs 来写。

而我的第一反应是一个流行的框架怎么可能会有这么不堪?一定是使用上哪里出现了问题。为了一探究竟,于是开启了这次 Laravel 应用性能调优之旅。

0x01 调优技巧

这次性能测试方案中用到的优化技巧主要基于 Laravel 框架本身及其提供的工具。

  1. 关闭应用debug app.debug=false
  2. 缓存配置信息 php artisan config:cache
  3. 缓存路由信息 php artisan router:cache
  4. 类映射加载优化 php artisan optimize
  5. 自动加载优化 composer dumpautoload
  6. 根据需要只加载必要的中间件
  7. 使用即时编译器(JIT),如:HHVM、OPcache
  8. 使用 PHP 7.x

除了以上优化技巧之外,还有很多编码上的实践可以提升 Laravel 应用性能,在本文中暂时不会做说明。(也可以关注我的后续文章)

1. 关闭应用 debug

打开应用根目录下的 .env 文件,把 debug 设置为 false。

APP_DEBUG=false

2. 缓存配置信息

php artisan config:cache

运行以上命令可以把 config 文件夹里所有配置信息合并到一个 bootstrap/cache/config.php 文件中,减少运行时载入文件的数量。

php artisan config:clear

运行以上命令可以清除配置信息的缓存,也就是删除 bootstrap/cache/config.php 文件

3. 缓存路由信息

php artisan route:cache

运行以上命令会生成文件 bootstrap/cache/routes.php。路由缓存可以有效的提高路由器的注册效率,在大型应用程序中效果越加明显。

php artisan route:clear

运行以上命令会清除路由缓存,也就是删除 bootstrap/cache/routes.php 文件。

4. 类映射加载优化

php artisan optimize --force

运行以上命令能够把常用加载的类合并到一个文件中,通过减少文件的加载来提高运行效率。这个命令会生成 bootstrap/cache/compiled.php 和 bootstrap/cache/services.json 两个文件。

通过修改 config/compile.php 文件可以添加要合并的类。

在生产环境中不需要指定 --force 参数文件也可以自动生成。

php artisan clear-compiled

运行以上命令会清除类映射加载优化,也就是删除 bootstrap/cache/compiled.php 和 bootstrap/cache/services.json 两个文件。

5. 自动加载优化

composer dumpautoload -o

Laravel 应用程序是使用 composer 来构建的。这个命令会把 PSR-0 和 PSR-4 转换为一个类映射表来提高类的加载速度。

注意:php artisan optimize --force 命令里已经做了这个操作。

6. 根据需要只加载必要的中间件

Laravel 应用程序内置了并开启了很多的中间件。每一个 Laravel 的请求都会加载相关的中间件、产生各种数据。在 app/Http/Kernel.php 中注释掉不需要的中间件(如 session 支持)可以极大的提升性能。

7. 使用即时编译器

HHVM 和 OPcache 都能轻轻松松的让你的应用程序在不用做任何修改的情况下,直接提高 50% 或者更高的性能。

8. 使用 PHP 7.x

只能说 PHP 7.x 比起之前的版本在性能上有了极大的提升。

嗯,限于你的真实企业环境,这个也许很长时间内改变不了,算我没说。

0x02 测试方案

我们使用简单的 Apache ab 命令仅对应用入口文件进行测试,并记录和分析数据。

  1. 仅对应用的入口文件 index.php 进行测试,访问 “/” 或者 “/index.php” 返回框架的欢迎页面。更全面的性能测试需要针对应用的更多接口进行测试。
  2. 使用 Apache ab 命令。ab -t 10 -c 10 {url}。该命令表示对 url 同时发起 10 个请求,并持续 10 秒钟。命令中具体的参数设置需要根据要测试的服务器性能进行选择。
  3. 为了避免机器波动导致的数据错误,每种测试条件会执行多次 ab 命令,并记录命令执行结果,重点关注每秒处理的请求数及请求响应时间,分析并剔除异常值。
  4. 每次对测试条件进行了调整,需要在浏览器上对欢迎页进行访问,确保没有因为测试条件修改而访问出错。如果页面访问出错会导致测试结果错误。

服务器环境说明

所有脱离具体环境的测试数据都没有意义,并且只有在相近的条件下才可以进行比较。

  1. 这套环境运行在 Mac 上,内存 8G,处理器 2.8GHz,SSD 硬盘。
  2. 测试服务器是使用 Homestead 搭建的。虚拟机配置为单核 CPU、2G 内存。
  3. 服务器 PHP 版本为 7.1,未特殊说明,则标识开启了 OPcache。
  4. 测试的 Laravel 应用程序采用 5.2 版本编写。app\Http\routes.php 中定义了 85 个路由。
  5. 测试过程中除了虚拟机、终端及固定的浏览器窗口外,没有会影响机器的程序运行。

以上的数据,大家在自己进行测试时可以参考。

0x03 测试过程及数据

1. 未做任何优化

1.1 操作

  • 按照以下检查项执行相应的操作。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

基础检查项

  • .env 文件中 APP_DEBUG=true
  • 不存在 bootstrap/cache/config.php
  • 不存在 bootstrap/cache/routes.php
  • 不存在 bootstrap/cache/compiled.php 和 bootstrap/cache/services.json
  • app/Http/Kernel.php 中开启了大部分的中间件
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问

1.2 数据记录

2. 关闭应用debug

2.1 操作

  • 在步骤 1 基础上修改 .env 文件中 APP_DEBUG=false
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

2.2 数据记录

2.3 对比结果

与步骤 1 结果比较发现:关闭应用 debug 之后,每秒处理请求数从 26-34 上升到 33-35,请求响应时间从 大部分 300ms 以上下降到 290ms 左右,效果不太明显,但确实有一定的提升。

注意:这部分与应用中的日志等使用情况有比较大的关联。

3. 开启缓存配置信息

3.1 操作

  • 在步骤 2 基础上,运行 php artisan config:cache,确认生成 bootstrap/cache/config.php
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

3.2 数据记录

3.3 对比结果

与步骤 2 结果比较发现:开启配置信息缓存之后,每秒处理请求数从 33-35 上升到 36-38,请求响应时间从 290ms 左右下降到 260ms 左右,效果不太明显,但确实有一定的提升。

4. 开启缓存路由信息

4.1 操作

  • 在步骤 3 基础上,运行 php artisan route:cache,确认生成 bootstrap/cache/routes.php
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

4.2 数据记录

4.3 对比结果

与步骤 3 结果比较发现:开启路由信息缓存之后,每秒处理请求数从 36-38 上升到 60 左右,请求响应时间从 260ms 下降到 160ms 左右,效果显著,从 TPS 看,提升了 70%。

5. 删除不必要的中间件

5.1 操作

  • 在步骤 4 基础上,注释掉不必要的中间件代码。
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

注意:这次测试中我注释掉了所有的中间件。实际情况中应该尽量只留下必要的中间件。

5.2 数据记录

5.3 对比结果

与步骤 4 结果比较发现:删除了不必要的中间件之后,每秒处理请求数从 60 左右上升到 90 左右,请求响应时间从 160ms 下降到 110ms 左右,效果非常明显,从 TPS 看,提升了 50%。

6. 开启类映射加载优化

6.1 操作

  • 在步骤 5 基础上,运行 php artisan optimize --force,确认生成 bootstrap/cache/compiled.php 和 bootstrap/cache/services.json
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

6.2 数据记录

6.3 对比结果

与步骤 5 结果比较发现:做了类映射加载优化之后,每秒处理请求数从 90 上升到 110,请求响应时间从 110ms 下降到 100ms 以下,效果还是比较明显的。

7. 关闭 OPcache

7.1 操作

  • 在步骤 6 基础上,关闭 PHP 的 OPcache,并重启服务器。通过 phpinfo() 的 Zend OPcache 确认 OPcache 已经关闭。
  • 浏览器访问 Laravel 应用程序欢迎页确保正常访问。
  • 运行 ab -t 10 -c 10 http://myurl.com/index.php

7.2 数据记录

7.3 对比结果

与步骤 6 结果比较发现:关闭 OPcache 之后,每秒处理请求数从 110 下降到 15,请求响应时间从 100ms 以下上升到 650ms 以上。开启与关闭 OPcache,数据上竟有几倍的差别。

此后,我重新开启了 PHP 的 OPcache,数据恢复到步骤 6 水平。

0x04 踩过的坑

1. [LogicException] Unable to prepare route [/] for serialization. Uses Closure.

在运行 php artisan route:cache 命令时报这个错误。

原因:路由文件中处理“/”时使用了闭包的方式。要运行该命令,路由的具体实现不能使用闭包方式。

修改方案:将路由的具体实现放到控制器中来实现。

2. [Exception] Serialization of ‘Closure’ is not allowed.

在运行 php artisan route:cache 命令时报这个错误。

原因:路由文件中定义了重复的路由。

修改方案:排查路由文件中的重复路由并修改。尤其要注意 resource 方法很可能导致与其方法重复。

3. [RuntimeException] Invalid filename provided.

在运行 php artisan optimize --force 命名时报这个错误。

原因:在加载需要编译的类时没有找到相应的文件。5.2 版本的 vendor/laravel/framework/src/Illuminate/Foundation/Console/Optimize/config.php 中定义了要编译的文件路径,但不知道为什么 /vendor/laravel/framework/src/Illuminate/Database/Eloquent/ActiveRecords.php 没有找到,所以报了这个错误。

修改方案:暂时注释掉了以上 config.php 中的 ../ActiveRecords.php 一行。

4. InvalidArgumentException in FileViewFinder.php line 137: View [welcome] not found.

在运行 php artisan config:cache 之后,浏览器上访问 Laravel 应用程序欢迎页报这个错误。

原因:Laravel 应用程序服务器是通过 Homestead 在虚拟机上搭建的。而这个命令我是在虚拟机之外运行的,导致生成的 config.php 中的路径是本机路径,而不是虚拟机上的路径。所以无法找到视图文件。

修改方案:ssh 到虚拟机内部运行该命令。

0x05 实践技巧

坑也踩了,测试也做过了。这里针对这次经历做个实践技巧的简单总结。

1. 有效的 Laravel 应用程序优化技巧

  1. 关闭应用debug app.debug=false
  2. 缓存配置信息 php artisan config:cache
  3. 缓存路由信息 php artisan router:cache
  4. 类映射加载优化 php artisan optimize(包含自动加载优化 composer dumpautoload
  5. 根据需要只加载必要的中间件
  6. 使用即时编译器(JIT),如:HHVM、OPcache

2. 编写代码时注意事项

  1. 路由的具体实现放到控制器中。
  2. 不定义重复的路由,尤其注意 resouce 方法。
  3. 弄清各中间件的作用,删除不必要的中间件引用。

0x06 下一步

以上的调优技巧及编码注意事项主要针对框架本身,在真正的业务逻辑编码中有很多具体的优化技巧,在此没有讨论。

后续的优化重点将会放在具体编码实践上:

  1. 使用 Memcached 来存储会话 config/session.php
  2. 使用专业的缓存驱动器
  3. 数据库请求优化
  4. 为数据集书写缓存逻辑
  5. 前端资源合并 Elixir

0x07 写在最后

网上看到很多框架性能对比的文章与争论,也看到很多简单贴出了数据。这些都不足以窥探真实的情况,所以有了我们这次的实践,并在过程中做了详实的记录。在各位读者实践过程中提供参考、比较、反思之用。对于这次实践有疑问的读者,也欢迎提出问题和意见。

不多说了,要学习更多技术干货,请关注微信公众号:up2048。

– EOF –

原文有图参考: https://segmentfault.com/a/1190000011569012

使用 Nginx 实现灰度发布

灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。

灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

灰度发布常见一般有三种方式:

  • Nginx+LUA方式
  • 根据Cookie实现灰度发布
  • 根据来路IP实现灰度发布

本文主要将讲解根据Cookie和来路IP这两种方式实现简单的灰度发布,Nginx+LUA这种方式涉及内容太多就不再本文展开了。

A/B测试流程

Nginx根据Cookie实现灰度发布

根据Cookie查询Cookie键为version的值,如果该Cookie值为V1则转发到hilinux_01,为V2则转发到hilinux_02。Cookie值都不匹配的情况下默认走hilinux_01所对应的服务器。

两台服务器分别定义为:

1 2 
hilinux_01  192.168.1.100:8080 hilinux_02  192.168.1.200:8080 
  • 用if指令实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   #match cookie  set $group "default";  if ($http_cookie ~* "version=V1"){  set $group hilinux_01;  }   if ($http_cookie ~* "version=V2"){  set $group hilinux_02;  }   location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  }  } 
  • 用map指令实现

在Nginx里面配置一个映射,$COOKIE_version可以解析出Cookie里面的version字段。$group是一个变量,{}里面是映射规则。

如果一个version为V1的用户来访问,$group就等于hilinux_01。在server里面使用就会代理到http://hilinux_01上。version为V2的用户来访问,$group就等于hilinux_02。在server里面使用就会代理到http://hilinux_02上。Cookie值都不匹配的情况下默认走hilinux_01所对应的服务器。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }   map $COOKIE_version $group { ~*V1$ hilinux_01; ~*V2$ hilinux_02; default default; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  }  } 

Nginx根据来路IP实现灰度发布

如果是内部IP,则反向代理到hilinux_02(预发布环境);如果不是则反向代理到hilinux_01(生产环境)。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;    set $group default;  if ($remote_addr ~ "211.118.119.11") {  set $group hilinux_02;  }  location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  } } 

如果你只有单台服务器,可以根据不同的IP设置不同的网站根目录来达到相同的目的。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 
server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   set $rootdir "/var/www/html";  if ($remote_addr ~ "211.118.119.11") {  set $rootdir "/var/www/test";  }   location / {  root $rootdir;  } } 

到此最基本的实现灰度发布方法就讲解完了,如果要做更细粒度灰度发布可参考ABTestingGateway项目。

ABTestingGateway是新浪开源的一个动态路由系统。ABTestingGateway是一个可以动态设置分流策略的灰度发布系统,工作在7层,基于nginx和ngx-lua开发,使用redis作为分流策略数据库,可以实现动态调度功能。

ABTestingGateway:https://github.com/CNSRE/ABTestingGateway

https://www.hi-linux.com/posts/34319.html

swoole最新的发行版安装

和任何开源项目一样, Swoole总是在最新的发行版提供最可靠的稳定性和最强的功能, 请尽量保证你使用的是最新版本

1. 直接使用Swoole官方的二进制包 (初学者 + 开发环境)

访问我们官网的下载页面

编译需求

  • Linux, OS X 系统 或 CygWin, WSL
  • PHP 7.0.0 或以上版本 (版本越高性能越好)
  • GCC 4.8 及以上

2. 使用PHP官方的PECL工具安装 (初学者)

pecl install swoole

3. 从源码编译安装 (推荐)

非内核开发研究之用途, 请下载发布版本的源码编译

cd swoole-src && \
phpize && \
./configure && \
make && sudo make install

启用扩展

编译安装到系统成功后, 需要在php.ini中加入一行extension=swoole.so来启用Swoole扩展

额外编译参数

使用例子: ./configure --enable-openssl --enable-sockets

  • --enable-openssl 或 --with-openssl-dir=DIR
  • --enable-sockets
  • --enable-http2
  • --enable-mysqlnd (需要 mysqlnd, 只是为了支持mysql->escape方法)

升级

⚠️ 如果你要从源码升级, 别忘记在源码目录执行 make clean

  1. pecl upgrade swoole
  2. git pull && cd swoole-src && make clean && make && sudo make install
  3. 如果你改变了PHP版本, 请重新执行 phpize clean && phpize后重新编译

Http性能压测工具wrk

用过了很多压测工具,却一直没找到中意的那款。最近试了wrk感觉不错,命令及结果很类似ab,写下这份使用指南给自己备忘用,如果能帮到你,那也很好。

安装

wrk支持大多数类UNIX系统,不支持windows。需要操作系统支持LuaJIT和OpenSSL,不过不用担心,大多数类Unix系统都支持。安装wrk非常简单,只要从github上下载wrk源码,在项目路径下执行make命令即可。

git clone https://github.com/wg/wrk make

make之后,会在项目路径下生成可执行文件wrk,随后就可以用其进行HTTP压测了。可以把这个可执行文件拷贝到某个已在path中的路径,比如/usr/local/bin,这样就可以在任何路径直接使用wrk了。

默认情况下wrk会使用自带的LuaJIT和OpenSSL,如果你想使用系统已安装的版本,可以使用WITH_LUAJIT和WITH_OPENSSL这两个选项来指定它们的路径。比如:

make WITH_LUAJIT=/usr WITH_OPENSSL=/usr

基本使用

  1. 命令行敲下wrk,可以看到使用帮助
Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open  -d, --duration    <T>  Duration of test  -t, --threads     <N>  Number of threads to use  -s, --script      <S>  Load Lua script file  -H, --header      <H>  Add header to request  --latency          Print latency statistics  --timeout     <T>  Socket/request timeout  -v, --version          Print version details  Numeric arguments may include a SI unit (1k, 1M, 1G) Time arguments may include a time unit (2s, 2m, 2h)

简单翻成中文:

使用方法: wrk <选项> <被测HTTP服务的URL> Options:                                            
    -c, --connections <N> 跟服务器建立并保持的TCP连接数量  
    -d, --duration <T> 压测时间           
    -t, --threads <N> 使用多少个线程进行压测   
                                                      
    -s, --script <S> 指定Lua脚本路径       
    -H, --header <H> 为每一个HTTP请求添加HTTP头      
        --latency          在压测结束后,打印延迟统计信息   
        --timeout <T> 超时时间     
    -v, --version          打印正在使用的wrk的详细版本信息 <N>代表数字参数,支持国际单位 (1k, 1M, 1G) <T>代表时间参数,支持时间单位 (2s, 2m, 2h)
  1. 看下版本
wrk -v 输出: wrk 4.0.2 [epoll] Copyright (C) 2012 Will Glozer

看到是4.0.2版本的wrk,使用了epoll。这意味着我们可以用少量的线程来跟被测服务创建大量连接,进行压测。

  1. 做一次简单压测,分析下结果
wrk -t8 -c200 -d30s --latency "http://www.bing.com" 输出:
Running 30s test @ http://www.bing.com 8 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency 46.67ms 215.38ms 1.67s 95.59% Req/Sec 7.91k 1.15k 10.26k 70.77% Latency Distribution 50%    2.93ms 75%    3.78ms 90%    4.73ms 99%    1.35s  1790465 requests in 30.01s, 684.08MB read
Requests/sec: 59658.29 Transfer/sec: 22.79MB

以上使用8个线程200个连接,对bing首页进行了30秒的压测,并要求在压测结果中输出响应延迟信息。以下对压测结果进行简单注释:

Running 30s test @ http://www.bing.com (压测时间30s) 8 threads and 200 connections (共8个测试线程,200个连接)
  Thread Stats   Avg      Stdev     Max   +/- Stdev
              (平均值) (标准差)(最大值)(正负一个标准差所占比例)
    Latency 46.67ms 215.38ms 1.67s 95.59% (延迟)
    Req/Sec 7.91k 1.15k 10.26k 70.77% (处理中的请求数)
  Latency Distribution (延迟分布) 50%    2.93ms 75%    3.78ms 90%    4.73ms 99%    1.35s (99分位的延迟) 1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)
Requests/sec: 59658.29 (平均每秒处理完成59658.29个请求)
Transfer/sec: 22.79MB (平均每秒读取数据22.79MB)

可以看到,wrk使用方便,结果清晰。并且因为非阻塞IO的使用,可以在普通的测试机上创建出大量的连接,从而达到较好的压测效果。

使用Lua脚本个性化wrk压测

以上两节安装并简单使用了wrk,但这种简单的压测可能不能满足我们的需求。比如我们可能需要使用POST METHOD跟服务器交互;可能需要为每一次请求使用不同的参数,以更好的模拟服务的实际使用场景等。wrk支持用户使用–script指定Lua脚本,来定制压测过程,满足个性化需求。

  1. 介绍wrk对Lua脚本的支持

wrk支持在三个阶段对压测进行个性化,分别是启动阶段、运行阶段和结束阶段。每个测试线程,都拥有独立的Lua运行环境。

启动阶段
function setup(thread)

在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。

thread.addr             - get or set the thread's server address thread:get(name)        - get the value of a global in the thread's env thread:set(name, value) - set the value of a global in the thread's env thread:stop()           - stop the thread
运行阶段
function init(args) function delay() function request() function response(status, headers, body)

init由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;

delay在每次发送request之前调用,如果需要delay,那么delay相应时间;

request用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;

reponse在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;

结束阶段
function done(summary, latency, requests)

该方法在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。

自定义脚本中可访问的变量和方法

变量:wrk

 wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>,
  }

一个table类型的变量wrk,是全局变量,修改该table,会影响所有请求。

方法:wrk.fomat wrk.lookup wrk.connect

 function wrk.format(method, path, headers, body) wrk.format returns a HTTP request string containing the passed parameters
    merged with values from the wrk table.
    根据参数和全局变量wrk,生成一个HTTP rquest stringfunction wrk.lookup(host, service) wrk.lookup returns a table containing all known addresses for the host and service pair. This corresponds to the POSIX getaddrinfo() function.
    给定hostserviceport/well known service name),返回所有可用的服务器地址信息。 function wrk.connect(addr) wrk.connect returns true if the address can be connected to, otherwise
    it returns false. The address must be one returned from wrk.lookup().
    测试与给定的服务器地址信息是否可以成功创建连接
  1. 示例
使用POST METHOD
wrk.method = "POST" wrk.body   = "foo=bar&baz=quux" wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

通过修改全局变量wrk,使得所有请求都使用POST方法,并指定了body和Content-Type头。

为每次request更换一个参数
request = function() uid = math.random(1, 10000000) path = "/test?uid=" .. uid return wrk.format(nil, path) end

通过在request方法中随机生成1~10000000之间的uid,使得请求中的uid参数随机。

每次请求之前延迟10ms
function delay() return 10 end
每个线程要先进行认证,认证之后获取token以进行压测
token = nil path = "/authenticate" request = function() return wrk.format("GET", path) end response = function(status, headers, body) if not token and status == 200 then token = headers["X-Token"] path = "/resource" wrk.headers["X-Token"] = token end end

在没有token的情况下,先访问/authenticate认证。认证成功后,读取token并替换path为/resource。

压测支持HTTP pipeline的服务
init = function(args) local r = {}
   r[1] = wrk.format(nil, "/?foo")
   r[2] = wrk.format(nil, "/?bar")
   r[3] = wrk.format(nil, "/?baz")

   req = table.concat(r) end request = function() return req end

通过在init方法中将三个HTTP request请求拼接在一起,实现每次发送三个请求,以使用HTTP pipeline。

https://www.cnblogs.com/xinzhao/p/6233009.html

nginx+php-fpm,使用Unix Socket还是tcp方式连接?

将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快”,看来这是真的存在。两者各有优缺点啊

1.worker_processes 越大越好(一定数量后性能增加不明显)


2.worker_cpu_affinity 所有cpu平分worker_processes 要比每个worker_processes 都跨cpu分配性能要好;不考虑php的执行,测试结果worker_processes数量是cpu核数的2倍性能最优

3.unix domain socket(共享内存的方式)要比tcp网络端口配置性能要好
不考虑backlog,请求速度有量级的飞跃,但错误率超过50%
加上backlog,性能有10%左右提升

4.调整nginx、php-fpm和内核的backlog(积压),connect() to unix:/tmp/php-fpm.socket failed (11: Resource temporarily unavailable) while connecting to upstream错误的返回会减少
nginx:
配置文件的server块
listen 80 default backlog=1024;

php-fpm:
配置文件的
listen.backlog = 2048

kernel参数:
/etc/sysctl.conf,不能低于上面的配置
net.ipv4.tcp_max_syn_backlog = 4096
net.core.netdev_max_backlog = 4096

5.增加单台服务器上的php-fpm的master实例,会增加fpm的处理能力,也能减少报错返回的几率
多实例启动方法,使用多个配置文件:
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm.conf &
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm1.conf &

nginx的fastcgi配置
    upstream phpbackend {
#      server   127.0.0.1:9000 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9001 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9002 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9003 weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm1.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm2.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm3.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm4.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm5.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm6.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm7.sock weight=100 max_fails=10 fail_timeout=30;
    }

        location ~ \.php* {
            fastcgi_pass   phpbackend;
#           fastcgi_pass   unix:/var/www/php-fpm.sock;
            fastcgi_index index.php;
       ……….
       }

6.测试环境和结果

内存2G
swap2G
cpu 2核 Intel(R) Xeon(R) CPU E5405  @ 2.00GHz
采用ab远程访问测试,测试程序为php的字符串处理程序


1)在开4个php-fpm实例,nginx 8个worker_processes 每个cpu4个worker_processes ,backlog为1024,php的backlog为2048,内核backlog为4096,采用unix domain socket连接的情况下,其他保持参数不变

性能和错误率较为平衡,可接受,超过4个fpm实例,性能开始下降,错误率并没有明显下降
结论是fpm实例数,worker_processes数和cpu保持倍数关系,性能较高
影响性能和报错的参数为
php-fpm实例,nginx worker_processes数量,fpm的max_request,php的backlog,unix domain socket


10W请求,500并发无报错,1000并发报错率为0.9%

500并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    4000 [#/sec] (mean) avg.
Time per request:       122.313 [ms] (mean)
Time per request:       0.245 [ms] (mean, across all concurrent requests)
Transfer rate:          800 [Kbytes/sec] received avg.

1000并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        524
   (Connect: 0, Length: 524, Exceptions: 0)
Write errors:           0
Non-2xx responses:      524
Requests per second:    3903.25 [#/sec] (mean)
Time per request:       256.197 [ms] (mean)
Time per request:       0.256 [ms] (mean, across all concurrent requests)
Transfer rate:          772.37 [Kbytes/sec] received

2)在其他参数不变,unix domain socket换为tcp网络端口连接,结果如下

500并发:
Concurrency Level:      500
Time taken for tests:   26.934431 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3712.72 [#/sec] (mean)
Time per request:       134.672 [ms] (mean)
Time per request:       0.269 [ms] (mean, across all concurrent requests)
Transfer rate:          732.37 [Kbytes/sec] received

1000并发:
Concurrency Level:      1000
Time taken for tests:   28.385349 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3522.94 [#/sec] (mean)
Time per request:       283.853 [ms] (mean)
Time per request:       0.284 [ms] (mean, across all concurrent requests)
Transfer rate:          694.94 [Kbytes/sec] received

与1)比较,有大约10%的性能下降

7. 5.16调整fpm的max_request参数为1000,并发1000报错返回降到200个以下,
Transfer rate在800左右

PHP7开启Opcode性能对比

Opcache 的前生是 Optimizer+ ,它是PHP的官方公司 Zend 开发的一款闭源但可以免费使用的 PHP 优化加速组件。 Optimizer+ 将PHP代码预编译生成的脚本文件 Opcode 缓存在共享内存中供以后反复使用,从而避免了从磁盘读取代码再次编译的时间消耗。同时,它还应用了一些代码优化模式,使得代码执行更快。从而加速PHP的执行。

 PHP的正常执行流程如下

 

request请求(nginx,apache,cli等)–>Zend引擎读取.php文件–>扫描其词典和表达式 –>解析文件–>创建要执行的计算机代码(称为Opcode)–>最后执行Opcode–> response 返回

每一次请求PHP脚本都会执行一遍以上步骤,如果PHP源代码没有变化,那么Opcode也不会变化,显然没有必要每次都重新生成Opcode,结合在Web中无所不在的缓存机制,我们可以把Opcode缓存下来,以后直接访问缓存的Opcode岂不是更快,启用Opcode缓存之后的流程图如下所示:

 

 Opcode cache 的目地是避免重复编译,减少 CPU 和内存开销。

下面介绍Opcache的安装

安装:

1、找到opcache的扩展,我的是php7.1
yum list php71*
2、安装扩展
yum install php71w-opcache.x86_64

配置:

zend_extension=opcache.so
[opcache]
;开启opcache
opcache.enable=1  

;CLI环境下,PHP启用OPcache
opcache.enable_cli=1

;OPcache共享内存存储大小,单位MB
opcache.memory_consumption=128  

;PHP使用了一种叫做字符串驻留(string interning)的技术来改善性能。例如,如果你在代码中使用了1000次字符串“foobar”,在PHP内部只会在第一使用这个字符串的时候分配一个不可变的内存区域来存储这个字符串,其他的999次使用都会直接指向这个内存区域。这个选项则会把这个特性提升一个层次——默认情况下这个不可变的内存区域只会存在于单个php-fpm的进程中,如果设置了这个选项,那么它将会在所有的php-fpm进程中共享。在比较大的应用中,这可以非常有效地节约内存,提高应用的性能。
这个选项的值是以兆字节(megabytes)作为单位,如果把它设置为16,则表示16MB,默认是4MB
opcache.interned_strings_buffer=8

;这个选项用于控制内存中最多可以缓存多少个PHP文件。这个选项必须得设置得足够大,大于你的项目中的所有PHP文件的总和。
设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。也就是说在200到1000000之间。
opcache.max_accelerated_files=4000

;设置缓存的过期时间(单位是秒),为0的话每次都要检查
opcache.revalidate_freq=60

;从字面上理解就是“允许更快速关闭”。它的作用是在单个请求结束时提供一种更快速的机制来调用代码中的析构器,从而加快PHP的响应速度和PHP进程资源的回收速度,这样应用程序可以更快速地响应下一个请求。把它设置为1就可以使用这个机制了。
opcache.fast_shutdown=1

;如果启用(设置为1),OPcache会在opcache.revalidate_freq设置的秒数去检测文件的时间戳(timestamp)检查脚本是否更新。
如果这个选项被禁用(设置为0),opcache.revalidate_freq会被忽略,PHP文件永远不会被检查。这意味着如果你修改了你的代码,然后你把它更新到服务器上,再在浏览器上请求更新的代码对应的功能,你会看不到更新的效果
强烈建议你在生产环境中设置为0,更新代码后,再平滑重启PHP和web服务器。
opcache.validate_timestamps=0 

;开启Opcache File Cache(实验性), 通过开启这个, 我们可以让Opcache把opcode缓存缓存到外部文件中, 对于一些脚本, 会有很明显的性能提升.
这样PHP就会在/tmp目录下Cache一些Opcode的二进制导出文件, 可以跨PHP生命周期存在.
opcache.file_cache=/tmp

查看phpinfo:

测试结果:

同样的接口从以前的几百毫秒提升到现在的50ms左右

 

禅道7.3升级到11.3版本,通过源代码方式升级(通用)

一、升级步骤

  1. 在我们 网站下载新版本的源码包,以.zip结尾。wget http://dl.cnezsoft.com/zentao/11.3/ZenTaoPMS.11.3.stable.zip
  2. 解压缩新的程序,覆盖到原来的目录。比如之前禅道安装的目录是在/home/wwwroot/zentao下面,则将代码覆盖到/home/wwwroot/zentao,操作方法:unzip ZenTaoPMS.11.3.stable.zip ;cd zentaopms/ 下面复制所有的文件到 /home/wwwroot/zentao下面粘帖。参考命令:rsync -azv zentaopms/ /home/wwwroot/zentao/
  3. 执行升级程序。假设禅道的访问路径是http://192.168.1.99/,升级路径为http://192.168.1.99/upgrade.php。
  4. 根据向导,选择对应的版本,按照提示进行即可。

注意:不要将原来的程序移走,再解压缩新的版本,注意是覆盖!不要拷贝成/home/wwwroot/zentao

          不要下载.exe结尾的程序进行升级,那样子会覆盖原来的数据!

二、插件兼容问题

如果升级之前有安装过第三方朋友开发的插件,需要注意检查下是否和新版本的禅道兼容。如果发现升级之后无法访问,可以替换一下禅道运行代码:

1、把禅道当前运行代码文件夹改名(一般默认是 zentao 或者 zentaopms);

2、到 禅道官网下载相同版本禅道的源码,解压后放在同目录下面,保持文件夹名字和之前禅道运行代码文件夹名称相同 。

3、把原先文件夹中的  config/my.php  www/data/upload/1  www/.ztaccess   www/.htaccess 拷贝到新禅道文件夹对应目录,再访问禅道试试。

安装Composer PHP Warning: copy(): SSL operation failed with code 1.

报错信息

[root@localhost ~]# php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" PHP Warning:  copy(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed in Command line code on line 1 Warning: copy(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed in Command line code on line 1 PHP Warning:  copy(): Failed to enable crypto in Command line code on line 1 Warning: copy(): Failed to enable crypto in Command line code on line 1 PHP Warning:  copy(https://install.phpcomposer.com/installer): failed to open stream: operation failed in Command line code on line 1 Warning: copy(https://install.phpcomposer.com/installer): failed to open stream: operation failed in Command line code on line 1

解决方法

  • 应该是CA证书验证失败造成的错误,下载个CA证书
[root@localhost ~]# wget http://curl.haxx.se/ca/cacert.pem [root@localhost ~]# mv cacert.pem /usr/local/openssl/ssl/certs/cacert.pem [root@localhost ~]# vim /yourpath/php.ini
  • 修改cafile路径,保存
[openssl]
; The location of a Certificate Authority (CA) file on the local filesystem
; to use when verifying the identity of SSL/TLS peers. Most users should
; not specify a value for this directive as PHP will attempt to use the
; OS-managed cert stores in its absence. If specified, this value may still
; be overridden on a per-stream basis via the "cafile" SSL stream context ; option.
;openssl.cafile=
openssl.cafile=/usr/local/openssl/ssl/certs/cacert.pem

使用nginx配置多个php-fastcgi负载均衡

配置还是非常简单的,充分体现了nginx的强大与配置的简单。

应用的最前端是一台nginx服务器,所有静态的内容都由nginx来处理,而将所有php的 请求都分摊到下游的若干台

运行PHP fastcgi守护进程的服务器中,这样可以以一种廉价的方案来实现对系统负载的分摊,扩展系统的负载能力。

三台php-fastcgi服务器的ip地址分别为:

          172.16.236.110 ,   172.16.236.111,     172.16.236.112

运行php-fastcgi进程时,需要让php-cgi监听到服务器的局域网地址(分别如上所示),而不是之前一般都是监听的

本地地址(127.0.0.1)。

以 172.16.236.110这台服务器为例:

 
/usr/local/php5/bin/php-cgi -b 172.16.236.110:9000

或许你用spawn-fcgi来启动php-fcgi,那么就是这样(供参考,其实也就是修改监听的地址和端口即可):

 
/usr/local/lighttpd/bin/spawn-fcgi -f /usr/local/php5/bin/php-cgi -a 172.16.236.110 -p 9000

又或许你是用php-fpm来管理php-fcgi,那么你需要修改php-fpm的配置:

 
vim  /usr/local/php5/etc/php-fpm.conf

找到这个配置项(其中的地址可能需要根据你自己环境来调整)



<value< span=”” style=”word-wrap: break-word;”> name=”listen_address“>127.0.0.1:9000>

修改为:

<value< span=”” style=”word-wrap: break-word;”> name=”listen_address>172.16.236.110:9000>



修改完毕后,重启你的php-fpm进程。

然后按照上面的步骤,依次修改其他php fastcgi服务器。

php方面的工作暂时就是这些,下面修改nginx。

 
vim  /usr/local/nginx/conf/nginx.conf

在配置文件的http段内增加类似如下的配置:

1
2
3
4
5
upstream myfastcgi { server 172.16.236.110 weight=1; server 172.16.236.111 weight=1; server 172.16.236.112 weight=1; }

我这里三台php fastcgi服务器的权重是相同的,所以其中的weight值都是1,如果你的php fastcgi服务器需要分主次,那么

可以通过调整其weight值来达到目的。比如以第一台服务器为主,其他两台为辅,则就是这样:

1
2
3
4
5
upstream myfastcgi { server 172.16.236.110 weight=1; server 172.16.236.111 weight=2; server 172.16.236.112 weight=2; }

然后找到原来nginx关于php fastcgi配置的部分,比如:

1
2
3
4
5
6
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

将其中的fastcgi_pass那一段改为:



fastcgi_pass myfastcgi;

其中的myfastcgi也就是上面刚刚配置的php fastcgi均衡器的名字了。

完了以后,重启nginx即可。

简单吧,就通过这么几个简单的配置,就可以实现一个经济高效的nginx、多php-fcgi的负载均衡解决方案了。

当然了,这样的方案运用到实际项目中 还需要进行一些细化的配置,主要是php方面还需要进一步配置