依賴 laravel(predis)、 redis、 nodejs(ioredis,socket.io)
1、修改config\app.php
providers數組 添加 'Illuminate\Broadcasting\BroadcastServiceProvider',
2、修改廣播驅動方式爲 config\broadcasting.php
'default' => env('BROADCAST_DRIVER', 'redis'), 改爲redis驅動
使用redis作爲php和js的通信方式。
3、配置config\database.php
配置redis服務連接參數
定義一個被廣播的事件
<?phpnamespace App\Events;use App\Events\Event;use Illuminate\Queue\SerializesModels;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Support\Facades\Session;class MessageBroadcastEvent extends Event implements ShouldBroadcast{ use SerializesModels; public $users; public $message = array(); protected $channel; /** * Create a new event instance. * * @return void */ public function __construct($users, $message, $channel) { $this->users = $users; $this->message = array( 'id' => $message['id'], 'title' => $message['title'], 'content' => $message['content'], 'url' => $message['url'], 'time' => date('m-d H:i:s', strtotime($message['created_at'])) ); $this->channel = $channel; } /** * Get the channels the event should be broadcast on. * 廣播到哪個頻道 * @return array */ public function broadcastOn() { return [$this->channel]; } }
默認情況下,Event中的所有public屬性都會被序列化後廣播。上面的例子中就是$users, $message 兩個屬性。也可以
使用broadcastWith這個方法,明確的指出要廣播什麼數據。例如:
public function broadcastWith(){ return ['message ' => $this->message ]; }
Redis和Websocket服務器
依賴的就是redis的sub/pub功能
啓動一個node websocket服務器來和client通信,我們使用socket.io
node 服務端代碼 保存爲 index.js 放在node服務目錄
var app = require('http').createServer(handler);var io = require('socket.io')(app);var Redis = require('ioredis');var redis = new Redis('6379', '192.168.10.10'); //連接redis服務器//監聽客戶端端口 這裏是 6001app.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); });
客戶端代碼,只要客戶端需要被廣播的頁面正確引用 node socket.io 模塊客戶端js文件(自行將這個客戶端模塊文件放到項目public目錄)
<script src="js/socket.io/node_modules/socket.io-client/socket.io.js"></script>//客戶端也使用socket.io,測試代碼:控制檯打印輸出 //連接socket服務器 var socket = io('http://localhost:6001'); socket.on('connection', function (data) { console.log(data); }); //收聽的頻道 socket.on('channel-{{ Session::get('shop')->id }}:App\\Events\\MessageBroadcastEvent', function(data) { //控制檯輸出廣播消息 console.log(message); //這裏可以根據收到的消息,做一些改變頁面結構的工作…… }); //可以收聽多個頻道 socket.on('channel-system:App\\Events\\MessageBroadcastEvent', function(data){ console.log(data); //這裏可以根據收到的消息,做一些改變頁面結構的工作…… }); //控制檯輸出連接信息 console.log(socket);
項目中觸發事件
在控制器或者在路由匿名函數中都可以直接調用廣播事件
1、控制器中直接調用
//發送給哪些用戶 id 。 這裏定義消息接收用戶,是在前臺用於檢測登陸用戶是否在這個數組中,存在則做出相應的即時提醒。//注意:其實廣播消息都會被髮送到對應的頻道的。$users = array(1, 2);//這裏可以保存發送消息到 messages 表$message = new Message(); $message->title = '您的店鋪有一條新銷售單'; $message->content = '您的店鋪有一條新銷售單,單號1000000'; $message->message_type_id = 1; $message->status = 0; $message->url = 'http://www.xxx.com'; $message->save();//保存發送用戶 到 user_message 表$userMessage = array(); $time = date("Y-m-d H:i:s");foreach ($users as $user) { $tmp = array( 'created_at' =>$time, 'updated_at' =>$time, 'user_id' =>$user, 'message_id' =>$message->id, 'read' =>0 ); $userMessage[] = $tmp; } UserMessage::insert($userMessage);//廣播的頻道//我們以店鋪id來標識頻道,這樣前端用戶頁面也根據店鋪id標識來收聽自己店鋪頻道,就能做到店鋪廣播消息消息只能廣播到本店鋪用戶$channel = 'channel-' . Session::get('shop')->id; //$channel = 'channel-system'; //其他頻道//$response = event(new MessageBroadcastEvent($users, $message, $channel));Event::fire(new MessageBroadcastEvent($users, $message, $channel)); //這兩種方式都可以觸發事件
2、路由中直接調用
//示例代碼Route::get('/event', function(){ Event::fire(new \App\Events\SomeEvent(3)); return "hello world"; });
使用:
必須開啓 node websocket 服務端。我在本機windows下C盤安裝node,服務端代碼就放在這個目錄下,進入cmd
終端,執行命令,node index.js 啓動服務端。
打開包含有 socket.io 代碼的客戶端頁面,等待被廣播
觸發我們的後臺廣播事件(執行相應的控制器代碼)