隨筆---laravel廣播事件
目標效果:像QQ郵箱那樣,在收到伺服器通知的時候在頁面上主動彈出提示框。
在很多場景中,我們不能等待伺服器做出響應,例如有些業務是需要人工進行處理的,可能耗時幾小時才有結果,但是我們又需要在有結果的時候及時收到回饋,那麼這裡有幾個選擇:1.手動重新整理2.前端迴圈ajax請求3.websocket。第一個不用說了吧,不太可能考慮,第二個的話伺服器壓力會很大,因為兩小時內一個瀏覽器就能發出成百上千次請求,使用者多的情況下處理請求量太大,所以這裡採用websocket的方法,我不知道qq郵箱是否也是用這種方法,但是在中小型網站中應用這種方法能做到差不多的效果。當然,廣播事件傳輸的僅僅是資料而已,你可以編寫前端js,在接收到伺服器資料的時候將資料包裝成任意需要的樣式。
參考資料:
事前準備: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