Swoole WebSocket 實現mysql實時資料展示
阿新 • • 發佈:2018-11-07
最近在學習swoole,自己完成下論壇裡留下的作業,通過swoole_websocket實時展示mysql資料,有個遺留問題,如何判斷mysql是否有增刪改,我想到的方法有:
1、在應用層中有增刪改時,發一個訊息到訊息佇列來監聽。
2、解析mysql bin-log日誌。
3、觸發器。
現在的解決辦法是利用觸發器,例子中我只監聽了一個表,當有多個表時,需要新增大量觸發器,觸發器本身佔用很多資源,太多觸發器也不好管理,之後會再想辦法解析下bin-log日誌來監聽Mysql資料變化。
服務端程式碼
<?php /** * Created by PhpStorm. * User: qyc * Date: 2017/10/22 * Time: 下午4:40 */ define('HOST', '0.0.0.0'); define('PORT', '9502'); define('WORKER_NUM', 8); define('DISPATCH_MODE', 2); define('DAEMONIZE', 0); class swoole_ws { private $pdo; private $server; //這裡寫死了資料表,之後完成資料庫監聽後改寫這裡邏輯 private $table = [ 'swoole_test', ]; public function __construct() { $this->server = new swoole_websocket_server(HOST, PORT); $this->server->set( [ //開啟woker程序數 'worker_num' => WORKER_NUM, //請求分發策略, 'dispatch_mode' => DISPATCH_MODE, //是否守護程序 'daemonize' => DAEMONIZE, ] ); $this->server->on('message', [$this, 'onMessage']); $this->server->on('open', [$this, 'onOpen']); $this->server->on('workerStart', [$this, 'onWorkerStart']); $this->server->start(); } //woker程序 public function onWorkerStart(swoole_websocket_server $server, $worker_id) { if ($worker_id == 0) { // 0 worker程序開啟一個定時器去監聽mysql資料變化 $this->server->tick(500, [$this, 'onTick']); } //每個worker程序維持一個pdo連線 $this->pdo = new PDO("mysql:host=localhost;dbname=swoole_ws", "root", "root"); } //接受客戶端資料 public function onMessage(swoole_websocket_server $server, swoole_websocket_frame $frame) { // echo $frame->data; //客戶端傳送的資料 // echo $server->worker_id; } //客戶端 服務端建立連線並完成握手後的回撥 public function onOpen(swoole_websocket_server $server, swoole_http_request $request) { //第一次連線去獲取一下Mysql資料 $this->update(); } private function update() { $res = []; foreach ($this->table as $table) { $res[$table] = $this->getTableData($table); } foreach ($this->server->connections as $connection) { //向所有客戶端傳送資料 $this->server->push($connection, json_encode($res)); } } //獲取表資料 private function getTableData($table) { $sql = 'select * from ' . $table; try { $stmt = $this->pdo->prepare($sql); $stmt->execute(); $res = $stmt->fetchAll(PDO::FETCH_ASSOC); return $res ?: []; } catch (Exception $e) { return []; } } //定時器回撥 public function onTick() { /* * is_update表記錄表是否更新過 * swoole_test表添加了3個觸發器, after insert、update、delete, * 會向is_update表更新swoole_test是否有更新操作 */ try { $sql = 'select is_update from is_update where table_name = "swoole_test"'; $stmt = $this->pdo->prepare($sql); $stmt->execute(); $res = $stmt->fetchAll(PDO::FETCH_ASSOC); if ( ! $res) { return []; } if ($res[0]['is_update'] == 1) { //當is_update欄位為1時表明資料有更新,向客戶端推送訊息 $this->update(); //更新下表更新欄位 $update = 'update is_update set is_update=0 where table_name = "swoole_test"'; $stmt = $this->pdo->prepare($update); $stmt->execute(); } } catch (Exception $e) { $this->pdo = new PDO("mysql:host=localhost;dbname=swoole_ws", "root", "root"); } } } new swoole_ws();
客戶端程式碼
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Mysql實時資料</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!--[if lt IE 9]> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> </head> <body> <div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">swoole_test</h3> </div> <div class="panel-body"> <table class="table table-bordered table-hover"> <thead id="thead"> </thead> <tbody id="tbody"> </tbody> </table> </div> </div> </div> <script> var websocket = new WebSocket('ws://127.0.0.1:9502'); websocket.onopen = function (event) { console.log('websocket connect'); websocket.send('hello swoole'); }; websocket.onclose = function (event) { console.log('websocket close'); }; websocket.onerror = function (event, e) { console.log('error occured:' + event.data); }; websocket.onmessage = function (event) { data = JSON.parse(event.data)['swoole_test']; var th = '<tr>'; var width = 1 / json_length(data[0]) * 100; for (var key in data[0]) { th += "<th style='width:" + width + "%'>" + key + "</th>"; } th += '</tr>'; $("#thead").html(th); var tbody = ''; for (var line in data) { tbody += '<tr>'; var td = ''; for (var column in data[line]) { td += "<td>" + data[line][column] + "</td>"; } tbody += td + '</tr>'; } $("#tbody").html(tbody); }; function json_length(json) { var length = 0; for (var item in json) { length++; } return length; } </script> </body> </html>
觸發器
DELIMITER $$ /*更新觸發器*/ DROP TRIGGER IF EXISTS swoole_test_is_update; $$ CREATE TRIGGER swoole_test_is_update AFTER UPDATE ON swoole_test FOR EACH ROW BEGIN UPDATE `is_update` SET is_update = 1 WHERE TABLE_NAME = 'swoole_test'; END; $$ /*新增觸發器*/ DROP TRIGGER IF EXISTS swoole_test_is_insert; $$ CREATE TRIGGER swoole_test_is_insert AFTER UPDATE ON swoole_test FOR EACH ROW BEGIN UPDATE `is_update` SET is_update = 1 WHERE TABLE_NAME = 'swoole_test'; END; $$ /*刪除觸發器*/ DROP TRIGGER IF EXISTS swoole_test_is_delete; $$ CREATE TRIGGER swoole_test_is_delete AFTER UPDATE ON swoole_test FOR EACH ROW BEGIN UPDATE `is_update` SET is_update = 1 WHERE TABLE_NAME = 'swoole_test'; END; $$ DELIMITER ;
開啟websocker服務端
php swoole_ws;
執行效果
現在訪問前端頁面,對資料庫進行增刪改操作測試效果。
1.png
結語
這只是個swoole_websocket練習,熟悉一下,大家可以看看Swoole官方文件進行學習,會有不錯的提升,之後會做個即時聊天工具玩玩,Just code for fun ~
參考文獻: