php进程间通信 yoc_续上篇Swoole多进程数据共享的问题

3f1c1241ce88da9c78465c154ff0dd0c.png

原因

进程作为程序执行过程中资源分配的基本单位,拥有独立的地址空间,同一进程的线程可以共享本进程的全局变量,静态变量等数据和地址空间,但进程之间资源相互独立。

由于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

解决方法:

swoole为我们提供了两种有效的解决方法,都是基于多进程内存型数据库,代替单进程PHP变量来存储fd

第一种为:swoole_redis,特点是使用简单,跟PHP原生的redis用法几乎一致。

第二种为:swoole_table,这是swoole官方研制的一款内存型数据库,比redis的可扩展性要强许多,单机器的情况下牛牛推荐大家使用这种方法。

Swoole_Tbale

swoole_redis没什么好谈的,因为redis都是个老活了,用法都一个鸟样。

下面我们就重点搞下swoole_table的用法。

一波官方说明袭来:

Table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。

请谨慎使用数组方式读写Table, 建议使用文档中提供的API来进行操作
数组方式取出的SwooleTableRow对象为一次性对象, 请勿依赖其进行过多操作

优势

性能强悍,单线程每秒可读写200万次
应用代码无需加锁,Table内置行锁自旋锁,所有操作均是多线程/多进程安全。用户层完全不需要考虑数据同步问题。
支持多进程,Table可以用于多进程之间共享数据
使用行锁,而不是全局锁,仅当2个进程在同一CPU时间,并发读取同一条数据才会进行发生抢锁
Table的内存容量不受PHP的memory_limit控制

官方文档地址:https://wiki.swoole.com/wiki/page/p-table.html

多进程数据共享的WebSocket例子:

<?php
// +----------------------------------------------------------------------
// 小黄牛blog - Swoole 即时通讯交互处理
// +----------------------------------------------------------------------
// Copyright (c) 2018 https://xiuxian.junphp.com All rights reserved.
// +----------------------------------------------------------------------
// Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// Author: 小黄牛 <1731223728@qq.com>
// +----------------------------------------------------------------------
class Server{/*** 客户端身份存储器*/private $_table = []; /*** WS的启动实例*/private $_ws;/*** host-IP,0.0.0.0表示允许接收所有请求*/private $_host = '0.0.0.0';/*** 端口号*/private $_port = '9502';/*** 最大服务端心跳重连次数*/private $_max  = 3;/*** 强制心跳重连启动状态*/private $_status = true;/*** 这是启动服务端的入口*/public function run() { $this->start_service(); $this->start_table(); $this->start_handshake();$this->start_message();$this->end();}/*** ①启动websocker服务*/private function start_service() {# 创建websocket服务器对象,监听0.0.0.0:9502端口$this->_ws = new swoole_websocket_server($this->_host, $this->_port);$this->_ws->set(['worker_num' => 4,// 开4个工作进程]);}/*** ①创建Table服务*/private function start_table() {# 创建最大只能存储1024个用户的数据$this->_table = new swoole_table(1024);# 创建字段$this->_table->column('fd', swoole_table::TYPE_INT, 8); // FD$this->_table->column('status', swoole_table::TYPE_INT, 8); // 离线状态$this->_table->column('heartbeat', swoole_table::TYPE_INT, 8); // 心跳重连数$this->_table->column('user_id', swoole_table::TYPE_STRING, 32); // 会员ID$this->_table->column('user_nice', swoole_table::TYPE_STRING, 32); // 会员名称$this->_table->create();# 将表附加到ws实例里,方便后续使用$this->_ws->user = $this->_table;}/*** ②监听WebSocket握手申请*/private function start_handshake() {# 监听WebSocket连接打开事件$this->_ws->on('open', function ($ws, $request){# 这里可以做些鉴权验证之类的});}/*** ③监听客户端消息发送请求*/private function start_message() {# 监听WebSocket消息事件$this->_ws->on('message', function ($ws, $frame) {$data    = json_decode($frame->data, true);$user_id = $data['user_id'];# 加入存储器$this->_ws->user->set($user_id, ['fd'        => $frame->fd, # FD'status'    => 1, # 设置上线状态'heartbeat' => 0, # 重置心跳重连数'user_id' => $data['user_id'], # 用户ID'user_nice' => $data['user_nice'], # 用户昵称]);# 登录广播处理if ($data['code'] == 1) {# 发送广播上线消息$data['content'] = '【'.$data['user_nice'].'】骑着小黄牛上线啦~!';$this->broadcast($ws, $this->json($data), $user_id);# 心跳重连检测} else if ($data['code'] == 4) {$this->broadcast($ws, $frame->data, $user_id);$this->timer();# 其他请求} else {# 广播消息$this->broadcast($ws, $frame->data, $user_id);}});} /*** ④监听客户端退出事件*/private function end() {# 这里加入了unset,清除open存储器,防止存储器无限增大# 监听WebSocket连接关闭事件$this->_ws->on('close', function ($ws, $fd) {# 这块提取用户信息还有优化空间,实际开发中这样for会消耗内存$user = null;foreach ($this->_ws->user as $k=>$v) {if ($v['fd'] == $fd) {$user = $v;break;}}# 获取用户ID$user_id = $user['user_id'];# 获取用户nice$user_nice = $user['user_nice'];# 设置离线状态$this->_ws->user->set($user_id, ['status'    => 0, # 设置离线状态]);$data = ['code' => 2,'user_id' => $user_id,'user_nice' => $user_nice,'content' => '【'.$user_nice.'】骑着小扫帚灰溜溜的走了~~!'];# 广播消息$this->broadcast($ws, $this->json($data));});$this->_ws->start();}/*** 广播消息* @todo 无* @author 小黄牛* @version v1.0.0.1 + 2018.11.12* @deprecated 暂不弃用* @global 无* @param object $wx 实例* @param string $content 广播内容* @param string $id 用户的userid*  @param bool $status 是否做心跳限制 * @return void*/private function broadcast($ws, $content, $id=null, $status=false) {# 向所有人广播foreach ($this->_ws->user as $k=>$v) {# 不向自己广播,并且要在线的# 注意,这里一定要有上线状态的限制,否则假设用户已经退出,但你的进程还开着,实际上已经关闭,这时候push就会报错# 只有正常在线的用户才能接收到广播# 加入心跳检测限制if ($k != $id && $v['status'] == 1 && $status == true) {$ws->push($v['fd'], $content);} else if ($v['user_id'] != $id && $v['status'] == 1 && $v['heartbeat'] == 0) {$ws->push($v['fd'], $content);}}}/*** 数组转json* @todo 无* @author 小黄牛* @version v1.0.0.1 + 2018.11.08* @deprecated 暂不弃用* @global 无* @param array $array 数组* @return json*/private function json($array) {return json_encode($array, JSON_UNESCAPED_UNICODE);}/*** 服务端定时强制心跳检测* @todo 无* @author 小黄牛* @version v1.0.0.1 + 2018.11.08* @deprecated 暂不弃用* @global 无* @return void*/private function timer() {# 注意强制心跳触发器不能放在open事件里,因为那时候用户还没有提交登录请求,是还没有userID的# 还有,强制心跳定时器只能触发一次,否则会出现生成多个定时器的情况if ($this->_status) {$this->_status = false;/*** ⑤服务端强制心跳检测* 每隔1分钟发送1次,如果连续3次强制心跳检测未通过,服务端将强制断开连接*/$obj = $this;swoole_timer_tick(60000, function ($timer_id) use (&$obj) {# 广播消息$obj->broadcast($obj->_ws, $obj->json(['code' => 5]), null, true);# 所有人的心跳次数+1foreach ($this->_ws->user as $k=>$v) {if (empty($v['heartbeat'])) {# 重置心跳次数$this->_ws->user->set($v['user_id'], ['heartbeat' => 0,]);}# 心跳次数累加$this->_ws->user->set($v['user_id'], ['heartbeat' => $v['heartbeat']+1]);# 心跳次数大于等于_max && 在线的 的连接关闭if ($v['heartbeat'] >= $obj->_max && $v['status'] == 1) {$data = $v;# 发送强制掉线广播$data['code'] = 6;$data['content'] = '【'.$data['user_nice'].'】已被服务端强制下线!';$obj->broadcast($obj->_ws, $obj->json($data), null, true);# 这里不需要unset连接,因为在close事件中,已经将这个连接设置为离线了# 主动关闭连接k$obj->_ws->close($v['fd']);}}});}}
}
$socketServer = new Server();
$socketServer->run();

最后推荐大家可以用下我开源的一个基于Swoole4.5+研发的PHP框架。该框架基于注解实现了很多好玩的功能,很适合新人快速上手Swoole扩展。

SW-X框架-专注高性能便捷开发而生的PHP-SwooleX框架​www.sw-x.cn
6c4bf0fcc8fd93cfe23676097afdd1e1.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376245.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解读Google分布式锁服务

背景介绍 在2010年4月&#xff0c;Google的网页索引更新实现了实时更新&#xff0c;在今年的OSDI大会上&#xff0c;Google首次公布了有关这一技术的论文。 在此之前&#xff0c;Google的索引更新&#xff0c;采用的的批处理的方式(map/reduce)&#xff0c;也就是当增量数据达到…

必应输入法产品分析

2013年4月&#xff0c;微软MSN(中国)宣布推出首款整合搜索体验的中文云输入法“必应Bing输入法”&#xff0c;其前身是“英库拼音输入法(于2012年8月发布测试版)” 在此&#xff0c;Fruits小组从宏观的软件工程角度和微观的产品实现细节对必应输入法进行了考察和分析。 &#x…

抓localhost包 - rawcap

抓localhost包的话用wireshark好像有点麻烦&#xff0c;所以用rawcap RawCap官网 RawCap下载连接 直接运行&#xff0c;首先根据需要选择监听相应的网卡&#xff0c;然后再填写抓包文件保存的名字

持续集成交付CICD:Jira 发布流水线

目录 一、实验 1.环境 2.GitLab 查看项目 3.Jira 远程触发 Jenkins 实现合并 GitLab 分支 4.K8S master节点操作 5.Jira 发布流水线 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构版本IP备注master1K8S master节点1.20.6192.168.204.180 jenkins…

三个彩灯循环点亮程序_近百组彩灯点亮江畔,义渡灯会正式亮灯啦

10月23日晚上&#xff0c;大渡口区义渡古镇华灯初上。夜幕之下&#xff0c;2020第一届义渡灯会亮灯仪式在此举行&#xff0c;来自四川的近百组彩灯将在这里点亮夜空&#xff0c;一直陪伴广大市民游客至明年元宵节后。当晚6点半&#xff0c;义渡灯会亮灯仪式正式开启。本次灯会以…

QPushButton hover配置

鼠标移动到QPushButton上面时显示下划线 //下面是当鼠标移动到按钮上时&#xff0c;按钮上的文字显示下划线 QPushButton#Button_2:hover{ text-decoration:underline; }//下面是普通显示 QPushButton#Button_2{ color:rgba(52, 144, 255 ,255); border-radius:0px; backgrou…

eclipse没有日志_强化公共DHT以抵抗eclipse攻击,ipfs官方还说了什么?

近日&#xff0c;IPFS官方发布博客&#xff0c;就如何强化公共DHT以抵抗eclipse攻击进行详细介绍&#xff0c;星球君帮大家翻译了一下&#xff0c;让我们来看看官方都说了什么吧&#xff1a;IPFS 2020 年的一个主要焦点是随着网络规模的不断扩大而改进内容路由。虽然我们已经对…

HTTP基础10--web(2)

因输出值转义不完全引发的安全漏洞 实施 Web 应用的安全对策可大致分为以下两部分。 客户端的验证Web 应用端&#xff08;服务器端&#xff09;的验证: 输入值验证 / 输出值转义客户端允许篡改数据或关闭 JavaScript&#xff0c;不适合将 JavaScript 验证作为安全的防范对策。保…

单一课和综合课的划分依据_武夷岩茶产地如何划分?

产地是指某种物品的生产、出产或加工制造的地点&#xff0c;日常含义是指某种物品的主要生产地。本文探讨的武夷岩茶种植产地&#xff0c;也就是当地茶人俗称的“山场”。武夷岩茶“山场”的俗称可能缘起于宋代的茶政。宋代官府设置“榷&#xff08;qu&#xff09;茶场”&#…

C# 枚举 字符串 转换

普通方法 这种方法尽管很SB但确实可以解决问题 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){string SelPath "";switch (comboBox1.SelectedIndex){case 0: SelPath System.Environment.GetFolderPath(System.Environment.SpecialFo…

arduino 机器视觉编程_万物皆可仿真的MATLAB/Simulink神奇在哪?解析如何将其应用于一整套机器人设计开发流程...

MATLAB/Simulink&#xff1a;万物皆可仿真 MATLAB是由美国MathWorks公司出品的一款商业数学软件。它是一个多功能的科学计算平台&#xff0c;将算法开发、数据分析、矩阵计算等诸多强大功能集成在一个易于操作的视窗环境中。MATLAB下的Simulink更是被认为可以“仿真任何系统”。…

boost 变量类型转换

如果vs版本比较低&#xff0c;会不支持一些std类型转换函数&#xff08;vs2008就不支持&#xff09;&#xff0c;比如&#xff1a; std::to_string \\数字转字符串 std::stoll \\字符串转数字而且项目碰巧用boost库&#xff0c;可以考虑用下面的的方法来进行类型转换…

android 系统源码调试 局部变量值_如何方便快速的整编Android 9.0系统源码?

点击上方“刘望舒”&#xff0c;选择“星标”多点在看&#xff0c;就是真爱&#xff01;作者 : 刘望舒 | 来源 &#xff1a;刘望舒的博客地址&#xff1a;http://liuwangshu.cn/framework/aosp/3-compiling-aosp.html前言在上一篇文章是时候下载Android 9.0系统源码了中&…

让“是男人就下到100层”在Android平台上跑起来

原工程:https://github.com/jeekun/DownFloors 移植后的代码&#xff1a;HelloCpp.zip 移植后的APK&#xff1a;HelloCpp.apk 说明&#xff1a;&#xff08;cocos2d-x版本是“ 2.2&#xff09; 1.该工程是直接在HelloCpp上修改完成,所以包名也不修改了 2.原工程里面可能是采用g…

Codeforces Round #277 (Div. 2) 题解

Codeforces Round #277 (Div. 2)A. Calculating Functiontime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputFor a positive integer n lets define a function f: f(n)   - 1  2 - 3  ..  ( - 1)nn Your …

QT 边框圆角处理

平时的边框是平角的&#xff1a; 如果需要圆角的话&#xff0c;就要加stylesheet加上这个&#xff1a; border-radius:3px;比如&#xff1a; QPushButton{ border-radius:3px; }就变成圆角了&#xff1a; px前面的数字越大就越圆&#xff0c;比如5px比3px圆 假如只需要某一…

3级调度 fpga_Vivado HLS学习笔记——1.了解FPGA架构

本篇文章为本人学习Xilinx的Vivado HLS教程记录的学习笔记&#xff0c;仅供学习参考。Vivado HLS官方视频教程&#xff1a;优酷视频​v.youku.com目录&#xff1a; Vivado HLS课程简介FPGA与CPU、GPU、DSP的区别FPGA的优势Xilinx FPGA架构:逻辑单元、算术逻辑单元、存储单元使用…

BZOJ2435 [Noi2011]道路修建

这是NOI11年题&#xff0c;你在逗我&#xff1f; 直接dfs就可以了&#xff0c;Linux下貌似不会爆栈。。。 1 /**************************************************************2 Problem: 24353 User: rausen4 Language: C5 Result: Accepted6 Time:5184 …

Qt异常结束程序无法重新运行

有时候代码有问题会导致qt异常结束 修改完后重新运行又会出现 查看任务管理器又没有这个进程 可以使用资源管理器打开看看 也可以考虑使用process explorer查看 发现程序挂起来&#xff0c;结束掉它就可以重新运行了

Qt新添加的类无法链接

通过这个方法给工程添加了个类&#xff1a; 编译的时候就出现了这个问题&#xff1a; 执行一下qmake 然后再重新构建项目就可以了