标签归档:nginx

关于php-dws的性能测试及定位

1.  与fcgi 的性能测试对比图:
[attachment=1203]

由图可见,在网络情况好的下几乎没有区别。

2. php-dws 的特点就是响应结果直接输出给浏览器,而不是WEB服务器,所以可能适合某些特殊需要长连接或较长处理时间的请求(如comet应用)。

[DOC] php-dws 的特别应用示例

php-dws 被设计成直接输出给用户, 这样 php script 在 HTTP 请求中被摆到了一线位置, 变成了用户直接接触, 而不像以往一样总是成为 webserver 的附属.

所以在熟悉HTTP协议后会变得非常有趣,可以实现一些在 fastcgi 中完不成的任务了。

后面会陆续举例说明,请期待!

[DOC] php-dws 的 TODO 列表

TODO List

1. 像apache的prefork模块一样动态调节子进程数量 (min_spare,max_spare,…)
2. 支持 SSL
3. 缩减阻塞在 accept() 上的子进程数量, 而不是所有空闲进程都在 accept()
4. 提供较为完善的工作日志和错误日志, 像 webserver 的 access_log, error_log 一样
5. 提供进程工作状态的查看, 请求数量的统计分析等
6. 完善的英文说明和介绍

[TOOL] php-dws 启动关闭控制脚本(php-dwsctl)

把下面的代码内容复制保存为 php-dwsctl 放在 /usr/bin 或 /usr/local/bin 之类的 PATH 路径中,那么就可以简单的调用以下命令来控制:
1. 启动 php-dwsctl start
2. 关闭 php-dwsctl stop
3. 重开 php-dwsctl restart

#!/bin/sh
# php-dwsctl <start|stop|restart>
#
php_dws=/usr/bin/php-dws
php_tmp_dir=/tmp

php_dws_children=16
php_dws_max_requests=512
php_user=nobody
php_group=

php_sock=$php_tmp_dir/php-dws.sock
pid_file=$php_tmp_dir/php-dws.pid

case "$1" in
  start)
    # check binary
    if [ ! -x $php_dws ] ; then
      echo "ERROR: Executable binary php-dws not found: $php_dws"
      exit 1
    fi
    # tmp dir
    if [ ! -d $php_tmp_dir ] ; then
      mkdir -m 0777 $php_tmp_dir
    fi
    if [ ! -d $php_tmp_dir ] || [ ! -w $php_tmp_dir ] ; then
      echo "ERROR: Temporary dir not exists or cannt' be written"
      exit 1
    fi
    # check upload & session
    php_up_dir=`$php_dws -i | grep upload_tmp_dir | cut -d ' ' -f 3`
    php_sess_dir=`$php_dws -i | grep session.save_path | cut -d ' ' -f 3`
    if [ "$php_up_dir" != "" ] && [ ! -d $php_up_dir ] ; then
      if ! mkdir -m 0777 $php_up_dir ; then
        echo "WARNING: upload_tmp_dir not exists"
      fi
    fi
    if [ "$php_sess_dir" != "" ] && [ ! -d $php_sess_dir ] ; then
      if ! mkdir -m 0777 $php_sess_dir ; then
        echo "WARNING: upload_sess_dir not exists"
      fi
    fi
    # check running
    if test -f $pid_file ; then
      pid=`cat $pid_file`
      if kill -0 $pid > /dev/null 2>&1 ; then
        echo "[ERROR] PHP-dws is running (pid=$pid)"
        exit 1
      fi
    fi
    # server options
    php_options=""
    if [ "$php_sock" != "/tmp/php-dws.sock" ] ; then
      php_options="$php_options -b$php_sock"
    fi
    if [ "$php_dws_children" != "16" ] ; then
      php_options="$php_options -C$php_dws_children"
    fi
    if [ "$php_dws_max_requests" != "512" ] ; then
      php_options="$php_options -C$php_dws_max_requests"
    fi
    run_uid=`id -u`
    if [ "$run_uid" = "0" ] ; then
      if [ "$php_user" != "" ] ; then
        php_options="$php_options -U$php_user"
      fi
      if [ "$php_group" != "" ] ; then
        php_options="$php_options -G$php_group"
      fi      
    fi
    php_options="$php_options -P$pid_file" 
    # starting & check
    echo -n "[INFO] Starting php-dws ... "
    $php_dws $php_options
    pid=`cat $pid_file`
    if kill -0 $pid > /dev/null 2>&1 ; then
      echo "OK(pid=$pid)"
    else
      echo "FAILED"
      exit 1
    fi
  ;;      
  stop)
    if test -f $pid_file ; then
      pid=`cat $pid_file`
      rm -f $pid_file
      if kill -TERM $pid > /dev/null 2>&1 ; then
        echo -n "[INFO] Killing running PHP-dws(pid=$pid) ..."
        while kill -0 $pid > /dev/null 2>&1 ; do
          echo -n "."
          sleep 1
        done
        echo " DONE"
        exit 0
      fi
    fi
    echo "[ERROR] PHP-dws may have not started?"  
    exit 1
  ;;
  restart)
    $0 stop
    $0 start
  ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
  ;;
esac
exit 0

[PHP] php-dws 运行启动选项及说明

1. 运行 php-dws -h 可以看到以下提示及说明, 注意不加任何选项则会以默认选项启动 php-dws.

Usage: php-dws [-?hvimn] [-b bindpath] [options]

Options:
-b <bindpath> Bind to Unix domain socket (default: /tmp/php-dws.sock)
-c <path>|<file> Look for php.ini file in this directory
-C <num> Number of children to fork (default: 16)
Zero value to disable for debugging only.
-d foo[=bar] Define INI entry foo with value 'bar'
-G <group> change to group-id of this group name
(default: primary group of user if -U is given)
-h -? This help
-i PHP information
-m Show compiled in modules
-n No php.ini file will be used
-P <file> PID-file for master process (ignored in no-fork mode)
-R <num> Max number of requests to be handled for every child.
Zero value to unlimited. (default: 512)
-U <user> Change processes owner to user-id
-v Version info

2. php-dws 默认绑定 /tmp/php-dws.sock, 但可以通过 -b 来改变, 但如何你不小心多运行了几次 php-dws 它不像 tcp 会监测到端口被占用而报错, 而是会覆盖这个 socket 文件同时运行着, ps 时多注意一下

3. 停止 php-dws 服务, php-dws 提供 -P 选项来保存主进程的 pid,方便用户做控制脚本,默认不设置的,所以请直接用
killall php-dws 来终止服务

强烈建议用统一提供的 php-dwsctl 来管理和控制启动或停止, 参见这个贴子

4. 选项说细说明(所有选项均为可选):
-b <bindpath> 指定要绑定的 Unix 套接字路径 (默认: /tmp/php-dws.sock)
-c <path>|<file>指定搜寻 php.ini 的文件或目录
-C <num> 要生成的子进程数量, 默认为 16.
设为 0 则不生成子进程而改为前台单任务运行主要用于调试/
-d foo[=bar] 定义 php的 ini 项目的 foo 值为 bar
-G <group> 服务运行的用户组(root启动时专用, 如果指定了 -U 则默认会用该用户的第一用户组)
-h -? 打印这个帮助页面
-i 文本形式显示 phpinfo
-m 显示编译或加载了的模块
-n 不采用任何 php.ini 文件
-P <file> 指定主进程的 pid 存入路径, 默认为不存.
-R <num> 指定每个工作子进程处理的最大请求数(达到上限后结束进程,由主进程重新生成一个新的进程,避免长时运行有内存泄露等问题)
默认为 512,设为 0表示不限制。
-U <user> 服务运行的用户属主(root启动时专用)
-v 显示php和zend的版本信息

[nginx] nginx_http_dwsgi_module 配置说明

nginx_http_dwsgi_module 改自 fastcgi module, 它的配置和 fastcgi 很像, 但选项更少一些, 指令名称由 fastcgi_xxxx 改变为 dwsgi_xxx
下面列出常用的指令及其异同,详细可参见 fastcgi 的官方配置WIKI.

一、各项指令及异同说明
1. dwsgi_pass 参见 fastcgi_pass
基本一致, 但如果传入的参数不是 unix: 开头的套接字地址或者命名的upstream列表中未包含unix:套接字的将在运行时
产生错误, 直接返回 500 internal server error
2. dwsgi_index 参见 fastcgi_index
3. dwsgi_split_path_info 参见 fastcgi_split_path_info

4. dwsgi_connect_timeout 参见 fastcgi_connect_timeout
dwsgi_send_timeout 参见 fastcgi_send_timeout
dwsgi_read_timeout 参见 fastcgi_read_timeout
默认值调为 6秒而不是60秒

5. dwsgi_buffer_size 参见 fastcgi_buffer_size
dwsgi_pass_request_headers 参见 fastcgi_pass_request_headers
dwsgi_buffers 参见 fastcgi_buffers
dwsgi_busy_buffers_size 参见 fastcgi_busy_buffers_size
dwsgi_param 参见 fastcgi_param

6. dwsgi_next_upstrem 参见 fastcgi_next_upstream
去掉支持 http_404 http_503 http_500

二、典型配置案例 (也可用作 fastcgi 配置, :)

1. 把参数和通用选项存入 dwsgi.conf

## DWSGI/PHP-dws
##
dwsgi_pass unix:/tmp/php-dws.sock;
dwsgi_param SCRIPT_FILENAME $document_root$dwsgi_script_name;
dwsgi_param PATH_INFO $dwsgi_path_info;
dwsgi_param QUERY_STRING $query_string;
dwsgi_param REQUEST_METHOD $request_method;
dwsgi_param CONTENT_TYPE $content_type;
dwsgi_param CONTENT_LENGTH $content_length;

dwsgi_param SCRIPT_NAME $dwsgi_script_name;
dwsgi_param REQUEST_URI $request_uri;
dwsgi_param DOCUMENT_URI $document_uri;
dwsgi_param DOCUMENT_ROOT $document_root;
dwsgi_param SERVER_PROTOCOL $server_protocol;

dwsgi_param GATEWAY_INTERFACE CGI/1.1;
dwsgi_param SERVER_SOFTWARE nginx/$nginx_version;

dwsgi_param REMOTE_ADDR $remote_addr;
dwsgi_param REMOTE_PORT $remote_port;
dwsgi_param SERVER_ADDR $server_addr;
dwsgi_param SERVER_PORT $server_port;
dwsgi_param SERVER_NAME $server_name;
## File END

2. locations 的配置示范:

## PHP scripts
location ~ \.php$ {
if (-d $request_filename) {
rewrite ^ $uri/ redirect;
}
if (!-f $request_filename) {
return 404;
}
include dwsgi.conf;
}

## PHP with pathinfo
location ~ \.php/ {
try_files $uri $uri/ @dwsgi;
}

## DwsCGI handler
location @dwsgi {
dwsgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$dwsgi_script_name) {
return 404;
}
include dwsgi.conf;
}

三、重启nginx服务即可, 如果需要兼容同时运行 php-fastcgi 和 php-dws 可以给 php-dws 指定一个新的后缀名来配置, 比如 .phpd

[综合] 发布 nginx/php-dws beta1

如果您还不了解什么是 php-dws, 可以先看看这个贴子:  关于 php-dws

2010/10/09: nginx/php-dws 作为全新的工作方式还需更多的实践检验,此次发布的beta1 是第一个版本,目前只能运行在 Unix 类的系统中。

一、下载:
1. nginx_http_dwsgi_module: (搭配 nginx-0.8.x , 较低版本未测试)
http://www.hightman.cn/down/nginx_0.8_dwsgi_module_beta1.tgz

2. php-dws: (搭配 php-5.3.x 开发, 但应当都兼容能用)
http://www.hightman.cn/down/php53_sapi_dws_beta1.tgz

二、安装php-dws
1. 将下载的 php53_sapi_dws_beta1.tgz 放到 php-5.3.3/ 这样的源码目录,
运行以下指令直接解压获得 sapi/dws/ 目录:
tar xvzf php53_sapi_dws_beta1.tgz

2. 如果你的 php-5.3.3 没有打过我之前发布的补丁, 请参见这个贴子修补一下, 以便更好的运行dws以及fastcgi.

3. 在 php 的源码目录中运行 ./buidconf –force (前提是安装了autoconf相关的工具)
注意: 如果提示什么vcs清理出错, 则可以先手动删除 autom4te.cache 目录然后运行 autoreconf, 如果还不行就听从它的建议采用 autoconf 2.13吧

4. 如果上述步骤都成功,尝试运行一下 ./configure –help | grep dws 应当能看到下面信息:
–enable-dws EXPERIMENTAL: Enable building DWS version of PHP

5. 去除旧编译选项中的 –with-apxs , –with-apxs2, –enable-cgi, –enable-fpm 等其它 sapi 的选项.
加入 –disable-cgi –enable-dws 重新 configure 和编译 php.
如果最终编译连接失败尝试 make clean 后再 make;
小提示如果你忘了以前的编译选项,你可以直接修改旧记录 config.nice 然后运行 ./config.nice 即可

6. 如果编译成功应当能在 sapi/dws/ 中看到 php-dws 了,试试以下指令:
sapi/dws/php-dws -h

7. 如果一切顺利就可以 make install 了,将 php-dws 安装到 $prefix/bin 中了

8. php-dws 的启动和关闭, 您可以直接在命令行启动和关闭它, 具体的选项参见其它贴子
启动: $prefix/bin/php-dws (默认会监听在 /tmp/php-dws.sock)
关闭: killall php-dws
建议使用统一提供的 php-dwsctl (位于源码包 sapi/dws/php-dwsctl) 参见这个贴子

三、安装 nginx_http_dwsgi_module

1. 模块是针对 0.8.x 系列开发和测试的, 其它版本未经测试, 建议用同样的最新的 0.8.x 版本

2. 将下载的 nginx_0.8_dwsgi_module_beta1.tgz 放到 ngx-0.8.50/ 这样的源码目录,
运行以下指令直接解压获得 src/addon/dwsgi 目录:
tar xvzf nginx_0.8_dwsgi_module_beta1.tgz

3. 运行 ./configure 重新配置 nginx ,在原有基础选项上增加:
–add-module=src/addon/dwsgi

4. 然后正常的编译 make clean ; make ; make install 即可

四、安装全部结束,就可以开始配置相应的选项来启动它了,具体的配置选项请分见其它贴子。

1. nginx_http_dwsgi_module 配置说明
2. php-dws 运行选项及说明

[DOC] 关于php-dws的重要说明

1. 什么是 php-dws ?

php-dws 是 PHP Direct Web Server 的缩写, 是针对 php 的一个新型 sapi 工作模块,
通过 dwsgi 协议与 web server (如 nginx) 协同工作.
(dwsgi: Direct Web Server Gateway Interface)

之所以叫 Direct , 是因为它在执行 php 脚本过程中是直接把输出结果传递给 http client 的
而不是转交 webserver(nginx) 再由 webserver 发送给 http client.

在多数情况下, php-dws 可以用于取代 php/fastcgi 的工作, 并且能更出色的完成.

2. 和 PHP/FastCGI 相比较有什么不同?

1) 我们先看一下下面的示意图了解它们的不同工作原理:

      [PHP/FATCGI]
      ============

            i)request header+body     ii)request header+body
           +~~~~>~~~~>~~~~>~~~+     +~~~~~>~~~~~>~~~~>~~~~~+
           |                  |     |                      |
          /|\                \|/   / \                    \|/
      WebVisitor             Web Server               FastCGIServer
        (user)                (nginx)                  (php-cgi)
          /|\                \ /   /|\                    \ /
           |                  |     |                      |
           +~~~<~~~~<~~~~<~~~~+     +~~~~~<~~~~~<~~~~<~~~~~+
          iv)response header+body    iii)response header+body

      [PHP/DWSGI]
      ===========

            i)request header           ii)request header
           +~~~~>~~~~>~~~~>~~~+     +~~~~~>~~~~~>~~~~>~~~~~+
           |                  |     |                      |
          /|\                \|/   / \                    \|/
      WebVisitor             Web Server               DWSCGI Server
        (user)                (nginx)                   (php-dws)
       /|\  \ /                                         /|\   \ /
        |    |                                           |     |
        |    +~~~~~~~~>~~~~~~~~~~~~>~~~~~~~~~~~~>~~~~~~~~+     |
        |     iii) request body                                |
        |                                                      |
        +~~~~~~~~~~~~~<~~~~~~~~~~~<~~~~~~~~~~~~~<~~~~~~~~~~~~~~+   
        iv)response header+body

2) 由图可以看出 DWSGI 相比 FASTCGI 有如下明显优势:

i) 节省了很多数据传输, 大大降低繁忙时期的 IO 负载和性能, 由于通常 HTTP 数据都不大,
所以测试数据并不是很明显 100KB 左右的 HTTP 输出大约提升 10~15% 的性能, 输出数据
越大则性能提升越明显;

ii) 给了 php 更直接的输出操控权限, 真正让 php 程序员有机会直接构造自己的 HTTP 输出,
而不再需要受到 webserver 的牵制, 典型的一个情况就是对于长时运行的脚本可以做到
一边执行一边输出, 如下代码:

for($i=1;$i<100;$i++) 
{ 
  echo "$i\n";
  ob_flush();
  flush();
  sleep(1); 
}

iii) 从此与那恼人的 502 Gateway error 说 byebye!

3) DWSGI 的不足之处, 并不是说 dwsgi 就绝对好了, 它也有以下缺陷:
i) 由于采用 unix domain socket 来实现描述字传递, 所以必须和 web server 同一台服务器;
ii) 目前不支持 win32 系统;
iii) 目前不支持 SSL 传输, 即 https.

3. DWSGI 是个怎么样子的协议?

出于FastCGI的优秀设计以及移植的方便考虑, DWSGI 几乎采用和 FastCGI 一样的协议格式, 但
略有不同.

1) 由于需要传递描述字, 所以 dwsgi server 只能绑定运行在 Unix domain socket 上;

2) 在首次会话发送请求头部时(16bytes), 采用 Sendmsg() 将 HTTP 连接的 socket fd
一并传递给 dwsgi server.

3) dwsgi 收到完整的请求后会发送一个结束包给 webserver, webserver收到后就关闭自身的
HTTP 连接描述符, 把连接读写的权限完全地交给 dwsgi, dwsgi 则可以直接将运行结果以
标准的 HTTP 协议规范发送给用户.

而 fastcgi 是全程保持和 webserver 相连, 输出结果先转交 webserver 由 webserver
作相应的缓冲和处理再转交给用户.

4. php-dws 的工作方式是如何的?

当前的 php-dws 是从原 sapi/cgi 的代码直接修改而来的, 还比较粗糙只支持固定进程数的. 计划
将来改变采用动态 prefork, 并记录相应的进程状态以及统一的配置文件等.

此外, php-dws 把子进程数和每个进程处理的最大请求数均改为启动选项而不再是环境变量, 同时支持
指定较低权限的用户身份来运行.