目标效果:像QQ邮箱那样,在收到服务器通知的时候在页面上主动弹出提示框。
在很多场景中,我们不能等待服务器做出响应,例如有些业务是需要人工进行处理的,可能耗时几小时才有结果,但是我们又需要在有结果的时候及时收到回馈,那么这里有几个选择:1.手动刷新2.前端循环ajax请求3.websocket。第一个不用说了吧,不太可能考虑,第二个的话服务器压力会很大,因为两小时内一个浏览器就能发出成百上千次请求,用户多的情况下处理请求量太大,所以这里采用websocket的方法,我不知道qq邮箱是否也是用这种方法,但是在中小型网站中应用这种方法能做到差不多的效果。当然,广播事件传输的仅仅是数据而已,你可以编写前端js,在接收到服务器数据的时候将数据包装成任意需要的样式。
参考资料:https://segmentfault.com/a/1190000002921506
事前准备:redis、node、wamp/lamp环境、一个能用的laravel项目
—————————————————————————————————————————————————————
一、配置
config/broadcasting.php中,如下配置’default’ => env(‘BROADCAST_DRIVER’,’redis’),使用redis作为php和js的通信方式。 config/database.php中配置redis的连接。
——————————————————————————————————————————————
二、定义一个广播事件
根据Laravel文档的说明,想让事件被广播,必须让Event类实现一个Illuminate\Contracts\Broadcasting\ShouldBroadcast接口,并且实现一个方法broadcastOn。broadcastOn返回一个数组,包含了事件发送到的channel(频道)。如下:
<?php
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class SomeEvent extends Event implements ShouldBroadcast
{
use SerializesModels;
public $user_id;
public $m1;
public $m2;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($user_id,$m1,$m2)
{
$this->user_id = $user_id;
$this->m1 = $m1;
$this->m2 = $m2;
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return ['test-channel:'.$this->user_id];
}
}
tips:默认情况下,Event中的所有public属性都会被序列化后广播。上面的例子中就是$user_id这个属性。你也可以使用broadcastWith这个方法,明确的指出要广播什么数据。例如:
public function broadcastWith() {
return ['user_id' => $this->user_id];
}
——————————————————————————————————————————————
三、准备好前端view
这里我把我的前端view代码写出来,这里要引用socket.io.js,算是一个引用库?可以自己百度下载一个,我也忘了自己是哪里弄来的了。我后续贴个度盘的链接把整个打包放上去好了。前端模板用的是bootstrap的toastr把数据输出,当然你可以用其他的前端样式,反正用你需要的方法和样式把数据输出就可以了
<html>
<head>
</head>
<body>
<link href="{{ asset('css/plugins/toastr/toastr.min.css')}}" rel="stylesheet">
<!-- Mainly scripts -->
<script src="{{ asset('js/jquery-2.1.1.js')}}"></script>
<script src="{{ asset('js/plugins/toastr/toastr.min.js')}}"></script>
<script src="{{ asset('js/socket.io-client/dist/socket.io.js')}}"></script>
<script>
//toastr 配置
toastr.options = {
"closeButton": true,
"debug": false,
"progressBar": true,
"preventDuplicates": false,
"positionClass": "toast-top-right",
"onclick": null,
"showDuration": "400",
"hideDuration": "1000",
"timeOut": "7000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
var mapping = [];
mapping['user_id'] = '用户id';
mapping['m1'] = '消息1';
mapping['m2'] = '消息2';
var socket = io('http://localhost:6001');
socket.on('connection', function (data) {
console.log(data);
});
socket.on('test-channel:3:App\\Events\\SomeEvent', function(message){
console.log(message);
var log = '';
for(var i in message){
log += mapping[i]+':'+message[i]+'<br>';
}
toastr.success(log,'您有一条新通知!');
});
console.log(socket);
</script>
</body>
</html>
——————————————————————————————————————————————
四、模拟触发事件
因为是服务器主动给客户端发指令,那么这里就需要在你的业务流程里面判断什么时候触发信息出去了
Event::fire(new \App\Events\SomeEvent(3,'qwe','asd'));
只要你处理完事务后,需要广播,把这句话加在代码里面就可以了
————————————————————————————————————————————————
五、启动Redis和websocket服务器
1.需要启动一个Redis,事件广播主要依赖的就是redis的sub/pub功能,具体可以看redis文档
~这里开的是redis的redis-server.exe,不需要其他的操作,打开就行了(不能关掉这个窗口)
2.需要启动一个websocket服务器来和client通信,建议使用socket.io,代码如下:
~这里是用node打开下面的文件(我命名是test.js),在命令行里面cd进入你放这个test.js的位置,然后执行 node test.js 启动它就行了(不能关掉这个窗口)
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
var Redis = require('ioredis');
var redis = new Redis('6379', '127.0.0.1');
app.listen(6001, function() {
console.log('Server is running!');
});
function handler(req, res) {
res.writeHead(200);
res.end('');
}
io.on('connection', function(socket) {
console.log('connected');
});
redis.psubscribe('*', function(err, count) {
console.log(count);
});
redis.on('pmessage', function(subscribed, channel, message) {
console.log(subscribed);
console.log(channel);
console.log(message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
3.好像还得打开队列(看了上面资料博客的回复,好像是看你 队列配置是不是sync也就是同步模拟,我也不太清楚,反正这里可能也需要用到队列,我这里是数据库队列,如果你发现触发了event但是前端没收到信息的话,把队列打开试试看),进入你的laravel项目,然后在命令行输入:
php artisan queue:listen
(不能关掉这个窗口)
————————————————————————————————
前端的那个socket.io库,放到public相应的位置就能用了
http://pan.baidu.com/s/1kVIBHI3