workerman学习笔记

序言

Workerman, 高性能socket框架.

Workerman是一款纯PHP开发的开源高性能的PHP socket 服务框架。

Workerman每个进程能维持上万并发连接。

同时支持TCP、UDP、UNIXSOCKET,支持长连接,支持Websocket、HTTP、WSS、HTTPS等通讯协议以及各种自定义协议。拥有定时器、异步socket客户端、异步Mysql、异步Redis、异步Http、异步消息队列等众多高性能组件。

应用方向

1、即时通讯类

2、物联网类

3、游戏服务器类

4、HTTP服务

5、SOA服务化

6、其它服务器软件

7、中间件

原理

Worker是WorkerMan中最基本容器, 采用Epoll(需要装event扩展)+非阻塞IO,每个Worker进程都能上万的客户端连接,并处理这些连接上发来的数据。

主进程与worker子进程关系[https://wenda.workerman.net/uploads/answer/20140815/5670ea17653a1a6e6811ed5148f77c96.png]

开发必读

1、windows环境限制

2、workerman不依赖apache或者nginx

3、workerman是命令行启动的

4、长连接必须加心跳

5、客户端和服务端协议一定要对应才能通讯

6、连接失败可能的原因

7、不要使用exit die sleep语句

8、不要使用pcntl_fork函数

9、业务代码里不要有死循环

10、改代码要重启

11、长连接应用建议用GatewayWorker框架

12、支持更高并发

入门指引

特性

1、纯PHP开发

2、支持PHP多进程

3、支持TCP、UDP

4、支持长连接

5、支持各种应用层协议

6、支持高并发

7、支持服务平滑重启

8、支持文件更新检测及自动加载

9、支持以指定用户运行子进程

10、支持对象或者资源永久保持

11、高性能

12、支持HHVM

13、支持分布式部署

14、支持守护进程化

15、支持多端口监听

16、支持标准输入输出重定向

简单的开发示例

创建http_test.php文件

<?php
use Workerman\Worker;
require_once __DIR__ . '/Workerman/Autoloader.php';

// 创建一个Worker监听2345端口,使用http协议通讯
$http_worker = new Worker("http://0.0.0.0:2345");

// 启动4个进程对外提供服务
$http_worker->count = 4;

// 接收到浏览器发送的数据时回复hello world给浏览器
$http_worker->onMessage = function($connection, $data)
{
    // 向浏览器发送hello world
    $connection->send('hello world');
};

// 运行worker
Worker::runAll();
php http_test.php start

安装

Composer安装:

composer require workerman/workerman

Git安装:

git clone https://github.com/walkor/Workerman

Linux系统环境检测

curl -Ss http://www.workerman.net/check.php | php

安装pcntl和posix扩展:

yum install php-process

安装event扩展

参考: https://amon.org/php-sysvsem

启动和停止

启动

以debug(调试)方式启动

php start.php start

以daemon(守护进程)方式启动

php start.php start -d

停止

php start.php stop

重启

php start.php restart

平滑重启

php start.php reload

查看状态

php start.php status

查看连接状态(需要Workerman版本>=3.5.0)

php start.php connections

Worker类

WorkerMan中有两个重要的类Worker与Connection。

构造函数

Worker::__construct([string $listen , array $context])
$listen (可选参数,不填写表示不监听任何端口)
    <协议> 可以为以下格式:
    tcp: 例如 tcp://0.0.0.0:8686
	udp: 例如 udp://0.0.0.0:8686
	unix: 例如 unix:///tmp/my_file(需要Workerman>=3.2.7)
	http: 例如 http://0.0.0.0:80
	websocket: 例如 websocket://0.0.0.0:8686
	text: 例如 text://0.0.0.0:8686(text是Workerman内置的文本协议,兼容telnet,详情参见附录Text协议部分)
	以及其他自定义协议,参见本手册定制通讯协议部分
    <监听地址> 可以为以下格式:
	如果是unix套接字,地址为本地一个磁盘路径
	非unix套接字,地址格式为 <本机ip>:<端口号>
$context 一个数组。用于传递socket的上下文选项,参见套接字上下文选项
    http://php.net/manual/zh/context.socket.php

属性

id

当前worker进程的id编号,范围为0$worker->count-1

进程重启后id编号值是不变的。

count

设置当前Worker实例启动多少个进程,不设置时默认为1。

name

设置当前Worker实例的名称,方便运行status命令时识别进程。不设置时默认为none。

protocol

设置当前Worker实例的协议类。

transport

设置当前Worker实例所使用的传输层协议,目前只支持3种(tcp、udp、ssl)。不设置默认为tcp。

reusePort

设置当前worker是否开启监听端口复用(socket的SO_REUSEPORT选项)。

开启监听端口复用后允许多个无亲缘关系的进程监听相同的端口,并且由系统内核做负载均衡,决定将socket连接交给哪个进程处理,避免了惊群效应,可以提升多进程短连接应用的性能。

从workerman 3.5.21版本开始默认开启此选项。之前版本需要手动设置Worker::$reusePort=true;开启。

connections

此属性中存储了当前进程的所有的客户端连接对象,其中id为connection的id编号,详情见手册TcpConnection的id属性

stdoutFile

此属性为全局静态属性,如果以守护进程方式(-d启动)运行,则所有向终端的输出(echo var_dump等)都会被重定向到stdoutFile指定的文件中。

user

设置当前Worker实例以哪个用户运行。此属性只有当前用户为root时才能生效。不设置时默认以当前用户运行。

建议$user设置权限较低的用户,例如www-data、apache、nobody等。

reloadable

设置当前Worker实例是否可以reload,即收到reload信号后是否退出重启。不设置默认为true,收到reload信号后自动重启进程。

有些进程维持着客户端连接,例如Gateway/Worker模型中的gateway进程,当运行reload重新载入业务代码时,却又不想客户端连接断开,则设置gateway进程的reloadable属性为false

daemonize

此属性为全局静态属性,表示是否以daemon(守护进程)方式运行。如果启动命令使用了 -d参数,则该属性会自动设置为true。也可以代码中手动设置。

回调属性

onWorkerStart

设置Worker子进程启动时的回调函数,每个子进程启动时都会执行。

回调函数的参数

$worker

onWorkerReload

此特性不常用到。

设置Worker收到reload信号后执行的回调。

可以利用onWorkerReload回调做很多事情,例如在不需要重启进程的情况下重新加载业务配置文件。

回调函数的参数

$worker

onConnect

当客户端与Workerman建立连接时(TCP三次握手完成后)触发的回调函数。每个连接只会触发一次onConnect回调。

回调函数的参数

$connection

连接对象,即TcpConnection实例,用于操作客户端连接,如发送数据关闭连接

onMessage

当客户端通过连接发来数据时(Workerman收到数据时)触发的回调函数

回调函数的参数

$connection

连接对象,即TcpConnection实例,用于操作客户端连接,如发送数据关闭连接

$data

客户端连接上发来的数据,如果Worker指定了协议,则$data是对应协议decode(解码)了的数据

onClose

当客户端连接与Workerman断开时触发的回调函数。不管连接是如何断开的,只要断开就会触发onClose。每个连接只会触发一次onClose

回调函数的参数

$connection

连接对象,即TcpConnection实例,用于操作客户端连接,如发送数据关闭连接

onError

当客户端的连接上发生错误时触发。

回调函数的参数

$connection

连接对象,即TcpConnection实例,用于操作客户端连接,如发送数据关闭连接

$code

错误码

$msg

错误消息

接口

runAll

运行所有Worker实例。

stopAll

停止当前进程(子进程)的所有Worker实例并退出。

此方法用于安全退出当前子进程,作用相当于调用exit/die退出当前子进程。

与直接调用exit/die区别是,直接调用exit或者die无法触发onWorkerStop回调,并且会导致一条WORKER EXIT UNEXPECTED错误日志。

listen

用于实例化Worker后执行监听。

此方法主要用于在Worker进程启动后动态创建新的Worker实例,能够实现同一个进程监听多个端口,支持多种协议。需要注意的是用这种方法只是在当前进程增加监听,并不会动态创建新的进程,也不会触发onWorkerStart方法。

TcpConnection类

属性

id

连接的id。这是一个自增的整数。

protocol

设置当前连接的协议类

worker

此属性为只读属性,即当前connection对象所属的worker实例

maxSendBufferSize

defaultMaxSendBufferSize

maxPackageSize

回调属性

onMessage

作用与Worker::$onMessage回调相同,区别是只针对当前连接有效,也就是可以针对某个连接的设置onMessage回调。

onClose

此回调与Worker::$onClose回调作用相同,区别是只针对当前连接有效,也就是可以针对某个连接的设置onClose回调。

onBufferFull

作用与Worker::$onBufferFull回调相同,区别是只针对当前连接起作用,即可以单独设置某个连接的onBufferFull回调

onBufferDrain

作用与Worker::$onBufferDrain回调相同,区别是只针对当前连接起作用,即可以单独设置某个连接的onBufferDrain回调

onError

作用与Worker::$onError回调相同,区别是只针对当前连接起作用,即可以单独设置某个连接的onError回调

接口

send

向客户端发送数据

参数

$data

要发送的数据,如果在初始化Worker类时指定了协议,则会自动调用协议的encode方法,完成协议打包工作后发送给客户端

$raw

是否发送原始数据,即不调用协议的encode方法,默认是false,即自动调用协议的encode方法

getRemoteIp

获得该连接的客户端ip

getRemotePort

获得该连接的客户端端口

close

安全的关闭连接.

调用close会等待发送缓冲区的数据发送完毕后才关闭连接,并触发连接的onClose回调

参数

$data

可选参数,要发送的数据(如果有指定协议,则会自动调用协议的encode方法打包$data数据),当数据发送完毕后关闭连接,随后会触发onClose回调

destroy

立刻关闭连接。

与close不同之处是,调用destroy后即使该连接的发送缓冲区还有数据未发送到对端,连接也会立刻被关闭,并立刻触发该连接的onClose回调。

pauseRecv

使当前连接停止接收数据。该连接的onMessage回调将不会被触发。此方法对于上传流量控制非常有用

resumeRecv

使当前连接继续接收数据。此方法与Connection::pauseRecv配合使用,对于上传流量控制非常有用

pipe

将当前连接的数据流导入到目标连接。内置了流量控制。此方法做TCP代理非常有用

AsyncTcpConnection类

异步

__construct 方法

创建一个异步连接对象。

目前AsyncTcpConnection支持的协议有tcpsslwsframetext

connect 方法

执行异步连接操作。此方法会立刻返回。

reConnect 方法

重连。一般在onClose回调中调用,实现断线重连。

由于网络问题或者对方服务重启等原因导致连接断开,则可以通过调用此方法实现重连。

参数

$delay

延迟多久后执行重连。单位为秒,支持小数,可精确到毫秒。

如果不传或者值为0则代表立即重连。

最好传递参数让重连延迟执行,避免因为对端服务问题一直不可连导致本机cpu消耗过高。

transport属性

设置传输属性,可选值为 tcpssl,默认是tcp。

transport为 ssl 时,要求PHP必须安装openssl扩展

当把Workerman作为客户端向服务端发起ssl加密连接(https连接、wss连接等)时请设置此选项为ssl

AsyncUdpConnection类

AsyncUdpConnection可以作为udp客户端与远程udp服务端进行通讯。

其实udp是无连接的,但是为了易用性,这里与AsyncTcpConnection命名规则和接口保持基本一致。

注意:与AsyncTcpConnection不同,AsyncUdpConnection不支持以下属性或者方法。

  1. 没有connection->id属性
  2. 没有connection->worker属性
  3. 没有connection->transport属性
  4. 没有connection->maxSendBufferSize属性
  5. 没有connection->defaultMaxSendBufferSize属性
  6. 没有connection->maxPackageSize属性
  7. 没有connection->onBufferFull回调
  8. 没有connection->onBufferDrain回调
  9. 没有connection->onError回调
  10. 没有connection->destroy()接口
  11. 没有connection->pauseRecv()接口
  12. 没有connection->resumeRecv()接口
  13. 没有connection->pipe()接口
  14. 没有connection->reconnect()接口

AsyncUdpConnection支持的属性或者方法 1.支持connection->protocol属性 2.支持connection->onMessage回调 3.支持connection->connect()方法 4.支持connection->send()方法 5.支持connection->getRemoteIp()方法 6.支持connection->getRemotePort()方法 7.支持connection->onClose回调。 注意:因为tcp是基于连接的,一般情况下,当任何一方调用close断开连接时双方都能触发onClose。但是udp是无连接的,调用connection->close()方法只能触发本地的onClose回调,无法触发对端的onClose回调。

__construct 方法

创建一个udp连接对象。

AsyncUdpConnection可以让Workerman作为客户端与远程服务端传输udp数据。

connect 方法

执行异步连接操作。此方法会立刻返回。

send 方法

执行异步连接操作。此方法会立刻返回。

参数

$data 向服务端发送的数据,数据大小不能超过65507字节(udp单个数据包最大传输大小为65507字节),否则会发送失败。

close

安全的关闭连接,并触发连接的onClose回调。

虽然udp是无连接的,但是对应的AsyncUdpConnection对象却是一直保留在内存中,必须调用close方法才可以释放掉对应的udp连接对象,否则这个udp连接对象会一直存在于内存中,造成内存泄漏。

参数

$data

可选参数,要发送的数据(如果有指定协议,则会自动调用协议的encode方法打包$data数据),当数据发送完毕后关闭连接,随后会触发onClose回调。

数据大小不能超过65507字节,否则会发送失败。

Timer定时器类

add

定时执行某个函数或者类方法。

注意:定时器是在当前进程中运行的,workerman中不会创建新的进程或者线程去运行定时器。

参数

time_interval

多长时间执行一次,单位秒,支持小数,可以精确到0.001,即精确到毫秒级别。

callback

回调函数注意:如果回调函数是类的方法,则方法必须是public属性

args

回调函数的参数,必须为数组,数组元素为参数值

persistent

是否是持久的,如果只想定时执行一次,则传递false(只执行一次的任务在执行完毕后会自动销毁,不必调用Timer::del())。默认是true,即一直定时执行。

返回值

返回一个整数,代表计时器的timerid,可以通过调用Timer::del($timerid)销毁这个计时器。

del

删除某个定时器

参数

timer_id

定时器的id,即add接口返回的整型

返回值

boolean

定时器注意事项

1、只能在onXXXX回调中添加定时器。全局的定时器推荐在onWorkerStart回调中设置,针对某个连接的定时器推荐在onConnect中设置。

2、添加的定时任务在当前进程执行(不会启动新的进程或者线程),如果任务很重(特别是涉及到网络IO的任务),可能会导致该进程阻塞,暂时无法处理其它业务。所以最好将耗时的任务放到单独的进程运行,例如建立一个/多个Worker进程运行

3、当前进程忙于其它业务时或者当一个任务没有在预期的时间运行完,这时又到了下一个运行周期,则会等待当前任务完成才会运行,这会导致定时器没有按照预期时间间隔运行。也就是说当前进程的业务都是串行执行的,如果是多进程则进程间的任务运行是并行的。

4、需要注意多进程设置了定时任务造可能会造成并发问题,

5、可能会有1毫秒左右的误差。

6、定时器不能跨进程删除,例如a进程设置的定时器无法在b进程直接调用Timer::del接口删除

7、不同进程间的定时器id可能会重复,但是同一个进程内产生的定时器id不会重复

crontab

除了定时器Timer,workerman提供了crontab组件,使用规则类似linux的crontab,支持秒级别定时。

参见:Crontab定时任务组件

Http服务

workerman从4.x版本开始加强了HTTP服务的支持。引入了请求类、响应类、session类以及SSE。如果你想使用workerman的HTTP服务,强烈推荐使用workerman4.x或者以后的更高版本。

请求

请求对象一律在onMessage回调函数中获取,框架会自动将Request对象通过回调函数第二个参数传递进来。

$worker->onMessage = function($connection, $request){}

点击查看

响应

session会话

session管理

SSE