标签归档:php

swoole —— process

  • 创建进程
  • 进程间通信
    • 管道
    • 队列
    • 信号
  • 其他
    • 执行系统命令
    • 回收
    • 守护进程
    • 命名进程
    • CPU亲和性

swoole_process

swoole的进程对比pcntl扩展进程的优势:

  • pcntl无法用在fpm/apache
  • pcntl没有提供进程间通信的功能
  • pcntl不支持重定向标准输入和输出
  • pcntl只提供了fork这样原始的接口,容易使用错误
  • swoole_process提供了比pcntl更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。

创建进程

原型:

参数:

  • function: 子进程创建成功后要执行的函数
  • redirect_stdin_stdout: 重定向子进程的标准输入和输出。 启用此选项后,在进程内echo将不是打印屏幕,而是写入到管道。读取键盘输入将变为从管道中读取数据。 默认为阻塞读取。
  • create_pipe: 是否创建管道,启用redirect_stdin_stdout后,此选项将忽略用户参数,强制为true 如果子进程内没有进程间通信,可以设置为false

返回值:

swoole_process对象,数据结构包括了管道描述符和回调函数。

进程间通信

管道

当创建进程时,进行了输入输出重定向,swoole就会帮我们自动创建管道。

这里运行程序并不会输出process: echo 1。因为输出意见被输入到管道中去了。

这里我们关闭输出输入重定向,并且开启管道。进行一个简单的案例说明:

队列

信号

父进程中注册信号(子进程被kill触发),自进程中注册信号(本身被kill触发)。

开启子进程后,父进程立即杀死子进程,触发子进程信号事件。子进程关闭后,触发父进程信号事件。

其他

执行系统命令

会输出 ps -ef执行后内容。

回收

子进程结束必须要执行wait进行回收,否则子进程会变成僵尸进程

守护进程

使当前进程脱变为一个守护进程。

命名进程

CPU亲和性

设置CPU亲和性,可以将进程绑定到特定的CPU核上。

创建自己的Composer包

事先准备:

  1. Github帐号 以及 composer帐号
  2. git 以及 composer

第一步:

创建一个Github库,并且创建项目上传到Github上。

在本地创建项目

第二部:

初始化composer.json

初始化完成后再次提交到Github

第三部:

Github上的项目提交到packagist.org上。

1、 点击submit, 输入仓库地址

2、 确认提交

3、 成功提交

4、 在Github注册hook,当我们对仓库触发事件时候,同步到packagist.org

第四步:

完成composer项目

1、 编辑 composer.json 添加需要的信息

在这里我加上了这个项目的autoload规则。

可以看到,我使用了psr-4规则。那么要想通过autoload访问它,必须把命名空间写成namespace Pangou\HelloWorld;

2、 完成项目内容

通过autoload的规则,我们创建src目录,并且在src目录下创建了Greeting.php

3、 上传至Github

由于我们之前设置了hook,此时packagist.org上的项目将被会同步。

之前项目的特征码为:596a0f66098ba40ef481d09dd331c96760ab91b8,
现在为:bdefdc5d407f3bf6b4b1deabe44d14d0a4eb30da,同步成功。

第五步:

至上一步,一个composer项目基本完成。我们就可以在其他项目中进行使用。

接下来就是编写我们自己的代码,然后运行它。

在项目根目录下创建index.php

然后再项目根目录下运行

浏览器访问127.0.0.1:8080

成功调用。

第六步:

为项目打包。

刚刚创建项目使用的命令是:composer require pangou/hello-world dev-master

加了一个dev-master,表示我们项目正在开发阶段,还未正式发布。如果不添加的话是composer不下来的。

接下来就为项目打包。

1、 创建并切换到分支,并且将其推送到服务器上去。

接下来打包

可以看到packagist.org上项目已经存在0.1版本了。

把刚刚的项目删了,使用composer require pangou/hello-world重新创建。这次会发现,可以创建了。

composer.json中的版本也变成了0.1.0版本。 重新测试,一样可以答应出hello world!!!


over

Libevent(一)

一、什么是libevent

libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。——引用自百度百科

需要注意以下几点:

  1. libevent是一个事件触发的网络库
  2. 可以跨平台(windows、linux、bsd等)
  3. 使用select、epoll(linux)、kqueue(freebsd)、IOCP(windows)等系统调用管理事件机制(IO复用)
  4. libevent在Linux环境下默认采用epoll作为IO多路复用方法

用户线程使用libevent则通常按以下步骤:

  1. 用户线程通过event_init()函数创建一个event_base对象。event_base对象管理所有注册到自己内部的IO事件。多线程环境下,event_base对象不能被多个线程共享,即一个event_base对象只能对应一个线程。
  2. 然后该线程通过event_add函数,将与自己感兴趣的文件描述符相关的IO事件,注册到event_base对象,同时指定事件发生时所要调用的事件处理函数(event handler)。服务器程序通常监听套接字(socket)的可读事件。比如,服务器线程注册套接字sock1的EV_READ事件,并指定event_handler1()为该事件的回调函数。libevent将IO事件封装成struct event类型对象,事件类型用EV_READ/EV_WRITE等常量标志。
  3. 注册完事件之后,线程调用event_base_loop进入循环监听(monitor)状态。该循环内部会调用epoll等IO复用函数进入阻塞状态,直到描述符上发生自己感兴趣的事件。此时,线程会调用事先指定的回调函数处理该事件。例如,当套接字sock1发生可读事件,即sock1的内核buff中已有可读数据时,被阻塞的线程立即返回(wake up)并调用event_handler1()函数来处理该次事件。
  4. 处理完这次监听获得的事件后,线程再次进入阻塞状态并监听,直到下次事件发生。

—— 引用自Memcached网络模型

二、php使用libevent扩展实现高性能服务器

此处不再赘述php-libevent扩展的安装。
上述在描述libevent步骤时候,用的并非php函数,php实现是略微有区别,大致流程不变。

在之前的文章『php-socket 实现简单服务器』中,我们使用stream+select的方式来实现一个简单的服务器。

现在我们就使用libevnet来实现更高性能的服务器。

附上libevent的常量

io复用

1、基本概念

多路复用模型是对多个IO操作进行检测,返回可操作集合,这样就可以对其进行操作了。这样就避免了阻塞IO不能随时处理各个IO和非阻塞占用系统资源的确定。多路复用适用如下场合:
1. 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
2. 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
3. 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
4. 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
5. 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

2、socket_select

socket_select()等待sockets打开的连接事件。socket_select()调用系统的select(2)函数来工作:前面三个参数是你要使用的socket的数组;你可以对其读取,写入和获取异常(分别针对三个参数)。

3、 php实现多路复用

大致流程见下图:
io复用图

php-socket 实现简单服务器

Socket基础知识

我们可以把Socket当做是一种数据结构,客户端和服务器间通过这种数据结构来交换数据。服务器开始监听连接,当客户端想要连接服务器时,会通过服务器监听的端口开启一个会话,服务器收到客户端的请求后,建立连接完毕,然后继续监听下一次连接。

要产生一个Socket,你我们需要三个变量:一个协议(protocol)、一个socket类型(socket type)和一个公共协议类型(common protocol type)。下面将会详细介绍各个部分的具体内容。

协议

  • AF_INET:这是大多数用来产生socket的协议,使用TCP或UDP来传输,用在IPv4的地址
  • AF_INET6:与上面类似,不过是来用在IPv6的地址
  • AF_UNIX:本地协议,使用在Unix和Linux系统上,它很少使用,一般都是当客户端和服务器在同一台及其上的时候使用

Socket类型

  • SOCK_STREAM:这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
  • SOCK_DGRAM:这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
  • SOCK_SEQPACKET:这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
  • SOCK_RAW:这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
  • SOCK_RDM:这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序

公共协议

  • ICMP:互联网控制消息协议,主要使用在网关和主机上,用来检查网络状况和报告错误信息
  • UDP:用户数据报文协议,它是一个无连接,不可靠的传输协议
  • TCP:传输控制协议,这是一个使用最多的可靠的公共协议,它能保证数据包能够到达接受者那儿,如果在传输过程中发生错误,那么它将重新发送出错数据包。

socket示例

stream示例

附:
socket系列方法

  • socket_accept() 接受一个Socket连接
  • socket_bind() 把socket绑定在一个IP地址和端口上
  • socket_close() 关闭一个socket资源
  • socket_connect() 开始一个socket连接
  • socket_create_listen() 在指定端口打开一个socket监听
  • socket_create() 产生一个socket,相当于产生一个socket的数据结构
  • socket_get_option() 获取socket选项
  • socket_getpeername() 获取远程主机的ip地址
  • socket_getsockname() 获取本地socket的ip地址
  • socket_listen() 监听由指定socket的所有连接
  • socket_read() 读取指定长度的数据
  • socket_readv() 读取从分散/聚合数组过来的数据
  • socket_recv() 从socket里结束数据到缓存
  • socket_recvfrom() 接受数据从指定的socket,如果没有指定则默认当前socket
  • socket_recvmsg() 从iovec里接受消息
  • socket_select() 多路选择
  • socket_send() 这个函数发送数据到已连接的socket
  • socket_sendmsg() 发送消息到socket
  • socket_sendto() 发送消息到指定地址的socket
  • socket_set_block() 在socket里设置为块模式
  • socket_set_nonblock() socket里设置为非块模式
  • socket_set_option() 设置socket选项
  • socket_shutdown() 这个函数允许你关闭读、写、或者指定的socket
  • socket_write() 写数据到socket缓存

stream系列方法

  • stream_bucket_append函数:为队列添加数据 
  • stream_bucket_make_writeable函数:从操作的队列中返回一个数据对象
  • stream_bucket_new函数:为当前队列创建一个新的数据
  • stream_bucket_prepend函数:预备数据到队列 
  • stream_context_create函数:创建数据流上下文
  • stream_context_get_default函数:获取默认的数据流上下文
  • stream_context_get_options函数:获取数据流的设置
  • stream_context_set_option函数:对数据流、数据包或者上下文进行设置
  • stream_context_set_params函数:为数据流、数据包或者上下文设置参数
  • stream_copy_to_stream函数:在数据流之间进行复制操作
  • stream_filter_append函数:为数据流添加过滤器
  • stream_filter_prepend函数:为数据流预备添加过滤器
  • stream_filter_register函数:注册一个数据流的过滤器并作为PHP类执行
  • stream_filter_remove函数:从一个数据流中移除过滤器
  • stream_get_contents函数:读取数据流中的剩余数据到字符串
  • stream_get_filters函数:返回已经注册的数据流过滤器列表
  • stream_get_line函数:按照给定的定界符从数据流资源中获取行
  • stream_get_meta_data函数:从封装协议文件指针中获取报头/元数据
  • stream_get_transports函数:返回注册的Socket传输列表
  • stream_get_wrappers函数:返回注册的数据流列表
  • stream_register_wrapper函数:注册一个用PHP类实现的URL封装协议
  • stream_select函数:接收数据流数组并等待它们状态的改变
  • stream_set_blocking函数:将一个数据流设置为堵塞或者非堵塞状态
  • stream_set_timeout函数:对数据流进行超时设置
  • stream_set_write_buffer函数:为数据流设置缓冲区
  • stream_socket_accept函数:接受由函数stream_ socket_server()创建的Socket连接
  • stream_socket_client函数:打开网络或者UNIX主机的Socket连接
  • stream_socket_enable_crypto函数:为一个已经连接的Socket打开或者关闭数据加密
  • stream_socket_get_name函数:获取本地或者网络Socket的名称
  • stream_socket_pair函数:创建两个无区别的Socket数据流连接
  • stream_socket_recvfrom函数:从Socket获取数据,不管其连接与否
  • stream_socket_sendto函数:向Socket发送数据,不管其连接与否
  • stream_socket_server函数:创建一个网络或者UNIX Socket服务端
  • stream_wrapper_restore函数:恢复一个事先注销的数据包
  • stream_wrapper_unregister函数:注销一个URL地址包

天然的memcache —— /dev/shm

首先让我们认识一下,什么是tmpfs和/dev/shm/?

tmpfs是Linux/Unix系统上的一种基于内存的文件系统。tmpfs可以使用您的内存或swap分区来存储文件。由此可见,tmpfs主要存储暂存的文件。
它有如下2个优势 :
1. 动态文件系统的大小。
2. tmpfs 的另一个主要的好处是它闪电般的速度。
因为典型的 tmpfs 文件系统会完全驻留在内存 RAM 中,读写几乎可以是瞬间的。同时它也有一个缺点 tmpfs 数据在重新启动之后不会保留,因为虚拟内存本质上就是易失的。所以有必要做一些脚本做诸如加载,绑定的操作。

tmpfs不具备持久性,重启后数据不保留,请务必注意!!!

性能测试

分别对磁盘、/dev/shm、memcache进行10W次读、写、读写操作。

inotify

一、简介

inotify是系统体层提供的机制,在版本号大于2.6.13的内核中才有提供(之前kernel版本,有dnotify)。PHP官方扩展库pecl提供了该扩展包。关于inotify的基本原理和用法介绍,可以看IBM的文档

inotify的API接口非常少,只有5个函数,inotify_init,inotifiy_read,inotify_add_watch,inotify_rm_watch,inotify_qeueue_len, 这几个函数的含义还是相当直接的,估计比较难理解的,就只有一个init和read函数了。这里简单解释一下,inotify是一个类似队列一样的东西, 把需要监控的一批文件和目录,加入到同一个inotify队列中,所以首先要先init一个空队列出来,然后用add_watch函数来添加监控对象。然 后,read函数就能大显身手了,read函数可以产生一个(默认)阻塞的操作,查询监控的对象中是否有事件发生,如果有,就会返回数据,否则就一直阻 塞。当然,也可以设置成非阻塞的,可以看相关代码范例。

inotify能够监控的文件系统事件罗列如下,基本上涵盖了linux server上的所有的文件事件。根据PHP官方文档和我实际测试,inotify不支持目录递归遍历,所以,如果要监控目录的变化,需要把每一个子目录 都加入到watch的列表中去。除此之外,因为我在虚拟机上测试,还发现了一点,就是宿主机编辑共享文件,guest系统中的inotify无法监控到文 件的变化。

二、安装

1、 使用pecl安装

2、 编译安装

安装完成后,在php.ini后面添加扩展

三、监控文件

使用ETag和LastModified做缓存

 

 

php SPL库 —— 简介

1、什么是SPL?

SPL是Standard PHP Library(PHP标准库)的缩写。

根据官方定义,它是”a collection of interfaces and classes that are meant to solve standard problems”。但是,目前在使用中,SPL更多地被看作是一种使object(物体)模仿array(数组)行为的interfaces和classes。

2、SPL包括什么?

  • 数据结构
  • 迭代器
  • 接口
  • 异常
  • SPL 函数
  • 文件处理
  • 其他

 

参考链接:

http://cn2.php.net/manual/zh/book.spl.php

http://www.ruanyifeng.com/blog/2008/07/php_spl_notes.html

php实现收到请求后,断开连接继续处理

有时候我发送一个请求后,不需要得到服务器的运行结果,只想快速结束本次访问连接,但是又希望服务器可以处理我们提交的数据。

那么下面方法就可以解决这个问题。