博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
wokerman启动分析
阅读量:7098 次
发布时间:2019-06-28

本文共 5723 字,大约阅读时间需要 19 分钟。

workerman官方描述

Workerman是一款纯PHP开发的开源高性能异步PHP socket框架。支持高并发,超高稳定性,被广泛的用于手机app、移动通讯,微信小程序,手游服务端、网络游戏、PHP聊天室、硬件通讯、智能家居、车联网、物联网等领域的开发。 支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。拥有异步Mysql、异步Redis、异步Http、MQTT物联网客户端、异步消息队列等众多高性能组件。

正如标题,我们把范围缩小.来看下启动wokerman时候源码涉及到的知识点:

如何启动一个服务

require_once "Autoloader.php";$http_worker = new \Workerman\Worker("http://0.0.0.0:2347");$http_worker->count = 2;$http_worker->onMessage = function ($connection, $data) {    $connection->send('hello baby');};$http_worker->runAll();复制代码

上面是一个最简单的一个例子,Wokerman类初始化时候传递了协议类型和服务地址【http类型】, 然后设置了进程数量为2,绑定了事件回调处理【onMessage】,最后核心的一步是启动这个服务

下面一步一步看下内部的实现:

调用:

$http_worker = new \Workerman\Worker("http://0.0.0.0:2347");复制代码
  1. 初始化
// Save all worker instances.$this->workerId                    = spl_object_hash($this);static::$_workers[$this->workerId] = $this;static::$_pidMap[$this->workerId]  = array();复制代码

spl_object_hash 将对象生成一个hash值,初始化$_workers和$_pidMap数组

  1. 设置自动加载目录
// Get autoload root path.  $backtrace                = debug_backtrace();  $this->_autoloadRootPath = dirname($backtrace[0]['file']);复制代码

注意debug_backtrace 产生一条 PHP 的回溯跟踪,此处只是获取执行脚本的目录,如果兼容5.3.6以下版本,建议debug_backtrace(false)或更高版本使用debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),忽略 "args" 的索引,能够节省内存占用

  1. 创建资源流上下文 stream_context_create 这个是php流操作里面的一个函数,具体作用如下
$opts = array(    'http' => array(        'method'  => "GET",        'header'  => "Accept-language: en\r\n" .            "Cookie: foo=bar\r\n",        'timeout' => 1,    ));$context = stream_context_create($opts);$text = file_get_contents('https://facebook.com',false,$context);//同样适用fopenvar_dump($text); //false复制代码

上面的是个简单的stream_context_create的示例, http协议设置了请求方法 、header头、超时时间,file_get_contents请求的是一个墙外的地址,第三个参数是需要一个资源对象,这里过1s钟后如果请求不到将会返回false.

流是一个很大的话题,可以做很多有意思的事情,这里不再展开,对此感兴趣可以参考http://www.php.net/manual/zh/ref.stream.php

下面继续回到程序主逻辑初始化后:

1.设置进程数[准确的讲应该成为worker进程数]

$http_worker->count = 2; //此处后期再讲复制代码

2.绑定事件

$http_worker->onMessage = function ($connection, $data) {    $connection->send('hello baby');};复制代码

上述表示当接收到一个消息的时候,触发onMessage绑定的function函数。 $connection表示为连接对象.用于操作客户端连接,发送数据 关闭连接等。 $connection->send() 发送数据给客户端。 $data 表示接收的数据。

3.启动服务

$http_worker->runAll();复制代码

此处为该启动流程分析的核心,下面我们一起看下这里面执行了哪些操作.

protected static function checkSapiEnv(){    // Only for cli.    if (php_sapi_name() != "cli") {        exit("only run in command line mode \n");    }    if (DIRECTORY_SEPARATOR === '\\') {        self::$_OS = OS_TYPE_WINDOWS;    }}复制代码

php_sapi_name函数获取运行模式。 DIRECTORY_SEPARATOR 根据系统分隔符判断是否是windows操作系统

foreach (static::$_workers as $worker_id => $worker) {    $new_id_map = array();    $worker->count = $worker->count <= 0 ? 1 : $worker->count;    for($key = 0; $key < $worker->count; $key++) {        $new_id_map[$key] = isset(static::$_idMap[$worker_id][$key]) ? static::$_idMap[$worker_id][$key] : 0;    }    static::$_idMap[$worker_id] = $new_id_map;}复制代码

注意我们从开始设置的$worker->count参数,此处绑定woker_id => 一组count数量的进程,此处我们只有一个。

我们再来看看如何注册信号量的

protected static function installSignal(){    if (static::$_OS !== OS_TYPE_LINUX) {        return;    }    // stop    pcntl_signal(SIGINT, array('\Workerman\Worker', 'signalHandler'), false);    // graceful stop    pcntl_signal(SIGTERM, array('\Workerman\Worker', 'signalHandler'), false);    // reload    pcntl_signal(SIGUSR1, array('\Workerman\Worker', 'signalHandler'), false);    // graceful reload    pcntl_signal(SIGQUIT, array('\Workerman\Worker', 'signalHandler'), false);    // status    pcntl_signal(SIGUSR2, array('\Workerman\Worker', 'signalHandler'), false);    // connection status    pcntl_signal(SIGIO, array('\Workerman\Worker', 'signalHandler'), false);    // ignore    pcntl_signal(SIGPIPE, SIG_IGN, false);}复制代码

首先信号量只能运行在linux环境下。 核心函数 pcntl_signal,安装一个信号处理器

bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )复制代码

保存pid

static::saveMasterPid();复制代码

绘制命令端界面

tatic::displayUI();复制代码

根据上面存储的static::$_workers和static::$_pidMap 循环fork进程

while (count(static::$_pidMap[$worker->workerId]) < $worker->count) {    static::forkOneWorkerForLinux($worker);}复制代码

核心函数 pcntl_fork()

在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程号,而子进程得到的是0.

调用listen 根据调用选择引入Protocols下协议文件

-------写的我好想去死,我先休息下再继续写---------

2018-09-16 天气晴

$pid = pcntl_fork();//父进程和子进程都会执行下面代码if ($pid == -1) {    //错误处理:创建子进程失败时返回-1.     die('could not fork');} else if ($pid) {     //父进程会得到子进程号,所以这里是父进程执行的逻辑     pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。} else {     //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。}复制代码

一部分信号表:

SIGHUP 1 A 终端挂起或者控制进程终止 SIGINT 2 A 键盘中断(如break键被按下) SIGQUIT 3 C 键盘的退出键被按下 SIGILL 4 C 非法指令 SIGABRT 6 C 由abort(3)发出的退出指令 SIGFPE 8 C 浮点异常 SIGKILL 9 AEF Kill信号 SIGSEGV 11 C 无效的内存引用 SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道 SIGALRM 14 A 由alarm(2)发出的信号 SIGTERM 15 A 终止信号 SIGUSR1 30,10,16 A 用户自定义信号1 SIGUSR2 31,12,17 A 用户自定义信号2 SIGCHLD 20,17,18 B 子进程结束信号 SIGCONT 19,18,25 进程继续(曾被停止的进程) SIGSTOP 17,19,23 DEF 终止进程 SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键 SIGTTIN 21,21,26 D 后台进程企图从控制终端读 SIGTTOU 22,22,27 D 后台进程企图从控制终端写 复制代码

捕获信号小例子

output:

^Cpcntl_wait return2退出了2退出了复制代码

declare(ticks = 1)效率低下,每一行都检查信号发生,建议使用pcntl_signal_dispatch 捕获

多进程执行

// 3个子进程处理任务for ($i = 0; $i < 3; $i++){    $pid = pcntl_fork();    if ($pid == -1) {        die("could not fork");    } elseif ($pid) {        echo "I'm the Parent $i\n";    } else {// 子进程处理        sleep(5);        echo "success\n";        exit($i);// 一定要注意退出子进程,否则pcntl_fork() 会被子进程再fork,带来处理上的影响。    }}// 等待子进程执行结束while (pcntl_waitpid(0, $status) != -1) {    $status = pcntl_wexitstatus($status);    echo "Child $status completed\n";}复制代码

output:

I'm the Parent 0I'm the Parent 1I'm the Parent 2successsuccesssuccessChild 0 completedChild 1 completedChild 2 completed复制代码

上面的代码实现了并行的运行。

转载地址:http://vueql.baihongyu.com/

你可能感兴趣的文章
博客NO.1争夺赛奖品汇总
查看>>
多动脑可以提高记忆力和智力
查看>>
2016联想校招软件开发类在线笔试真题
查看>>
在Azure Cloud Service中部署Java Web App(2)
查看>>
nodejs递归创建多层目录
查看>>
SCCM2012操作系统部署OSD补充
查看>>
rz sz 安装使用
查看>>
MySQL my.cnf参数配置优化详解
查看>>
php读取ACCESS数据库时,内容被截断的原因和解决
查看>>
#pragma指令
查看>>
FreeBSD使用
查看>>
电子书平台不应有裁判权
查看>>
XQuery:连通SQL与NoSQL的良好桥梁
查看>>
Trie树
查看>>
UVA 11389——The Bus Driver Problem
查看>>
黑白图像(DFS)
查看>>
RFID与智能卡
查看>>
Ubuntu16.04架设最新版本SSH2开发框架(maven构建)
查看>>
CentOS 7 的初始化
查看>>
Hibernate 一对一双向关联 Annotation
查看>>