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,一经查实,立即删除!

相关文章

JavaBean的规范

&#xff08;1&#xff09;JavaBean 类必须是一个公共类&#xff0c;并将其访问属性设置为 public &#xff08;2&#xff09;JavaBean 类必须有一个空的构造函数&#xff1a;类中必须有一个不带参数的公用构造器&#xff0c;此构造器也应该通过调用各个特性的设置方法来设置特…

linux虚拟机ip修改无效

把一个centos虚拟机移动到另一台电脑的时候&#xff0c;移动前是静态ip&#xff0c;移动后发现虚拟机的ip不同了。 由于使用的是NAT&#xff0c;于是就修改了虚拟机的配置&#xff0c;发现虚拟机的ip仍然不是配置文件需要的情况。 可以尝试命令nmcli con show&#xff0c;如果…

验证(Verification)与确认(Validation)的差别

验证(Verification)与确认&#xff08;Validation&#xff09;的差别 说法一&#xff1a; &#xff08;2&#xff09;“验证(Verification)”的涵义 通过提供客观证据对规定要求已得到满足的认定。 &#xff08;2&#xff09;“确认&#xff08;Validation&#xff09;”的涵义…

vscode自动格式化不符合eslint_VsCode(Visual Studio Code)格式化代码符合EsLint

利用Visual Studio Code ESlint插件&#xff0c;实现自动格式化代码步骤一&#xff1a;安装ESlint插件>点击Extensions或者CtrlShiftX>搜索ESlint>install EsLint步骤二: 重启VsCode&#xff0c; 发现代码提示报错&#xff0c;代码不符合规范步骤三&#xff1a;鼠标ho…

解读Google分布式锁服务

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

使用PHPMailer邮件发不出去

遇到了PHPMailer发不出去邮件的问题&#xff0c;在执行smtpConnect()时失败了&#xff0c;同样的配置在其他环境就能发送邮件。 最后发现是dns没有配置&#xff0c;解析不了邮箱服务器的域名&#xff0c;所以没发出去。。。。 如果其他语言也遇到了这样的情况&#xff0c;可以…

PHPcurl抓取AJAX异步内容(转载)

PHPcurl抓取AJAX异步内容其实抓ajax异步内容的页面和抓普通的页面区别不大。ajax只不过是做了一次异步的http请求&#xff0c;只要使用firebug类似的工具&#xff0c;找到请求的后端服务url和传值的参数&#xff0c;然后对该url传递参数进行抓取即可。 利用Firebug的网络工具 …

做自适应网站专业乐云seo_自适应网站方案品牌乐云seo

自适应网站方案品牌乐云seo&#xff0c;做乐云seo网站推广哪收录比较稳定&#xff0c;下面小编从以下几点详细介绍一下自适应网站方案品牌乐云seo&#xff1a;一、乐云seo做核心关键词首页排名技术怎么样&#xff1f;孔祥永seo做核心关键词到首页的秘诀就是做好原创内容&#x…

boost windows编译

执行&#xff1a; &#xff08;1&#xff09;bootstrap.bat &#xff08;2&#xff09;b2 -j4 toolsetmsvc-9.0 linkstatic threadingmulti runtime-linkstatic address-model64 stage --stagedir“D:\Code\boost_1_66_0\lib” debug release toolset:msvc-9.0 使用vs2008编…

必应输入法产品分析

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

这是我第一题AC的线段树

题目简述&#xff1a; 有N个整数&#xff0c;Q次操作&#xff0c;每次操作为询问一个区间[a, b]内数的和(0号操作)或者把一个区间内的数全部加上v(1号操作) 线段树求解即可。 #include <cstdio> #include <algorithm> using std::min; using std::max; #define L(n…

a频繁连接不上redis_连接不到redis Caused by:..._慕课问答

redis装在linux虚拟机上&#xff0c;在xshell上可以成功访问redis&#xff0c;配了密码拿了老师完整的代码作测试&#xff0c;就是访问失败&#xff0c;不知道哪里出了问题地址端口密码都没错的&#xff0c;求解org.springframework.data.redis.RedisConnectionFailureExceptio…

抓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…

计算几何_多边形

判定凸多边形&#xff1a;顶点凹凸性法 连续三个顶点p1,p2,p3。计算p1p2,p2p3的叉乘&#xff0c;阶乘大于0&#xff0c;则表示p3点在线段p1和p2的左侧&#xff0c;然后依次计算下一个前后所组成向量的阶乘&#xff0c;如果在计算时&#xff0c;出现负值&#xff0c;则此多边形是…

wps完成率怎么设置_WPS表格中如何计算完成率?详细操作方法看这里!

平时我们在使用像WPS这样的办公软件时&#xff0c;我们经常会使用到其中的Excel表格软件&#xff0c;来完成日常工作当中所需要完成的各种数据的统计以及录入等工作。而在我们使用WPS表格来录入、修改或者是统计某一些数据时&#xff0c;我们往往会因为表格内容的设定需求&…

[原创]WebScarab工具介绍

[原创]WebScarab工具介绍 一 WebScarab介绍 WebScarab是一个用来分析使用HTTP和HTTPS协议的应用程序框架。其原理很简单&#xff0c;WebScarab可以记录它检测到的会话内容&#xff08;请求和应答&#xff09;&#xff0c;并允许使用者可以通过多种形式来查看记录。WebScarab的设…

段表的作用

表格来自《程序员的自我修养 ——链接、装载与库》 ELF段名作用.text代码段&#xff0c;存放执行语句.data数据段&#xff0c;存放初始化的全局变量和局部静态变量.bss未初始化的全局变量和局部静态变量.rodata只读数据段.comment注释信息段.note.GNU-stack堆栈提示段.debug调…

layoutSubviews总结

ios layout机制相关方法 - (CGSize)sizeThatFits:(CGSize)size- (void)sizeToFit——————- - (void)layoutSubviews- (void)layoutIfNeeded- (void)setNeedsLayout——————– - (void)setNeedsDisplay- (void)drawRectlayoutSubviews在下面情况下会被调用&#xff1a; …

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

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