使用php+swoole对client数据实时更新(二) (转)
上一篇提到了swoole的基本使用,现在通过几行基本的语句来实现比较复杂的逻辑操作:
先说一下业务场景。我们目前的大多数应用都是以服务端+接口+客户端的方式去协调工作的,这样的好处在于不论是处在何种终端的情况下,都可以完美的和服务端兼容。这样就轻松实现了MVC各个部分的真正解耦。但是提高程序的友好性还是有很多路要走,其中一个大家都会遇到的就是数据实时更新的问题。比如一个用户在手机上做了添加操作,这时候其他的终端也应该及时显示数据的变化情况。这个对于手机来说还算好办,因为现在的各种推送服务完全可以满足需求,当收到推送更新时,根据推送内容请求相应接口就可以了。但是放到PC上就不是这么回事了。浏览器和http协议的特殊性质不得不让我们另辟蹊径。
举一个大家生活中都会遇到的场景:
某个周末你想要和女朋友去看一场电影,你在自己的pc上找到了某场的场次和座位。正当你要下单支付时,系统提示该座位已经售出,这时你不得不重新回到选座页面重新挑选。那如果改进一下产品体验,当有别的用户已经购买某个座位的时候,浏览器会及时将座位标识已售出,这样你就不用来回操作,节省操作时间。
** 针对上述的情景呢,这里有一个系统间交互的流程图:**

- 上面一行就是使用系统的当前用户,他对数据进行了相关操作,同时返回操作结果
下面一行是其他用户也正在操作同一条数据。当①(绿色)返回结果时,web服务器会同时告诉当前用户和redis队列服务(①黄色),因为websocket服务实时监听redis,这样一旦有数据变化,websocket服务会及时感知。此时通过与浏览器建立的长连接进行通讯并告知数据已经更新并重新加载。
另外一种方式是当①(绿色)返回结果时不告诉redis队列,而是直接通讯websocket服务数据已经发生变化,再由websocket服务通知浏览器客户端重新加载。因为此种方案比较简单再加上swoole对一些操作的封装比较便利,这里就采用此种办法
- 当websocket连接被打开时,向socket服务发送当前注册id,因为只有这样websocket服务才能定向的为指定连接发送数据
ws.onopen = function(){
console.log("socket连接已打开");
$.post('/mercha/merchant/find',function(d){ //从web服务端获取注册id
d = $.parseJSON(d);
ws.send("merchantId_"+d.data.id);
})
};
- 当websocket服务收到注册id时会将当前连接的id和由服务端传来的商户id对应关系写入redis
$server->on('Message', function ($serv, $frame) {
if(stripos($frame->data,'merchantId_') !== false){
$fd = $redis->get($frame->data);
if($fd != null){
$fd = json_decode($fd,true);
}
//这里的$frame->fd是指当前的websocket连接id,swoole会通过此id发送给对应的接收方,可以理解为手机号码
$fd[$frame->fd] = $frame->fd;
$fd = json_encode($fd);
$redis->set($frame->data,$fd);
}
});
- 因为swoole_websocket_server 继承自 swoole_http_server ,这样就可以通过http的方式和websocket服务进行交互
$server->on('Request', function ($req, $respone) {
if(isset($req->get['merchantId'])){
global $server;
global $redis;
$merchantId = $req->get['merchantId'];
$fd = $redis->get("merchantId_".$merchantId);
if($fd != null){
$fd = json_decode($fd,true);
}
$err = 0;
if(is_array($fd)){
foreach($fd as $f){
$res = @$server->push($f, "refresh");
if($res === false){
unset($fd[$f]);
$err++;
}
}
if($err){
$fd = json_encode($fd);
$redis->set("merchantId_".$merchantId,$fd);
}
}
}
$respone->end("success");
});
** 需要强调的一点是监听http请求的server并不具备push方法,所以这里通过全局变量的方式使用websocket的$server来向客户端发送数据 **
以上就是解决问题的大概思路了,文章最后会附上websocket的服务端源码,因为业务稍多所以里面集成了很多业务代码,而且需要运行起来必须要先安装swoole的扩展,安装方式上篇文章会有说明,所以这里仅供参考
对于图中另一种使用redis的方式也是很好的,之前采用了redis发布订阅的模式基本可以达到想要的效果。但是中间遇到一个问题redis的subscribe运行几十秒后,就会抛出一个RedisException。原因处在于,php的redis库使用的subscribe是使用PHP内置的socket,而php.ini默认是设置了socket的超时时间是60秒,所以大家只要找到default_socket_timeout 这个配置项,把时间改长点就可以了。或者在代码中加入ini_set('default_socket_timeout', -1);
http://www.cnblogs.com/JasonLeemz/p/5116814.html
使用php+swoole对client数据实时更新(二) (转)的更多相关文章
- 使用php+swoole对client数据实时更新(上)
如果想对一个列表做实时的更新,传统的做法是采用轮询的方式.以web为例,通过Ajax定时请求服务端然后获取数据显示在页面.这种方式实现简单,缺点就是浪费资源. HTTP1.1新增加了对websocke ...
- 使用php+swoole对client数据实时更新(下)
上一篇提到了swoole的基本使用,现在通过几行基本的语句来实现比较复杂的逻辑操作: 先说一下业务场景.我们目前的大多数应用都是以服务端+接口+客户端的方式去协调工作的,这样的好处在于不论是处在何种终 ...
- MySQL 实现将一个库表里面的数据实时更新到另一个库表里面
MySQL 实现将一个库表里面的数据实时更新到另一个库表里面 需求描述:MySQL 里面有很多的数据库,这些数据库里面都有同一种表结构的表 (tb_warn_log),这张表的数据是实时更新的,现在需 ...
- Web网站数据”实时”更新设计
请注意这个实时打上了双引号,没有绝对的实时,只是时间的颗粒不一样罢了(1ms,1s,1m). 服务器数据有更新可以快速通知客户端.Web 基于取得模式,而服务器建立大量的和客户端连接来提供数据实时更新 ...
- 基于server broker 的数据实时更新
Service Broker介绍:SQL Server Service Broker 为消息和队列应用程序提供 SQL Server 数据库引擎本机支持.这使开发人员可以轻松地创建使用数据库引擎组件在 ...
- vue 跳转并传参,实现数据实时更新
原文链接:点我 比如我现在在页面A跳转到页面B,A中的router-link :to={path:’B’,params:{id:’5’}} 求助:在页面B中的mounted生命周期函数中使用this. ...
- MFC编辑框数据实时更新问题
在VC里,很多情况下需要更新控件,也就是调用UpdateData(FALSE);但是如果是在循环中调用该函数,会导致没有时间来刷新界面,消息得不到相应,从外部看来,似乎整个循环只执行了一次Update ...
- IDEA 修改JSP和后端数据后,页面刷新可以实时更新
情况:刚开始使用IDEA进行开发时,发现修改JSP页面或者后端数据后,再刷新浏览器页面,发现没有变化,页面无更新. 这样就导致不得不频繁重启tomcat服务器.非常麻烦 解决方法: 步骤1. 先设置t ...
- Tornado实现监控数据实时展示
前言: It has been a while since I last updated my blogs. 使用Tornado开发一个实时监控信息系统,其中包括 CUP.内存.网卡.磁盘使用率. 涉 ...
随机推荐
- HDU 4380 Farmer Greedy 计算几何+bitset
枚举直线,对于直线的某个点在直线的左端还是右端,能够状压出一个数.用bitset记录. 然后三角形就是3个bitset&一下 #include <cstdio> #include ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block 代理对象(Proxy Object) ...
- C#操作注册表--DarrenF
步骤/方法 读取指定名称的注册表的值 private string GetRegistData(string name) { string registData; RegistryKey hkml = ...
- C#新DataColumn类Type生成的方法类型参数
DataColumn有的需要等级Type构造类型的参数,如以下: // // 摘要: // 使用指定列名称和数据类型初始化 System.Data.DataColumn 类的新实例. // // 參数 ...
- w7关闭休眠增加C盘容量
http://jingyan.baidu.com/article/f3ad7d0fc0992e09c2345b51.html powercfg -h off,即可关闭休眠功能 powercfg -h ...
- 将ACCESS数据库迁移到SQLSERVER数据库
原文:将ACCESS数据库迁移到SQLSERVER数据库 将ACCESS数据库迁移到SQLSERVER数据库 ACCESS2000文件 用ACCESS2007打开,并迁移到SQLSERVER2005里 ...
- Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils
1.错误叙述性说明 2014-7-10 23:12:23 org.apache.catalina.core.StandardContext filterStart 严重: Exception star ...
- wireshark教程
Wireshark世界上最流行的网络分析工具. 这个强大的工具能够捕捉网络中的数据,并为用户提供关于网络和上层协议的各种信息.与非常多其它网络工具一样.Wireshark也使用pcap network ...
- 查看文章strncpy()功能更好的文章
strncpy()功能 原型:extern char *strncpy(char *dest, char *src, int n); 使用方法:#include <string.h> ...
- SQL Server提高并发查询效率
同事写了个程序用创建多个线程使用ado同时对同个数据库进行相同的查询,涉及2张数据表的联查.当线程数非常多的情况下,读取数据的效率就会变得很慢,例如50个线程同时查询大概3000条数据,查询完成后通过 ...