进程作为程序执行过程中资源分配的基本单位,拥有独立的地址空间,同一进程的线程可以共享本进程的全局变量,静态变量等数据和地址空间,但进程之间资源相互独立.由于PHP语言不支持多线程,因此Swoole使用多进程模式,再多进程模式下就存在进程内存隔离,进程间通信与数据共享问题.

swoole中master主进程会创建manager管理进程和reactor线程,真正的工作进程为worker进程.  manager是创建和管理worker进程,reactor进程测试监听socket,接受数据任务,发送给worker进程去工作,因此所有业务逻辑最终都是在worker进程中进行的,worker进程之间的数据共享与通信必不可少.

swoole中 设置选项worker_num设置 启动的worker进程数,默认设置为CPU核数

 $server = new swoole_server('127.0.0.1',9898);
$server->set(array(
'worker_num' => 4, //设置启动的Worker进程数。
));

如上面说描述,进程存在进程隔离:

 $fds = array();
$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
global $fds;
$fds[] = $fd;
var_dump($fds);
});

$fds虽然是全局变量,但是只在但前的进程内有效,swoole服务器底层会创建多个worker进程,此处打印出来的只有部分连接的fd,本文讲简述两种解决方案的简单示例:

1.外部存储服务 : Redis

作为内存数据库redis 无太多IO等待,并且读写速度快

示例代码:以简易聊天室websocket服务 swoole_websocket_server为例

 1 $ws = new swoole_websocket_server("0.0.0.0", 9999);
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$ws->set(array(
'daemonize' => false,
'worker_num' => 4,
));
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) use($redis) {
var_dump($request->fd, $request->get, $request->server);
//记录连接
$redis->sAdd('fd', $request->fd);
$count = $redis->sCard('fd');
var_dump($count);
$ws->push($request->fd, 'hello, welcome ☺ 当前'.$count.'人连接在线');
});
//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) use($redis) {
$fds = $redis->sMembers('fd');
$data = json_decode($frame->data,true);
if($data['type'] ==1 ){
$redis->set($frame->fd,json_encode(['fd'=>$frame->fd,'user'=>$data['user']]));
//通知所有用户新用户上线
$fds = $redis->sMembers('fd');$users=[];
$i=0;
foreach ($fds as $fd_on){
$info = $redis->get($fd_on);
$users[$i]['fd'] = $fd_on;
$users[$i]['name'] = json_decode($info,true)['user'];
$message = "欢迎 <b style='color: darkmagenta ;'>".$data['user']."</b> 进入聊天室";
$push_data = ['message'=>$message,'users'=>$users];
$ws->push($fd_on,json_encode($push_data));
$i++;
}
}else if($data['type'] ==2){
if($data['to_user'] == 'all'){
foreach ($fds as $fd){
$message = "<b style='color: crimson'>".$data['from_user']." say:</b> ".$data['msg'];
$push_data = ['message'=>$message];
$ws->push($fd,json_encode($push_data));
}
}
}
echo "Message: {$frame->data}\n";
});
//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) use ($redis){
$redis->sRem('fd',$fd);
$fds = $redis->sMembers('fd');
$i=0;
foreach ($fds as $fd_on){
$user = json_decode($redis->get($fd),true)['user'];
$info = $redis->get($fd_on);
$users[$i]['fd'] = $fd_on;
$users[$i]['name'] = json_decode($info,true)['user'];
$message = "<b style='color: blueviolet'>".$user."</b> 离开聊天室了";
$push_data = ['message'=>$message,'users'=>$users];
$ws->push($fd_on,json_encode($push_data));
$i++;
}
echo "client-{$fd} is closed\n";
});

2.共享内存拓展:swoole_table

swoole_table是swoole官方提供的基于共享内存和锁实现的超高性能冰饭数据结构.swoole_table在swoole1.7.5版本后可用.

目前swoole只支持3种类型:

swoole_table::TYPE_INT 整形字段

swoole_table::TYPE_FLOAT浮点字段

swoole_table::TYPE_STRING 字符串字段

函数方法:

column() :给内存表增加一列 参数:字段名,字段类型,字节数

$table->column('id', swoole_table::TYPE_INT, 4);

create():基于前一步对表结构的创建,执行创建表.

set() :设置行的数据(key-value的方式) 参数: 数据的key,数据的值(必须数组,键名必须与字段定义的$name相同)

$table->set($fd, ['id'=>1]);

get() :获取一行数据  参数:数据的key

$table->get($fd);

del() :删除一行数据 参数:数据的key

$table->del($fd);

lock():锁定整个表

unlock():释放锁

lock/unlock 必须成对出现,否则会发生死锁.

示例代码: 还是上面的websocket服务为例

 class WebSocketServer {
private $server;
public function __construct()
{
$this->server = new swoole_websocket_server("0.0.0.0",9988);
$this->server->set(array(
'daemonize' => false,
'worker_num' => 4,
));
//内存表
$fd_table = new swoole_table( 1024 );
$fd_table->column( "user",swoole_table::TYPE_STRING, 30 );
$fd_table->column( "time", swoole_table::TYPE_STRING, 20 );
$fd_table->create(); $user_table = new swoole_table(1024);
$user_table->column("fd",swoole_table::TYPE_INT,8);
$user_table->create(); $this->server->fd = $fd_table;
$this->server->user = $user_table; //启动开始
$this->server->on('Start',[$this,'onStart']);
//与onStart同级
$this->server->on('workerStart',[$this,'onWorkerStart']);
//webSocket open 连接触发回调
$this->server->on('open',[$this,'onOpen']);
//webSocket send 发送触发回调
$this->server->on('message', [$this, 'onMessage']);
//webSocket close 关闭触发回调
$this->server->on('Close', [$this, 'onClose']);
//tcp连接 触发 在 webSocket open 之前回调
$this->server->on('Connect', [$this, 'onConnect']);
//tcp 模式下(eg:telnet ) 发送信息才会触发 webSocket 模式下没有触发
$this->server->on('Receive', [$this, 'onReceive']);
// 服务开启
$this->server->start(); } public function onStart( $server)
{
echo "Start\n";
} public function onWorkerStart($server,$worker_id)
{
//判断是worker进程还是 task_worker进程 echo 次数 是worker_num+task_worker_num
if($worker_id<$server->setting['worker_num']){
echo 'worder'.$worker_id."\n";
}else{
echo 'task_worker'.$worker_id."\n";
}
// echo "workerStart{$worker_id}\n";
} public function onOpen( $server,$request)
{
$this->server->fd->set($request->fd,['user'=>'']);
echo "server: handshake success with fd{$request->fd}\n";
$count = count($server->connections);
$server->push($request->fd, 'hello, welcome ☺ 当前'.$count.'人连接在线');
} public function onMessage( $server,$frame)
{
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$data = json_decode($frame->data,true);
if($data['type'] ==1 ){
$server->fd->set($frame->fd,['user'=>$data['user']]);
//通知所有用户新用户上线
foreach($server->connections as $key => $fd) {
$server->push($fd, "欢迎 <b style='color: darkmagenta ;'>".$data['user']."</b> 进入聊天室");
}
}else if($data['type'] ==2){
if($data['to_user'] == 'all'){
foreach($server->connections as $key => $fd) {
$server->push($fd, "<b style='color: crimson'>".$data['from_user']." say:</b> ".$data['msg']);
}
}
}
} public function onConnect( $server, $fd, $from_id ) {
echo "Client {$fd} connect\n";
echo "{$from_id}\n";
} public function onReceive( $server, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
} public function onClose($server, $fd)
{
echo "Client {$fd} close connection\n";
foreach($server->connections as $key => $on_fd) {
$user = $server->fd->get($fd)['user'];
$server->push($on_fd, "<b style='color: blueviolet'>".$user."</b> 离开聊天室了");
}
}
}
new WebSocketServer();

swoole 多进程共享数据的更多相关文章

  1. Nodejs中cluster模块的多进程共享数据问题

    Nodejs中cluster模块的多进程共享数据问题 前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核 ...

  2. Android开发中多进程共享数据

    # 背景 最近在工作中遇到一个需求,需要在接收到推送的时候将推送获得的数据存起来,以供app启动时使用.我们会认为这不是So easy吗?只要把数据存到SharedPreferences中,然后让ap ...

  3. 多进程共享数据,真正的通信Manager

    Managers A manager object returned by Manager() controls a server process which holds Python objects ...

  4. Python 进程之间共享数据

    最近遇到多进程共享数据的问题,到网上查了有几篇博客写的蛮好的,记录下来方便以后查看. 一.Python multiprocessing 跨进程对象共享  在mp库当中,跨进程对象共享有三种方式,第一种 ...

  5. Swoole 中使用 Table 内存表实现进程间共享数据

    背景 在多进程模式下进程之间的内存是相互隔离的,在一个工作进程中的全局变量和超全局变量,在另一个工作进程中是无法读取和操作的. 如果只有一个工作进程,则不存在进程隔离问题,可以使用全局变量和超全局变量 ...

  6. python并发编程之多进程(三):共享数据&进程池

    一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...

  7. 通过 Swoole\Table 实现 Swoole 多进程数据共享

    第三方存储媒介 前面我们介绍了基于 Swoole 的 Process 及 Process\Pool 模块在 PHP 中实现多进程管理,但是多进程模式下进程间是相互隔离的,无法共享数据和变量,即便是通过 ...

  8. python 进程间共享数据 (二)

    Python中进程间共享数据,除了基本的queue,pipe和value+array外,还提供了更高层次的封装.使用multiprocessing.Manager可以简单地使用这些高级接口. Mana ...

  9. python 进程间共享数据 (一)

    def worker(num, mystr, arr): num.value *= 2 mystr.value = "ok" for i in range(len(arr)): a ...

随机推荐

  1. java虚拟机笔记-1

    java虚拟机学习笔记 Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行.Java程序的运行需要Java虚拟机.Java API和Java Class文件的配合.Java虚 ...

  2. 虚拟机VMware,安装中标麒麟系统,64位的,版本6.0,并安装qt

    为了使用qt开发,安装中标麒麟系统. 虚拟机中安装,本来安装的是32位麒麟系统,结果发现qt无法安装(官网提供的是64位的run程序). qt安装的是qt-opensource-linux-x64-5 ...

  3. 最小生成树,Prim算法实现

    最小生成树 所谓最小生成树,就是一个图的极小连通子图,它包含原图的所有顶点,并且所有边的权值之和尽可能的小. 首先看看第一个例子,有下面这样一个带权图: 它的最小生成树是什么样子呢?下图绿色加粗的边可 ...

  4. myeclipse 文件注释部分乱码问题

    前几天安装了myeclipse,用了几天,写了一些Demo,并且都有注释,今天上午根据要求,重新配置了一下myeclipse,包括许多编码方式的改变,当时没在意,下午打开原来的Demo时,发现它们的注 ...

  5. NGUI的widget的使用

    一,我们看看widget有什么属性,如下图: 二,Pivot是什么意思? 我们都知道在Untiy3D中有一个中央坐标点,而这个Pivot这个就是选择控件的某一个点与中央坐标点定位. 如下图区别: 当你 ...

  6. vue iview分页

    距离上次博客更新已经快一个月了,期间也有想法在空闲的时候更新几篇博文. 燃鹅,最近懒癌作祟,丢掉的东西越来越多,再不遏止的话就真成癌了. 趁着刚看完一篇心灵鸡汤,让打满鸡血的我总结下前段时间用到的iv ...

  7. 前端 ----- 初探ES6 Promise

    前段时间做项目,在调用接口的时候,遇到了异步问题.开始是使用定时器,发现效果并不理想,于是又用了回调,效果还好但是,很明显的影响了代码的整洁性. 于是我想起了在面试的那段时间,背过的面试题里,出现过一 ...

  8. rsync之expect脚本shell

    r_expect.sh: #!/bin/expect -f set timeout 30 #spawn rsync -avz --delete --exclude-from=exclude.list ...

  9. python集合set,交集,并集,差集,对称差集,子集和超集

    python集合set,交集,并集,差集,对称差集,子集和超集 x = {1, 2, 3, 4} y = {2, 4, 5, 6} # 交集(取x中与y中相同部分) print(x.intersect ...

  10. Codeforces Round #393 (Div. 2) - C

    题目链接:http://codeforces.com/contest/760/problem/C 题意:有n个烤串,并且每个烤串起初都放在一个火盆上并且烤串都正面朝上,现在定义p序列,p[i]表示在i ...