swoole 多进程共享数据
进程作为程序执行过程中资源分配的基本单位,拥有独立的地址空间,同一进程的线程可以共享本进程的全局变量,静态变量等数据和地址空间,但进程之间资源相互独立.由于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 多进程共享数据的更多相关文章
- Nodejs中cluster模块的多进程共享数据问题
Nodejs中cluster模块的多进程共享数据问题 前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核 ...
- Android开发中多进程共享数据
# 背景 最近在工作中遇到一个需求,需要在接收到推送的时候将推送获得的数据存起来,以供app启动时使用.我们会认为这不是So easy吗?只要把数据存到SharedPreferences中,然后让ap ...
- 多进程共享数据,真正的通信Manager
Managers A manager object returned by Manager() controls a server process which holds Python objects ...
- Python 进程之间共享数据
最近遇到多进程共享数据的问题,到网上查了有几篇博客写的蛮好的,记录下来方便以后查看. 一.Python multiprocessing 跨进程对象共享 在mp库当中,跨进程对象共享有三种方式,第一种 ...
- Swoole 中使用 Table 内存表实现进程间共享数据
背景 在多进程模式下进程之间的内存是相互隔离的,在一个工作进程中的全局变量和超全局变量,在另一个工作进程中是无法读取和操作的. 如果只有一个工作进程,则不存在进程隔离问题,可以使用全局变量和超全局变量 ...
- python并发编程之多进程(三):共享数据&进程池
一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...
- 通过 Swoole\Table 实现 Swoole 多进程数据共享
第三方存储媒介 前面我们介绍了基于 Swoole 的 Process 及 Process\Pool 模块在 PHP 中实现多进程管理,但是多进程模式下进程间是相互隔离的,无法共享数据和变量,即便是通过 ...
- python 进程间共享数据 (二)
Python中进程间共享数据,除了基本的queue,pipe和value+array外,还提供了更高层次的封装.使用multiprocessing.Manager可以简单地使用这些高级接口. Mana ...
- python 进程间共享数据 (一)
def worker(num, mystr, arr): num.value *= 2 mystr.value = "ok" for i in range(len(arr)): a ...
随机推荐
- JVM(Java虚拟机)详解(JDK7)
1.Java内存区域 运行时数据区域: Java 虚拟机在执行Java程序时,定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁.另外一些则是与线程 ...
- Little Sub and Mr.Potato's Math Problem (构造法)
题目传送门Little Sub and Mr.Potato's Math Problem Time Limit: 2 Seconds Memory Limit: 65536 KB Littl ...
- JVM(17)之 准备-解析-初始化
开发十年,就只剩下这套架构体系了! >>> 在类加载机制的五个阶段中,我们已经讲完了第一个阶段.剩下的四个阶段由于涉及到比较多的类文件相关的知识,现在讲了会看得很吃力,所以我们暂 ...
- unity DOTween Pro的使用--简化流程--自动播放
当gameobject setActive(true)的时候自动播放动画 1) 添加DoTween Animation. 设置动画效果, 略 选中 AutoPlay, 取消 AutoKill 2) 在 ...
- linux性能相关的各个环节
- CodeForces - 343D 树链剖分
题目链接:http://codeforces.com/problemset/problem/343/D 题意:给定一棵n个n-1条边的树,起初所有节点权值为0,然后m个操作. 1 x:把x为根的子树的 ...
- Sass-变量计算
在 Sass 中除了可以使用数值进行运算之外,还可以使用变量进行计算,其实在前面章节的示例中也或多或少的向大家展示了.在 Sass 中使用变量进行计算,这使得 Sass 的数学运算功能变得更加实用.一 ...
- javascript中数组元素删除方法splice,用在for循环中巨坑
一.demo splice: 该方法会改变自动原始数组长度 实例: var array = ["aa","dd","cc","aa ...
- Java Web入门二
Web应用服务器 供向外发布web资源的服务器软件. Web资源 存在于Web服务器可供外界访问的资源就是web资源.例如:存在于web服务器内部的Html.CSS.js.图片.视频等. 静态资源 w ...
- webpack.config.js配置入口出口文件
目录结构: 新建webpack.config.js配置文件 const path = require('path') //导出 path是node内置的包 通过npm init初始化得到package ...