workerman业务子进程之间可以进程间共享内存存吗

workerman 能在thinkphp的框架上使用吗_百度知道
workerman 能在thinkphp的框架上使用吗
我有更好的答案
workerman 能在thinkphp的框架上使用$Gateway = &new \Org\Util\Gateway('127.0.0.1:5136');$Gateway-&sendToUid(1021,json_encode(array()));下载workerman, 放入thinkphp,注意与Home平级,就把workerman当做是一个模块。进入Home/Controller目录,新建WorkermanController.class.phpworkerman和thinkphp完美结合使用源码直接上源码,这里我是以守护进程方式运行的,调试的话,可以去掉daemonize = true 这行.&?phpnamespace Admin\Cuse Workerman\W/*** 用户信息查询*/class WorkermanController{/*** 用户信息查询*/public function index(){if(!IS_CLI){die(&access illegal&);}require_once APP_PATH.'Workerman/Autoloader.php';// 每个进程最多执行1000个请求define('MAX_REQUEST', 1000);Worker::$daemonize =//以守护进程运行Worker::$pidFile = '/data/wwwlogs/CMSWorker/workerman.pid';//方便监控WorkerMan进程状态Worker::$stdoutFile = '/data/wwwlogs/CMSWorker/stdout.log';//输出日志, 如echo,var_dump等Worker::$logFile = '/data/wwwlogs/CMSWorker/workerman.log';//workerman自身相关的日志,包括启动、停止等,不包含任何业务日志$worker = new Worker('text://172.16.0.10:10024');$worker-&name = 'CMSWorker';$worker-&count = 2;//$worker-&transport = 'udp';// 使用udp协议,默认TCP$worker-&onWorkerStart = function($worker){echo &Worker starting...\n&;};$worker-&onMessage = function($connection, $data){static $request_count = 0;// 已经处理请求数var_dump($data);$connection-&send(&hello&);/** 退出当前进程,主进程会立刻重新启动一个全新进程补充上来,从而完成进程重启*/if(++$request_count &= MAX_REQUEST){// 如果请求数达到1000Worker::stopAll();}};$worker-&onBufferFull = function($connection){echo &bufferFull and do not send again\n&;};$worker-&onBufferDrain = function($connection){echo &buffer drain and continue send\n&;};$worker-&onWorkerStop = function($worker){echo &Worker stopping...\n&;};$worker-&onError = function($connection, $code, $msg){echo &error $code $msg\n&;};// 运行workerWorker::runAll();}}修改Workerman/Worker.php源码,找到parseCommand()方法,workerman版本3.3.2的话,在586行,修改命令行检测语法:protected static function parseCommand(){global $// C$start_file = $argv[0];if (!isset($argv[2])) {exit(&Usage: php yourfile.php Controller/Action {start|stop|restart|reload|status|kill}\n&);}// Get command.$command = trim($argv[2]);$command2 = isset($argv[3]) ? $argv[3] : '';.....}OK,此时大功告成。Linux命令行下运行,注意,此处要切换到thinkphp根目录下面/usr/local/php/bin/php index.php Workerman/index start查看运行状态:/usr/local/php/bin/php index.php Workerman/index status此处命令行可以放入全局变量中,直接以php运行
可以使用de
为您推荐:
其他类似问题
您可能关注的内容
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
标签:至少1个,最多5个
wokerman 启动分析
@(学习)[workerman, php]
前期想说的
也是最近才看的代码,遇到不懂得地方就去google,所以这篇文章里面穿插了很多参考资料,可以直接点击阅览。
需要了解一些知识pcntl、posix、libevent,然后我们从服务的启动开始来看。
runAll顾名思义,运行所有的,注释中也写了,Run all worker instances,运行所有的实例,也就是说脚本中可以同时new多个worker服务,这也是后面一个重要的$workers包含了每一个$worker都是一个服务实例。然后会根据每一个实例初始化count个子进程。
* Run all worker instances.
* @return void
public static function runAll()
// 判断是否命令行模式
self::checkSapiEnv();
// 创建目录、设置权限、绑定时钟信号脚本
self::init();
// 解析cli的命令,完成start、reload、restart、kill、stop等
self::parseCommand();
// 是否开启守护进程
self::daemonize();
// 对socket进行了一系列的配置
self::initWorkers();
// 注册信号处理器
self::installSignal();
// 保存主进程的pid
self::saveMasterPid();
// 每个$worker服务fork出count个子进程,然后给每个子进程绑定loop循环监听事件tcp
self::forkWorkers();
self::displayUI();
self::resetStd();
// 主进程来监听子进程状态
self::monitorWorkers();
生成实例的id
根据对象生成hashid,并且同一个对象生成的id是一样的。
$this-&workerId = spl_object_hash($this);
并且将当前对象$this存储到静态数组中self::$_workers
创建资源环境
$this-&_context = stream_context_create($context_option);
校验是否命令行模式
只有命令行模式可以执行
if(php_sapi_name() != 'cli') {}
获取初始化运行的脚本文件的绝对路径
debug_backtrace返回的数组的最后一个元素就是初始化的文件,第一个元素就是当前执行该命令的文件。
$backtrace = debug_backtrace();
$self::startFile = $backtrace[count($backtrace) -1 ]['file']
生成pid的存储文件
将debug_backtrace获取到的绝对路径通过_进行分割作为pid的文件名。
创建日志文件
如果路径没定义直接指定../workerman.log
touch(self::$logFile);
chmod(self::$logFile,0622); // 可读写、写、写
初始化当前主进程状态
self:$_status = self::STATUS_STARTING;
脚本使用self::$_status来存储当前运行的状态(current status),一共有四种状态
const STATUS_STARTING = 1;
const STATUS_RUNNING = 2;
const STATUS_SHUTDOWN=4;
// 重新加载
const STATUS_RELOADING=8;
slef::$_statisticsFile = sys_get_temp_dir().'/workerman.status';
设置进程标题
// 设置当前进程的标题
cli_set_process_title($title);
// 如果不存在上面的方法,那么使用proc_title扩展
if(extension_loaded('proctitle') && function_exists('setproctitle')) {
setproctitle($title);
每个$worker_id都是当前脚本中要初始化的实例,每个服务要开启$worker-&count个子进程,先用0来填充数组。后面每开启一个子进程,会将子进程的pid存储到idMap中,用来后面主进程监控子进程,如果子进程意外终止,主进程可以重新佛。
self::$_idMap[$worker_id] = array_fill(0, $worker-&count, 0);
进程信号处理器
pcntl_signal安装一个信号处理器,用来监听信号SIGALRM。如果不安装SIGALRM信号,当进程接收到SIGALRM信号时会默认终止进程。
pcntl_signal(SIGALRM, ['\Workerman\Lib\Timer','signalHandle'],false);
解析cli参数
允许进程脚本接受参数,用来完成相应的指令。
xxx.php start -d 会开启守护进程
xxx.php start 会进入debug模式
判断进程是否已经存在
从pid文件中读取存在的进程号,如果进程号存在,并且进程也存活。如果传入的命令是start并且pid文件中的进程id和当前脚本执行的不一样,那么说明脚本重复执行了,报错。posix_kill($master_pid,0)用来判断进程是否存在,posix_kill本意是向该进程发送信号。
// Get master process PID.
$master_pid
= @file_get_contents(self::$pidFile);
$master_is_alive = $master_pid && @posix_kill($master_pid, 0);
// Master is still alive?
if ($master_is_alive) {
// 如果已经有进程存在,并且当前执行的进程和已存在的进程不一样,报错。
if ($command === 'start' && posix_getpid() != $master_pid) {
self::log("Workerman[$start_file] already running");
使用pcntl_signal注册了信号,然后使用posix_kill发送信号,使用pcntl_signal_dispatch来处理信号。
先发送kill -SIGINT ,千分之一毫秒之后发送kill -SIGKILL
删掉临时文件
向进程发送SIGUSR2信号
从临时文件中读取内容
restart、stop
向进程发送终止信号
$master_pid && posix_kill($master_pid, SIGINT);
while(1)循环判断是否进程已经被终止,如果超过时间5s,那么写入日志终止失败,并且结束当前脚本。(没有结束进程,只是结束了当前的命令脚本)
发送特殊信号SIGUSR1,但是这个信号不会被立即执行,而是需要等待pcntl_signal_dispatch来进行信号分发。
posix_kill($master_pid,SIGUSR1);
例如用户现在输入了php worker.php reload,那么会使所有的进程重新进行加载配置。
当前脚本解析参数
判断进程是否存活,如果进程pid存在,但是进程没有存活,那么报错,说not run
进程存活,注册信号SIGUSR1
退出当前脚本
运行中的父进程开始触发所有的信号,由于之前已经安装了信号处理方法,所以会触发self::reload()方法。
启动守护进程
fork两次并不是为了避免僵尸进程,而是为了避免svr4系统重新打开终端
方法通用,可以稍作修改,用到别的地方。用这个方法做过一个队列监听,redis的list启动一个rpop。umask(0)将默认权限的掩码修改为0,即将要创建的所有的文佳你的权限都是777$pid = pcntl_fork()启动子进程,判断$pid是否存在,只有在父进程中pcntl_fork()才会返回id,我们要将父进程kill掉。posix_setsid()将当前子进程设置为会话组leader再次创建子进程,为了防止在SVR4的系统下重新打开控制终端。
protected static function daemonize()
if (!self::$daemonize) {
// 将默认权限掩码修改为0,意味着即将要创建的文件的权限都是777
$pid = pcntl_fork();
// 子进程创建失败
if (-1 === $pid) {
throw new Exception('fork fail');
} elseif ($pid & 0) {
//说明当前进程是父进程,只有在父进程中fork才会返回pid
// 关闭父进程,让子进程成为孤儿进程被init进程收养
// 将子进程作为进程组的leader,开启一个新的会话,脱离之前的会话和进程组。即使用户logout也不会终止
if (-1 === posix_setsid()) {
throw new Exception("setsid fail");
// Fork again avoid SVR4 system regain the control of terminal.
// 避免svr4系统重新获取控制终端
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception("fork fail");
} elseif (0 !== $pid) {
// 如果不是孙子进程,直接干掉。让孙子进程成为孤儿进程被init进程(1号进程收养)
初始化所有的woker实例
获取进程的当前用户信息
$user_info = posix_getpwuid(posix_getuid());
创建socket服务,启动一个本地的socket服务。如果是unix域socket的话,$local_socket需要是本地的文件。个人理解就是通过某个端口来监听某个协议。
$this-&_mainSocket = stream_socket_server($local_socket, $errno, $errmsg, $flags, $this-&_context);
将包含socket的流导入到socket的扩展资源中
Imports a stream that encapsulates a socket into a socket extension resource.
php提供两种socket:php提供了两种类型的socket,stream_socket 和 sockets,二者api不兼容。stream_socket是php内置的,可以直接使用,并且api和stream 的api通用(可以调用fread fwrite...)。sockets需要php安装sockets扩展才能使用。
设置socket
设置心跳检测,减少传输延迟,设置为非阻塞模式。
SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入
@socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
设置最小化传输延迟
// 最小化传输延迟,而不是追求最小化报文数量
@socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
设置为非阻塞模式
在非阻塞模式下,调用 fgets() 总是会立即返回;而在阻塞模式下,将会一直等到从资源流里面获取到数据才能返回。
stream_set_blocking($this-&_mainSocket,0);
注册信号处理器
pcntl_signal(SIGINT, array('\Workerman\Worker', 'signalHandler'), false);
// reload 重新加载的时候发送R1信号
pcntl_signal(SIGUSR1, array('\Workerman\Worker', 'signalHandler'), false);
// status 查看状态的时候发送R2信号
pcntl_signal(SIGUSR2, array('\Workerman\Worker', 'signalHandler'), false);
pcntl_signal(SIGPIPE, SIG_IGN, false);
保存当前进程的pid
如果已经开启了守护进程,那么获取的是当前孙子进程(守护进程)的pid,将其写入到pid文件中。
将每个woker实例(服务)创建count个子进程
遍历整个$workers,将进程按照里面的每一个实例的参数fork count个数的子进程。并且将子进程的号码,记录到父进程中
// 创建子进程
$pid = pcntl_fork();
// Get available worker id.
$id = self::getId($worker-&workerId, 0);
// For master process.
// 如果当前进程是父进程
if ($pid & 0) {
// 将子进程的号码,记录到父进程中(重要)
self::$_pidMap[$worker-&workerId][$pid] = $
// 父进程将自己的pid写入到idMap的第一位(非常重要),在此之后,同一个实例下每个进程的id都不一样。
self::$_idMap[$worker-&workerId][$id]
} // For child processes.
让每个子进程下都开始启动对应worker的服务
$worker-&run();
设置当前状态为运行中
self::$_status = self::STATUS_RUNNING;
设置globalEvent
self::getEventLoopName从三个事件扩展中选择一个libevent,event,ev
向socket添加事件监听回调函数
self::$globalEvent-&add($this-&_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection'));
终端下打印UI界面
再次重置标准化输入输出
self::resetStd();
监控所有子进程
pcntl_signal()函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是pcntl_signal_dispatch()函数,而posix_kill会发送信号。
只有父进程才会监控所有的子进程,因为子进程运行的是run方法
触发所有的信号
等待子进程退出
将退出的子进程从实例维护的pidMap中移除
将退出的子进程从对应的idMap中进行重置为0,取消占取的位置
如果脚本仍然在执行,但是子进程退出了,那么重启子进程
获取所有的子进程号,如果子进程都退出了,那么结束父进程
关于pcntl_wait
pcntl_signal(SIGUSR1,'signalHandle1'); 用来注册信号;posix_kill($pid,SIGUSR1)向指定进程发送信号(返回结果是即时的,但是并不会触发信号所绑定的行为);pcntl_signal_dispatch()用来触发收到的信号的回调函数。
pcntl_wait($status,WUNTRACED)开启阻塞模式来监控子进程是否退出,之后子进程退出之后,才会执行后面的操作。
$pid = pcntl_fork();
if($pid == -1) {
echo "创建子进程失败\n";
function signalHandle1() {
echo "信号1回调\n";
function signalHandle2() {
echo "信号2回调\n";
// 注册信号
pcntl_signal(SIGUSR1, 'signalHandle1');
pcntl_signal(SIGUSR2, 'signalHandle2');
if($pid & 0) {
$status = 0;
// 等待子进程终止
pcntl_wait($status,WUNTRACED);
echo "wait已执行 {$pid}?\n";
// 执行信号的回调函数
pcntl_signal_dispatch();
// 发送信号2
posix_kill($pid,SIGUSR2);
// 处理信号2的回调函数
pcntl_signal_dispatch();
// 父进程处理信号2
// 子进程发送信号,看是否pcntl_wait会执行(结果,wait没有执行)
posix_kill($pid,SIGUSR1);
// 继续等待,观察是否pcntl_wait等子进程结束后执行
echo "子进程终止\n"; // 这个时候wait往下执行了,看来wait等待子进程让父进程进行了挂起操作
子进程终止
wait已执行 18425?
6 收藏&&|&&34
你可能感兴趣的文章
12 收藏,2.9k
1 收藏,1.6k
2 收藏,820
本作品 保留所有权利 。未获得许可人许可前,不允许他人复制、发行、展览和表演作品。不允许他人基于该作品创作演绎作品
这是干嘛的
这是干嘛的
用来做tcpserver的
用来做tcpserver的
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。  workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架。被广泛的用于手机app、手游服务端、网络游戏服务器、聊天室服务器、硬件通讯服务器、智能家居、车联网、物联网等领域的开发。支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。基于workerman开发者可以更专注于业务逻辑开发,不必再为PHP Socket底层开发而烦恼。
  workerman
  workerman是一个高性能的PHP socket服务器框架,workerman基于PHP多进程以及libevent事件轮询库,PHP开发者只要实现一两个接口,便可以开发出自己的网络应用,例如Rpc服务、聊天室服务器、手机游戏服务器等。
  workerman的目标是让PHP开发者更容易的开发出基于socket的高性能的应用服务,而不用去了解PHP socket以及PHP多进程细节。workerman本身是一个PHP多进程服务器框架,具有PHP进程管理以及socket通信的模块,所以不依赖php-fpm、nginx或者apache等这些容器便可以独立运行。
  支持HHVM
  使用PHP开发
  支持PHP多进程/多线程(多线程版本)
  标准输入输出重定向
  支持毫秒定时器
  支持基于事件的异步编程
  守护进程化
  支持TCP/UDP
  支持多端口监听
  接口上支持各种应用层协议
  支持libevent事件轮询库,支持高并发
  支持服务平滑重启
  支持PHP文件更新检测及自动加载
  支持PHP长连接
  支持以指定用户运行子进程
  支持telnet远程控制
  高性能
  下载后解压即可
  服务端开发示例WebSocket Server
  1、新建文件start.php
  &code&&span class="preprocessor"&&?php&/span&
  &span class="keyword"&use&/span&Workerman\W
  &span class="keyword"&require_once&/span&&span class="string"&'./Workerman/Autoloader.php'&/span&;
  &span class="comment"&//创建一个Worker监听2346端口,使用websocket协议通讯&/span&
  &span class="variable"&$ws_worker&/span&=&span class="keyword"&new&/span&Worker(&span class="string"&"websocket://0.0.0.0:4151"&/span&);
  &span class="comment"&//启动4个进程对外提供服务&/span&
  &span class="variable"&$ws_worker&/span&-&count=&span class="number"&4&/span&;
  &span class="comment"&//当收到客户端发来的数据后返回hello$data给客户端&/span&
  &span class="variable"&$ws_worker&/span&-&onMessage=&span class="function"&&span class="keyword"&function&/span&&span class="params"&(&/span&&/span&&span class="function"&&span class="params"&&span class="variable"&$connection&/span&&/span&&/span&&span class="function"&&span class="params"&,&/span&&/span&&span class="function"&&span class="params"&&span class="variable"&$data&/span&&/span&&/span&&span class="function"&&span class="params"&)&/span&
  {&/span&
  &span class="comment"&//向客户端发送hello$data&/span&
  &span class="variable"&$connection&/span&-&send(&span class="string"&'hello'&/span&.&span class="variable"&$data&/span&);
  &span class="comment"&//运行&/span&
  Worker::runAll()
  2、启动服务类似下图:
  php start.php start-d
  3、查看workerman运行状态类似如下界面:
  php start.php status
  测试环境:
  系统:ubuntu 12.04 LTS 64位
  内存:8G
  cpu:Intel(R)Core(TM)i3-3220 CPU 3.30GHz×4
  &span class="preprocessor"&&?php&/span&
  &span class="keyword"&use&/span&Workerman\W
  &span class="variable"&$worker&/span&=&span class="keyword"&new&/span&Worker(&span class="string"&'tcp://0.0.0.0:1234'&/span&);
  &span class="variable"&$worker&/span&-&count=&span class="number"&3&/span&;
  &span class="variable"&$worker&/span&-&onMessage=&span class="function"&&span class="keyword"&function&/span&&span class="params"&(&/span&&/span&&span class="function"&&span class="params"&&span class="variable"&$connection&/span&&/span&&/span&&span class="function"&&span class="params"&,&/span&&/span&&span class="function"&&span class="params"&&span class="variable"&$data&/span&&/span&&/span&&span class="function"&&span class="params"&)&/span&
  {&/span&
  &span class="comment"&//长连接&/span&
  &span class="variable"&$connection&/span&-&send(&span class="string"&"HTTP/1.1 200 OK\r\nConnection:keep-alive\r\nServer:workerman\1.1.4\r\n\r\nhello"&/span&);
  &span class="comment"&//短连接&/span&
  &span class="comment"&//$connection-&close("HTTP/1.1 200 OK\r\nServer:workerman\1.1.4\r\n\r\nhello");&/span&
  Worker::runAll();
  业务逻辑
  EchoServer
  短链接(每次请求完成后关闭链接,下次请求建立新的链接):
  条件:压测脚本开500个线程,每个线程链接Workerman 10W次,每次链接发送1个请求
  结果:吞吐量:3W/S,cpu:60%,内存占用:4*8M=32M
  长链接(每次请求后不关闭链接,下次请求继续复用这个链接):
  条件:压测脚本开1000个线程,每个线程链接Workerman 1次,每个链接发送10W请求
  结果:吞吐量:15.6W/S,cpu:68%,内存占用:4*8M=32M
  无流量抖动,无内存泄漏,性能非常强悍
阅读(...) 评论()输入关键字进行搜索
我想通过LVS负载均衡实现多台gateway来满足高并发,如果LVS把用户A连接到了gateway1,用户B连接到了gateway2,两个用户不在一个gateway下面,这两个用户如何互相发送数据?实现原理是什么呢?盼回复。
workerman启动过程:
workermangateway/worker模型中有两种进程,gateway进程(负责异步网络IO)和worker进程(负责业务逻辑)。wokerman启动时会启动gateway进程和worker进程,gateway进程启动时会向一个存储(文件或者memcache,可配置)写入每个gateway进程的内部通讯地址,worker进程启动时会读取存储,并向每个geteway内部通讯地址建立起长连接。同时worker进程中会有个定时任务,定时监测存储中是否有新的gateway内部通讯地址加入,有的话同样建立长连接(这样增加workerman服务器就会自动探测到,新的gateway、worker进程就会加入集群)。gateway和worker之间的通讯就是靠内部的长连接通讯的,每个gateway进程会和后端所有worker进程有一个长连接,每个gateway进程都知道有多少worker进程,每个worker进程也知道有哪些gateway进程。
广播和单独发送
发送数据分为两种,一种是广播(给所有在线客户端发),另外一种是单独给某客户端发送。
广播比较好实现,当某个客户端要广播数据时,worker进程中调用Gateway::sendToAll($message)即可。这时worker进程会向所有gateway进程通过已经建立的长连接发送一条广播命令+数据,每个gateway进程都会收到这个命令+数据,向gateway进程自己所维护的所有客户端连接发送数据(异步)。
单独发送:
单独发送调用Gateway::sendToClient($client_id, $message);。也就是说单独发送需要得到对方的client_id。workerman发送向client_id发送数据时,需要通过client_id获得到client_id对应的gateway内部通信地址,而这个地址是在每个客户端连接gateway时就存储了的(文件或者memcache,可配置),通过这个存储得知gateway内部通讯地址后,向这个地址发送命令+数据,对应gateway进程收到命令+数据后找到对应的客户端socket发送数据即可。
要回复问题请先或
浏览: 4299
关注: 3 人
Powered by

我要回帖

更多关于 python多进程共享内存 的文章

 

随机推荐