备忘录

备忘录的内容全部以评论的形式记录
http://www.nowamagic.net/librarys/veda/cate/PHP php的一些相关知识博客


本文固定链接: http://blog.wwllcchf.com/?p=296 | 吴文龙的个人博客

作者:wuwenlong 于2013年07月24日发表 &
备忘录 | 吴文龙的个人博客

备忘录:目前有2 条留言

  1. 板凳
    紫衣若兰:

    PHP多进程编程一
    使用PHP真正的多进程运行模式,适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。
    PHP有一组进程控制函数(编译时需要 –enable-pcntl与posix扩展),使得php能在*nix系统中实现跟c一样的创建子进程、使用exec函数执行程序、处理信号等功能。PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。
    常用的PCNTL函数
    pcntl_alarm ( int $seconds )设置一个$seconds秒后发送SIGALRM信号的计数器
    pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )为$signo设置一个处理该信号的回调函数。下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个“Caught SIGALRM”的例子:
    declare(ticks = 1);

    function signal_handler($signal) {
    print “Caught SIGALRM\n”;
    pcntl_alarm(5);
    }

    pcntl_signal(SIGALRM, “signal_handler”, true);
    pcntl_alarm(5);

    for(;;) {
    }

    ?>

    pcntl_exec ( string $path [, array $args [, array $envs ]] )在当前的进程空间中执行指定程序,类似于c中的exec族函数。所谓当前空间,即载入指定程序的代码覆盖掉当前进程的空间,执行完该程序进程即结束。
    $dir = ‘/home/shankka/’;
    $cmd = ‘ls’;
    $option = ‘-l’;
    $pathtobin = ‘/bin/ls’;

    $arg = array($cmd, $option, $dir);

    pcntl_exec($pathtobin, $arg);
    echo ’123′; //不会执行到该行
    ?>

    pcntl_fork ( void )为当前进程创建一个子进程,并且先运行父进程,返回的是子进程的PID,肯定大于零。在父进程的代码中可以用pcntl_wait(&$status)暂停父进程知道他的子进程有返回值。注意:父进程的阻塞同时会阻塞子进程。但是父进程的结束不影响子进程的运行。
    父进程运行完了会接着运行子进程,这时子进程会从执行pcntl_fork()的那条语句开始执行(包括此函数),但是此时它返回的是零(代表这是一个子进程)。在子进程的代码块中最好有exit语句,即执行完子进程后立即就结束。否则它会又重头开始执行这个脚本的某些部分。
    注意两点:
    1. 子进程最好有一个exit;语句,防止不必要的出错;
    2. pcntl_fork间最好不要有其它语句,例如:
    $pid = pcntl_fork();
    //这里最好不要有其他的语句
    if ($pid == -1) {
    die(‘could not fork’);
    } else if ($pid) {
    // we are the parent
    pcntl_wait($status); //Protect against Zombie children
    } else {
    // we are the child
    }

    pcntl_wait ( int &$status [, int $options ] )阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。使用$status返回子进程的状态码,并可以指定第二个参数来说明是否以阻塞状态调用:
    1. 阻塞方式调用的,函数返回值为子进程的pid,如果没有子进程返回值为-1;
    2. 非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。
    pcntl_waitpid ( int $pid , int &$status [, int $options ] )功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为-1时pcntl_waitpid与pcntl_wait一样。在pcntl_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息,这个参数可以用于pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid这些函数。
    例如:
    $pid = pcntl_fork();
    if($pid) {
    pcntl_wait($status);
    $id = getmypid();
    echo “parent process,pid {$id}, child pid {$pid}\n”;
    }else{
    $id = getmypid();
    echo “child process,pid {$id}\n”;
    sleep(2);
    }
    ?>

    子进程在输出child process等字样之后sleep了2秒才结束,而父进程阻塞着直到子进程退出之后才继续运行。
    pcntl_getpriority ([ int $pid [, int $process_identifier ]] )取得进程的优先级,即nice值,默认为0,在我的测试环境的linux中(CentOS release 5.2 (Final)),优先级为-20到19,-20为优先级最高,19为最低。(手册中为-20到20)。
    pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )设置进程的优先级。
    posix_kill可以给进程发送信号
    pcntl_singal用来设置信号的回调函数
    当父进程退出时,子进程如何得知父进程的退出
    当父进程退出时,子进程一般可以通过下面这两个比较简单的方法得知父进程已经退出这个消息:
    当父进程退出时,会有一个INIT进程来领养这个子进程。这个INIT进程的进程号为1,所以子进程可以通过使用getppid()来取得当前父进程的pid。如果返回的是1,表明父进程已经变为INIT进程,则原进程已经推出。
    使用kill函数,向原有的父进程发送空信号(kill(pid, 0))。使用这个方法对某个进程的存在性进行检查,而不会真的发送信号。所以,如果这个函数返回-1表示父进程已经退出。
    除了上面的这两个方法外,还有一些实现上比较复杂的方法,比如建立管道或socket来进行时时的监控等等。
    PHP多进程采集数据的例子
    /**
    * Project: Signfork: php多线程库
    * File: Signfork.class.php
    */

    class Signfork{
    /**
    * 设置子进程通信文件所在目录
    * @var string
    */
    private $tmp_path=’/tmp/’;

    /**
    * Signfork引擎主启动方法
    * 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
    * @param object $obj 执行对象
    * @param string|array $arg 用于对象中的__fork方法所执行的参数
    * 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])…
    * @return array 返回 array(子进程序列=>子进程执行结果);
    */
    public function run($obj,$arg=1){
    if(!method_exists($obj,’__fork’)){
    exit(“Method ‘__fork’ not found!”);
    }

    if(is_array($arg)){
    $i=0;
    foreach($arg as $key=>$val){
    $spawns[$i]=$key;
    $i++;
    $this->spawn($obj,$key,$val);
    }
    $spawns['total']=$i;
    }elseif($spawns=intval($arg)){
    for($i = 0; $i spawn($obj,$i);
    }
    }else{
    exit(‘Bad argument!’);
    }

    if($i>1000) exit(‘Too many spawns!’);
    return $this->request($spawns);
    }

    /**
    * Signfork主进程控制方法
    * 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
    * 2、$data收集子进程运行结果及数据,并用于最终返回
    * 3、删除子进程文件
    * 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
    * @param string|array $arg 用于对应每个子进程的ID
    * @return array 返回 array([子进程序列]=>[子进程执行结果]);
    */
    private function request($spawns){
    $data=array();
    $i=is_array($spawns)?$spawns['total']:$spawns;
    for($ids = 0; $idstmp_path.’sfpid_’.$cid;
    $data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
    unlink($tmpfile);
    }
    return $data;
    }

    /**
    * Signfork子进程执行方法
    * 1、pcntl_fork 生成子进程
    * 2、file_put_contents 将’$obj->__fork($val)’的执行结果存入特定序列命名的文本
    * 3、posix_kill杀死当前进程
    * @param object $obj 待执行的对象
    * @param object $i 子进程的序列ID,以便于返回对应每个子进程数据
    * @param object $param 用于输入对象$obj方法’__fork’执行参数
    */
    private function spawn($obj,$i,$param=null){
    if(pcntl_fork()===0){
    $cid=getmypid();
    file_put_contents($this->tmp_path.’sfpid_’.$cid,$obj->__fork($param));
    posix_kill($cid, SIGTERM);
    exit;
    }
    }
    }
    ?>

    参考网址:http://blog.zol.com.cn/2366/article_2365152.html

    2013-07-24 下午 12:08
  2. 沙发
    紫衣若兰:

    一、环境

    需要备份文件的服务器(服务器端):192.168.1.201 (CENTOS 5)
    接收备份文件的服务器(客户端) :192.168.1.202 (CENTOS 5)

    配置系统环境
    1、关闭SELINUX
    vi /etc/selinux/config #编辑防火墙配置文件
    #SELINUX=enforcing #注释掉
    #SELINUXTYPE=targeted #注释掉
    SELINUX=disabled #增加
    :wq #保存,关闭

    2、开启防火墙tcp 873端口(rsync默认端口)
    vi /etc/sysconfig/iptables #编辑防火墙配置文件
    -A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 873 -j ACCEPT
    :wq! #保存
    /etc/init.d/iptables restart #最后重启防火墙使配置生效

    yum install xinetd #安装
    vi /etc/xinetd.d/rsync #编辑配置文件,设置开机启动rsync
    disable = no #修改为
    /etc/init.d/xinetd start #启动(CentOS中是以xinetd 来管理rsync服务的)

    二、安装配置

    1.服务器端的配置

    A、安装rsync

    采用系统默认安装的rsync 用 yum install rsync

    如果采用自己编译安装的话,先到官网 http://rsync.samba.org/ 下载源文件 wget http://rsync.samba.org/ftp/rsync/src/rsync-3.0.9.tar.gz
    tar -zxvf rsync-3.0.9.tar.gz
    cd rsync-3.0.9.tar.gz
    ./configure
    make
    make install

    编译安装完成之后,编辑/etc/rsyncd.conf文件,如果没有则新建一个。

    vi /etc/rsyncd.conf
    #[globale] #公共信息配置
    strict modes= yes #是否监测密码文件的权限,如果该选项值为true那么密码文件只能被rsync服务器运行身份的用户访问,其他任何用户不可以访问该文件。默认值为true。
    port= 873 #default port默认端口
    log file= /var/log/rsyncd.log #同步日志文件,日志文件位置,启动rsync后自动产生这个文件,无需提前创建
    lock file= /var/run/rsyncd.lock #指定支持max connections参数的锁文件
    pid file= /var/run/rsyncd.pid #启动同步进程的文件
    motd file = /etc/rsyncd.motd #motd file 是定义服务器信息的,要自己写 rsyncd.motd 文件内容。当用户登录时会看到这个信息。这个可以不要
    max connections= 4 #指定该模块的最大并发连接数量以保护服务器,超过限制的连接请求将被告知随后再试。默认值是0,也就是没有限制。
    #[modules]
    [testlink] #备份模块 设置认证的模块名,这个自己定义
    uid= root #备份以什么身份进行,用户
    gid= root #用户group组
    path= /usr/local/apache/htdocs/testlink/upload_area #要备份的目录 指定该模块的供备份的目录树路径,该参数是必须指定的。
    read only= no #该选项设定是否允许客户上载文件。如果为true那么任何上载请求都会失败,如果为false并且服务器目录读写权限允许那么上载是允许的。默认值为true。
    write only = no #设置为no,客户端可下载文件,yes不能下载
    host allow= * #该选项指定哪些IP的客户允许连接该模块,可以设置多个,用英文状态下逗号隔开。
    hosts deny = * #禁止数据同步的客户端IP地址,可以设置多个,用英文状态下逗号隔开。
    auth users= wwyhy #该选项指定由空格或逗号分隔的用户名列表,只有这些用户才允许连接该模块。这里的用户和系统用户没有任何关系。
    secrets file= /etc/rsyncd.scrt #该选项指定一个包含定义用户名:密码对的文件。只有在”auth users”被定义时,该文件才有作用。文件每行包含一个username:passwd对。
    list = yes #显示Rsync服务端资源列表
    exclude = beinan/ samba/ #exclude 是排除的意思,也就是说,要把当前要同步的目录下的beinan和samba 排除在外; beinan/和samba/目录之间有空格分开。这个可以不要
    transfer logging = yes #这是传输文件的日志。这个可以不要
    ignore errors=yes #忽略IO错误

    [bugfree] #备份模块
    uid= root
    gid= root
    path= /usr/local/apache/htdocs/bugfree/BugFile #要备份的目录
    read only= no
    host allow= *
    auth users= wwyhy
    secrets file= /etc/rsyncd.scrt

    [redmine] #备份模块
    uid= root
    gid= root
    path= /usr/local/redmine-0.8.1/files #要备份的目录
    read only= no
    host allow= *
    auth users= wwyhy
    secrets file= /etc/rsyncd.scrt

    B、 添加一个密码文件

    vi /etc/rsyncd.scrt
    内容如下:
    wwyhy:123456 #(自己设置)

    C、改变权限为600

    chmod 600 /etc/rsyncd.scrt

    D、启动服务(如开有防火墙请允许873端口通过)

    rsync –daemon –config=/etc/rsyncd.conf &
    这里有时候这样不能启动服务,那么只能用/usr/local/bin/rsync –daemon –config=/etc/rsyncd.conf &

    常见问题:
    1、可能在执行了rsync –daemon –config=/etc/rsyncd.conf & 之后会出现这个问题
    /usr/local/bin/rsync: error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory
    解决办法:在/etc/ld.so.conf中加一行/usr/local/lib,运行ldconfig。再运行/usr/local/rsync/bin/rsync –daemon,就可以了。报错的原因可能之前更新过iconv库.
    如果你不是root,ldconfig也运行不了的,解决的方法就是,设置环境变量 LDFLAGS=-L/usr/local/lib

    2、还有可能在执行了rsync –daemon –config=/etc/rsyncd.conf & 之后会出现这个问题
    failed to create pid file /var/run/rsyncd.pid: File exists
    解决办法:将这个文件删掉,rm -rf /var/run/rsyncd.pid

    3、可能在同步文件的时候会出现下面的错误:
    rsync: failed to connect to 10.8.5.25: Connection refused (111)
    rsync error: error in socket IO (code 10) at clientserver.c(122)
    解决方法:安装 xinetd 来解决。

    这里也可以让电脑启动后自动启动服务
    在usr/local/中创建一个文件夹名字叫rsync,然后再rsync中创建一个执行文件rsync.sh
    mkdir /usr/local/rsync
    vi /usr/local/rsync/rsync.sh 然后给文件加上执行权限 chmod -755 /usr/local/rsync/rsync.sh
    在rsync.sh 中加/usr/local/bin/rsync –daemon –config=/etc/rsyncd.conf &
    把rsync.sh 文件地址加到 /etc/rc.local文件中/usr/local/rsync/rsync.sh

    如果开启了定义服务信息了
    #vi /etc/rsyncd.motd
    Welcome to 10.8.5.25 server!
    #wq

    2.配置客户端

    A、安装

    采用系统默认安装的rsync 用 yum install rsync

    如果采用自己编译安装的话,先到官网 http://rsync.samba.org/ 下载源文件 wget http://rsync.samba.org/ftp/rsync/src/rsync-3.0.9.tar.gz
    tar -zxvf rsync-3.0.9.tar.gz
    cd rsync-3.0.9.tar.gz
    ./configure
    make
    make install

    B、添加密码文件

    vi /etc/rsyncd.scrt (没有就新建)
    内容如下:
    123456 (注意:这里只填写密码)

    C、改文件权限为600

    chmod 600 /etc/rsyncd.scrt

    三、开始备份

    可以在客户端通过man rsync指令来查看备份指令

    我们用脚本来自动执行备份
    例如:rsync -avzrtopg –progress –delete –password-file=密码文件路径 username@需要备份的主机IP::备份里的模块名称 接收备份文件的路径

    在客户端,在/root建一个脚本文件
    vi backup

    添加内容如下:
    #1.192.168.1.201上的testlink附件备份指令
    rsync -avzrtopg –progress –delete –password-file=/etc/rsyncd.scrt wwyhy@192.168.1.201::testlink /home/wangwei/testlink/upload_area
    (如果要把客户端的文件同步到服务器上,rsync -avzrtopg –progress –delete –password-file=/etc/rsyncd.scrt /home/wangwei/testlink/upload_area wwyhy@192.168.1.201::testlink)

    #2.192.168.1.201上的bugfree附件备份指令
    rsync -avzrtopg –progress –delete –password-file=/etc/rsyncd.scrt wwyhy@192.168.1.201::bugfree /home/wangwei/bugfree/BugFile

    #3.192.168.1.201上的redmine附件备份指令
    rsync -avzrtopg –progress –delete –password-file=/etc/rsyncd.scrt wwyhy@192.168.1.201::redmine /home/wangwei/redmine-0.8.1/files

    chmod u+x backup

    每晚2.30自动执行
    vi /etc/crontab

    30 2 * * * root /root/backup

    如果想先测试一下执行文件则 ./backup

    重新启动任务计划
    /etc/init.d/crond restart 或则 service crond restart

    至此,通过rsync 来同步文件的配置已经配置成功。

    rsync 的部分参数说明:

    -v,-verbose 详细模式输出
    -z,-compress 压缩
    -r,-recursive 对子目录以递归模式处理
    -topg 保持文件的原有属性,如属主、时间等参数
    -c,-checksum 打开校验开关,强制对文件传输进行校验
    -a,-archive 归档模式,表示以递归方式传输文件,并保持所有文件属性,等于-rlptgoD
    -R,-relative 使用相对路径信息
    -q,-quiet 精简输出模式
    –progress 显示出详细的进展情况
    –delete 如果服务器删除了这一文件,客户算也会相应的把文件删除,保持真正的一致
    –exclude 不包含某些文件

    参考网址:
    http://blog.s135.com/post/259/
    http://www.centos.bz/2011/06/rsync-server-setup/
    http://www.osyunwei.com/archives/3769.html
    http://sunshyfangtian.blog.51cto.com/1405751/484945

    2013-08-01 下午 9:33