workerman源码分析_简略篇

2021/07/05 posted in  服务端
Tags:  #php #网络

使用workerman在写一个cronjob项目,出现问题引发的wm分析。


配置个web的http worker

在代码里再请求curl 8089端口,即http://127.0.0.1:8089
为了调试,我们开启curl的debug功能

curl_setopt($ch, CURLOPT_VERBOSE, true); // curl debug
curl_setopt($ch, CURLOPT_STDERR, fopen(CRONPATH.'/Application/Log/curl.log', 'w+'));
$response = curl_getinfo( $ch );

测试正常连接百度是正常的,但连接127.0.0.1:8089一直在等待中
Warning: file_get_contents(http://127.0.0.1:8089/?op=cl_list&secret=b61e757422497ccaef7dab5d59b180dd): Failed to open stream: HTTP request failed! in /webwww/y/cronshow/Application/Library/Web.php on line 174

curl_start
exec
* Expire in 0 ms for 6 (transfer 0x558eac35eb10)
* Expire in 2000 ms for 8 (transfer 0x558eac35eb10)
* Expire in 2000 ms for 2 (transfer 0x558eac35eb10)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x558eac35eb10)
* Connected to 127.0.0.1 (127.0.0.1) port 8089 (#0)
> GET /?op=cl_list&secret=b61e757422497ccaef7dab5d59b180dd HTTP/1.1
Host: 127.0.0.1:8089
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded; charset=utf-8
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0
X-MicrosoftAjax: Delta=true

* Operation timed out after 2000 milliseconds with 0 bytes received
* Closing connection 0

显示2秒之后断开

docker里开启strace调试

docker run --cap-add=SYS_PTRACE -it -v ~/code:/webwww --name php-fpm-8033 show/php-fpm:8.0.3

进入docker里查看

strace -tt -p 对应进程端口
可使用php job.php status查看workerman的worker端口

可以监测到连接8089的时候是-1

socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 8

connect(8, {sa_family=AF_INET, sin_port=htons(8089), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)

getpeername(8, {sa_family=AF_INET, sin_port=htons(8089), sin_addr=inet_addr("127.0.0.1")}, [128->16]) = 0
getsockname(8, {sa_family=AF_INET, sin_port=htons(53752), sin_addr=inet_addr("127.0.0.1")}, [128->16]) = 0

正常的情况下是0

connect(9, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.65.1")}, 16) = 0

这时候思考到workerman的count参数只开启了一个阻塞进程,故而改成3个进程即可。

通过这个事故,下定决定分析workerman源码

分析

我们先查看一下workerman的目录

.
├── _webwww_y_cronshow_job.php.pid
├── workerman
│   ├── Autoloader.php
│   ├── Connection
│   │   ├── AsyncTcpConnection.php
│   │   ├── AsyncUdpConnection.php
│   │   ├── ConnectionInterface.php
│   │   ├── TcpConnection.php
│   │   └── UdpConnection.php
│   ├── Events
│   │   ├── Ev.php
│   │   ├── Event.php
│   │   ├── EventInterface.php
│   │   ├── Libevent.php
│   │   ├── React
│   │   │   ├── Base.php
│   │   │   ├── ExtEventLoop.php
│   │   │   ├── ExtLibEventLoop.php
│   │   │   └── StreamSelectLoop.php
│   │   ├── Select.php
│   │   └── Swoole.php
│   ├── Lib
│   │   ├── Constants.php
│   │   └── Timer.php
│   ├── MIT-LICENSE.txt
│   ├── Protocols
│   │   ├── Frame.php
│   │   ├── Http
│   │   │   ├── Chunk.php
│   │   │   ├── Request.php
│   │   │   ├── Response.php
│   │   │   ├── ServerSentEvents.php
│   │   │   ├── Session
│   │   │   │   ├── FileSessionHandler.php
│   │   │   │   └── RedisSessionHandler.php
│   │   │   ├── Session.php
│   │   │   └── mime.types
│   │   ├── Http.php
│   │   ├── ProtocolInterface.php
│   │   ├── Text.php
│   │   ├── Websocket.php
│   │   └── Ws.php
│   ├── README.md
│   ├── Timer.php
│   ├── Worker.php
│   └── composer.json
└── workerman.log

上来来说
Connection 连接类
Events 网络事件类
Protocols 请求协议类
主要这三部分组成
核心文件主要是Worker.php,不得不佩服workerman作者,简单即精华。
所以我们从Worker.php文件开始说起


其实我们所有服务都是从runAll开始

一层层来分析一下runAll所做的事

checkSapiEnv

主要是检查环境符不符合要求

init

初始化主要生成master的pid文件和对应的log文件
Timer::init(); 初始化事件

注册并signal事件
这里要了解一下php的pcntl库

parseCommand

主要解析命令行的输入
登录php job.php start
即对'start','stop','restart','reload','status','connections'等做接收处理

daemonize

判断是否需要守护进程,如约需要pcntl_fork当前一下

initWorkers

初始化worker
如果你定义了三组worker就三次
例如

$jobworker = new JobWorker();
$cronworker = new CronWorker();
$webworker = new WebWorker();

最近监听各worker的端口

设置监听端口并设置接收监听resumeAccept

这里主要使用到了php的stream和socket库


resumeAccept会增加Event事件,监听每个用户请求的connection

installSignal

注册Signal库,即触发各种网络进程事件
例如ctrl-c关闭进程,kill -9关闭进程等
这里要了解每个sig的含义
SIGINT,SIGTERM,SIGHUP,SIGUSR1,SIGQUIT,SIGUSR2,SIGIO,SIGPIPE

saveMasterPid

保存Master进程的pid

displayUI

设置要显示的UI
运行成功之后会有个界面

forkWorkers

这里主要是查看定义worker里的个数,例如我们的webworker定义count=3,会生成webworker相关的3个子进程。

resetStd

设置定义的输入输出

monitorWorkers

使用pcntl_signal_dispatch事件,监控每个子进程的情况。
Worker类就大概这样的情况了

网络相关

因为在stream和socket里,并没有协议做解释,所以协议的处理我们会在Protocols文件夹里做相对就的处理

例如我们定义的是text://127.0.0.1:123, 就会找到Text做弄对应的处理
http://0.0.0.0:456,即指定Http.php做相关处理
协议的定义在workerman手册能找到

结尾

workerman为我们带来了异步,提升并发,这是比php-fpm带来更多的好处。但我们也要了解一下进程内是怎么一回事,因为mysql长链接断开等问题。
workerman主要的难度是多进程和网络事件,这部分需要我们学习select、poll、epoll等。了解网络解释的一些过程,此篇比较精简说明一下workerman,后续更新每个模块的详解。