进程作为程序执行过程中资源分配的基本单位,拥有独立的地址空间,同一进程的线程可以共享本进程的全局变量,静态变量等数据和地址空间,但进程之间资源相互独立.由于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. UI自动化通过文字、父子元素,兄弟元素定位

    在百度首页,通过文字,父子元素,兄弟元素进行定位 一.文字定位: 通过界面上的文字进行定位,注意如果同一个页面上存在多个同样的文字的情况,返回的值会是多个,建议只存在一个情况下才用这方法. 如:定位百 ...

  2. Java单链表

    一.概述 二.主方法 //创建头结点 private HeroNode head = new HeroNode(-1,null,null); //计数器,用于id的自增 private static ...

  3. 问题 1436: 地宫取宝 (dp)

    题目传送门 时间限制: 1Sec 内存限制: 128MB 提交: 423 解决: 94 题目描述 X  国王有一个地宫宝库.是  n  x  m  个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标 ...

  4. 专题:性能调优之工具---perf

    1. Linux Perf简介 1.1 Perf是什么 Perf 是内置于Linux 内核源码树中的性能剖析(profiling)工具.它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标 ...

  5. go web编程——自定义路由设计

    本文主要讲解go语言web编程中自定义路由器的设计.在此之前需要先了解一下go语言web编程中路由与http服务的基本原理,可以参考笔者另一篇博文:go web编程——路由与http服务 . 我们已经 ...

  6. Panabit的各种配置文件

    Panabit的各种配置文件 1.启动配置文件路径:/etc/rc.local作用:这个文件里的内容是BSD启动后会自动执行的命令默认配置如下:fsck -y -t ufs /dev/ad0s2a & ...

  7. 获取class的儿子,报错undefined

       var tds = document.getElementsByClassName("dv1")[0].children     console.log(tds) 因为cla ...

  8. lodash throttle和debounce

    https://lodash.com/docs#debounce throttle(又称节流)和debounce(又称防抖)其实都是函数调用频率的控制器 throttle:将一个函数的调用频率限制在一 ...

  9. shell脚本实现ftp上传下载文件

    前段时间工作中需要将经过我司平台某些信息核验数据提取后上传到客户的FTP服务器上,以便于他们进行相关的信息比对核验.由于包含这些信息的主机只有4台,采取的策略是将生成的4个文件汇集到一个主机上,然后在 ...

  10. 解决java compiler level does not match the version of the installed java project facet问题

    在编写项目的时候是,后来有改java写法,将工程改成了jdk1.7,后来工程就有了一个红叉,但是代码,文件里没有任何问题,也可以运行,不知道是什么原因,后来在problems里才知道是:java co ...