成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

基于wokerman實現(xiàn)即時消息推送

JellyBool / 1726人閱讀

摘要:目前網(wǎng)站有兩個用到實時消息推送的功能,源碼最新動態(tài),實時顯示用戶的操作行為消息推送,如重要消息通知,任務指派等等考慮的問題既然要實現(xiàn)即時,那就少不了。

目前網(wǎng)站有兩個用到實時消息推送的功能,源碼:https://github.com/wuzhc/team

最新動態(tài),實時顯示用戶的操作行為

消息推送,如重要消息通知,任務指派等等

考慮的問題

既然要實現(xiàn)即時,那就少不了socketio。因為項目是PHP寫的,所以服務端直接用phpsocket.io

我們應該保存離線消息,否則如果用戶不在線,那就接受不到消息。這里我用mongodb來存儲消息。

一是消息不需要關(guān)聯(lián)表,一條消息一個文檔

二是mongodb適合做海量數(shù)據(jù)存儲,并且分片也很簡單

三是消息不需要永久存儲,隨著時間推移,消息的價值性越低,可以用固定集合來存儲消息,當數(shù)據(jù)量達到一定值時覆蓋最久的消息

一個頁面連接一個socket,若用戶同時打開了多個頁面(即一個用戶會對應多個socketID),那么消息應該推送到用戶每一個打開的頁面。一個簡單的做法就是把用戶多個sockID加到group分組(socket.emit("group")),group分組名稱可以是"uid:1",表示userID為1的用戶,然后socket.broadcast.to("uid:1").emit("event_name", data);

就可以實現(xiàn)對多個頁面推送消息

實時動態(tài)

實時消息推送

客戶端
        // 初始化io對象
        var socket = io("http://" + document.domain + ":2120");
        // 當socket連接后發(fā)送登錄請求
        socket.on("connect", function () {
            socket.emit("login", {
                uid: "user->id?>",
                companyID: "user->identity->fdCompanyID?>"
            });
        });
        // 實時消息,當服務端推送來消息時觸發(fā)
        socket.on("new_msg", function (msg) {
            if (msg.typeID == "") {
                var html = "
  • " + "" + "
    " + "" + "
    " + "

    " + msg.title + "

    " + "

    " + msg.content + "

    " + "
    " + "
  • "; $("#msg-handle").prepend(html); var num = $(".msg-handle-num").eq(1).text() * 1; $(".msg-handle-num").text(num + 1); } }); // 實時動態(tài),當服務端推送來消息時觸發(fā) socket.on("update_dynamic", function (msg) { var html = "
  • " + "" + "User Image" + "
    " + "" + "" + msg.date + "" + "

    " + "" + msg.operator + "" + "" + msg.title + "" + "

    " + "
    " + msg.content + "
    " + "
    " + "
  • "; $("#dynamic-list").prepend(html); }); // 在線人數(shù) socket.on("update_online_count", function (msg) { $("#online-people").html(msg); });
    服務端
        /**
         * 消息推送
         */
        public function actionStart()
        {
            // PHPSocketIO服務
            $io = new SocketIO(2120);
            // 客戶端發(fā)起連接事件時,設置連接socket的各種事件回調(diào)
            $io->on("connection", function ($socket) use ($io) {
    
                /** @var Connection $redis */
                $redis = Yii::$app->redis;
    
                // 當客戶端發(fā)來登錄事件時觸發(fā)
                $socket->on("login", function ($data) use ($socket, $redis, $io) {
    
                    $uid = isset($data["uid"]) ? $data["uid"] : null;
                    $companyID = isset($data["companyID"]) ? $data["companyID"] : null;
    
                    // uid和companyID應該做下加密 by wuzhc 2018-01-28
                    if (empty($uid) || empty($companyID)) {
                        return ;
                    }
                    $this->companyID = $companyID;
    
                    // 已經(jīng)登錄過了
                    if (isset($socket->uid)) {
                        return;
                    }
    
                    $key = "socketio:company:" . $companyID;
                    $field = "uid:" . $uid;
    
                    // 用hash方便統(tǒng)計用戶打開頁面數(shù)量
                    if (!$redis->hget($key, $field)) {
                        $redis->hset($key, $field, 0);
                    }
    
                    // 同個用戶打開新頁面時加1
                    $redis->hincrby($key, $field, 1);
    
                    // 加入uid分組,方便對同個用戶的所有打開頁面推送消息
                    $socket->join("uid:". $uid);
    
                    // 加入companyID,方便對整個公司的所有用戶推送消息
                    $socket->join("company:".$companyID);
    
                    $socket->uid = $uid;
                    $socket->companyID = $companyID;
    
                    // 整個公司在線人數(shù)更新
                    $io->to("company:".$companyID)->emit("update_online_count", $redis->hlen($key));
                });
    
                // 當客戶端斷開連接是觸發(fā)(一般是關(guān)閉網(wǎng)頁或者跳轉(zhuǎn)刷新導致)
                $socket->on("disconnect", function () use ($socket, $redis, $io) {
                    if (!isset($socket->uid)) {
                        return;
                    }
    
                    $key = "socketio:company:" . $socket->companyID;
                    $field = "uid:" . $socket->uid;
                    $redis->hincrby($key, $field, -1);
    
                    if ($redis->hget($key, $field) <= 0) {
                        $redis->hdel($key, $field);
    
                        // 某某下線了,刷新整個公司的在線人數(shù)
                        $io->to("company:".$socket->companyID)->emit("update_online_count", $redis->hlen($key));
                    }
                });
            });
    
            // 開始進程時,監(jiān)聽2121端口,用戶數(shù)據(jù)推送
            $io->on("workerStart", function () use ($io) {
                /** @var Connection $redis */
                $redis = Yii::$app->redis;
    
                $httpWorker = new Worker("http://0.0.0.0:2121");
                // 當http客戶端發(fā)來數(shù)據(jù)時觸發(fā)
                $httpWorker->onMessage = function ($conn, $data) use ($io, $redis) {
                    $_POST = $_POST ? $_POST : $_GET;
                    switch (@$_POST["action"]) {
                        case "message":
                            $to = "uid:" . @$_POST["receiverID"];
                            $_POST["content"] = htmlspecialchars(@$_POST["content"]);
                            $_POST["title"] = htmlspecialchars(@$_POST["title"]);
    
                            // 有指定uid則向uid所在socket組發(fā)送數(shù)據(jù)
                            if ($to) {
                                $io->to($to)->emit("new_msg", $_POST);
                            }
    
                            $companyID = @$_POST["companyID"];
                            $key = "socketio:company:" . $companyID;
                            $field = "uid:" . $to;
    
                            // http接口返回,如果用戶離線socket返回fail
                            if ($to && $redis->hget($key, $field)) {
                                return $conn->send("offline");
                            } else {
                                return $conn->send("ok");
                            }
                            break;
                        case "dynamic":
                            $to = "company:" . @$_POST["companyID"];
                            $_POST["content"] = htmlspecialchars(@$_POST["content"]);
                            $_POST["title"] = htmlspecialchars(@$_POST["title"]);
    
                            // 有指定uid則向uid所在socket組發(fā)送數(shù)據(jù)
                            if ($to) {
                                $io->to($to)->emit("update_dynamic", $_POST);
                            }
    
                            $companyID = @$_POST["companyID"];
                            $key = "socketio:company:" . $companyID;
    
                            // http接口返回,如果用戶離線socket返回fail
                            if ($to && $redis->hlen($key)) {
                                return $conn->send("offline");
                            } else {
                                return $conn->send("ok");
                            }
                    }
    
                    return $conn->send("fail");
                };
    
                // 執(zhí)行監(jiān)聽
                $httpWorker->listen();
            });
    
            // 運行所有的實例
            global $argv;
            array_shift($argv);
    
            if (isset($argv[2]) && $argv[2] == "daemon") {
                $argv[2] = "-d";
            }
    
            Worker::runAll();
        }
    例子
        /**
         * 新建任務
         * @param $projectID
         * @param $categoryID
         * @return string
         * @throws ForbiddenHttpException
         * @since 2018-01-26
         */
        public function actionCreate($projectID, $categoryID)
        {
            $userID = Yii::$app->user->id;
    
            if (!Yii::$app->user->can("createTask")) {
                throw new ForbiddenHttpException(ResponseUtil::$msg[1]);
            }
    
            if ($data = Yii::$app->request->post()) {
                if (empty($data["name"])) {
                    ResponseUtil::jsonCORS(["status" => Conf::FAILED, "msg" => "任務標題不能為空"]);
                }
    
                // 保存任務
                $taskID = TaskService::factory()->save([
                    "name"       => $data["name"],
                    "creatorID"  => $userID,
                    "companyID"  => $this->companyID,
                    "level"      => $data["level"],
                    "categoryID" => $categoryID,
                    "projectID"  => $projectID,
                    "content"    => $data["content"]
                ]);
    
                if ($taskID) {
                    $url = Url::to(["task/view", "taskID" => $taskID]);
                    $portrait = UserService::factory()->getUserPortrait($userID);
                    $username = UserService::factory()->getUserName($userID);
                    $title = "創(chuàng)建了新任務";
                    $content = $data["name"];
    
                    // 保存操作日志
                    LogService::factory()->saveHandleLog([
                        "objectID"   => $taskID,
                        "companyID"  => $this->companyID,
                        "operatorID" => $userID,
                        "objectType" => Conf::OBJECT_TASK,
                        "content"    => $content,
                        "url"        => $url,
                        "title"      => $title,
                        "portrait"   => $portrait,
                        "operator"   => $username
                    ]);
    
                    // 動態(tài)推送
                    MsgService::factory()->push("dynamic", [
                        "companyID"  => $this->companyID,
                        "operatorID" => $userID,
                        "operator"   => $username,
                        "portrait"   => $portrait,
                        "title"      => $title,
                        "content"    => $content,
                        "url"        => $url,
                        "date"       => date("Y-m-d H:i:s"),
                    ]);
    
                    ResponseUtil::jsonCORS(null, Conf::SUCCESS, "創(chuàng)建成功");
                } else {
                    ResponseUtil::jsonCORS(null, Conf::FAILED, "創(chuàng)建失敗");
                }
            } else {
                return $this->render("create", [
                    "projectID" => $projectID,
                    "category"  => TaskCategory::findOne(["id" => $categoryID]),
                ]);
            }
        }
    調(diào)用保存操作日志底層接口
    LogService::factory()->saveHandleLog($args)
    參數(shù)說明
    參數(shù) 說明
    operatorID 操作者ID,用于用戶跳轉(zhuǎn)鏈接
    companyID 公司ID,用于該公司下的動態(tài)
    operator 操作者姓名
    portrait 操作者頭像
    receiver 接受者,可選
    title 動態(tài)標題,例如創(chuàng)建任務
    content 動態(tài)內(nèi)容,例如創(chuàng)建任務的名稱
    date 動態(tài)時間
    url 動態(tài)跳轉(zhuǎn)鏈接
    token 驗證
    objectType 對象類型,例如任務,文檔等等
    objectID 對象ID,例如任務ID,文檔ID等等

    companyID 用于查詢該公司下的動態(tài)

    objectType和objectID組合可以查詢某個對象的操作日志,例如一個任務被操作的日志

    保存消息
    參數(shù) 說明
    senderID 發(fā)送者ID,用于用戶跳轉(zhuǎn)鏈接
    companyID 公司ID,用于該公司下的動態(tài)
    sender 發(fā)送者姓名
    portrait 發(fā)送者頭像
    receiverID 接受者ID
    title 動態(tài)標題,例如創(chuàng)建任務
    content 動態(tài)內(nèi)容,例如創(chuàng)建任務的名稱
    date 動態(tài)時間
    url 動態(tài)跳轉(zhuǎn)鏈接
    typeID 消息類型,1系統(tǒng)通知,2管理員通知,3操作通知

    receiverID和typeID和isRead組合可以查詢用戶未讀消息

    請求接口
    MsgService::factory()->push($action, $args)
    即時動態(tài)
    參數(shù) 說明
    operatorID 操作者ID,用于用戶跳轉(zhuǎn)鏈接
    companyID 公司ID,用于給該公司的所有socket推送
    operator 操作者姓名
    portrait 操作者頭像
    receiver 接受者,可選
    title 動態(tài)標題,例如創(chuàng)建任務
    content 動態(tài)內(nèi)容,例如創(chuàng)建任務的名稱
    date 動態(tài)時間
    url 動態(tài)跳轉(zhuǎn)鏈接
    即時消息
    參數(shù) 說明
    senderID 發(fā)送者ID,用于用戶跳轉(zhuǎn)鏈接
    sender 發(fā)送者姓名
    receiverID 接受者ID,用于給接受者的所有socket推送消息
    typeID 消息類型,1系統(tǒng)通知,2管理員通知,3操作通知
    portrait 發(fā)送者頭像
    title 消息標題
    content 消息內(nèi)容
    url 消息跳轉(zhuǎn)鏈接
    完整代碼參考:https://github.com/wuzhc/team

    文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/28194.html

    相關(guān)文章

    • 打造你的Laravel即時應用(二)-消息推送與監(jiān)聽

      摘要:打造你的即時應用二消息推送與監(jiān)聽年月日接于上篇博客打造你的即時應用一項目初始化構(gòu)建在上一篇博客中介紹了項目的基本構(gòu)建現(xiàn)在進入實戰(zhàn)操作一消息推送創(chuàng)建事件類的廣播推送通過來實現(xiàn)下面通過命令來創(chuàng)建一個事件類為了配合我們的廣播系統(tǒng)使用需要實現(xiàn)接 打造你的Laravel即時應用(二)-消息推送與監(jiān)聽 2019年08月04日20:16:21 XXM 接于上篇博客: 打造你的Laravel即時應用(...

      omgdog 評論0 收藏0
    • 搭建IM服務 so easy

      摘要:現(xiàn)在很多網(wǎng)站都通過服務來實現(xiàn)消息推送及數(shù)據(jù)即時同步功能,即時通訊組件逐漸成為產(chǎn)品的標配。目前國內(nèi)有很多成熟穩(wěn)定的第三方即時通訊服務廠家,比如融云。 現(xiàn)在很多網(wǎng)站、APP都通過IM服務來實現(xiàn)消息推送及數(shù)據(jù)即時同步功能,即時通訊組件逐漸成為產(chǎn)品的標配。目前國內(nèi)有很多成熟穩(wěn)定的第三方即時通訊服務廠家,比如:融云。使用這些專業(yè)的服務可以提高開發(fā)效率而且服務穩(wěn)定有保障。 如果自己DIY或者需要在...

      imccl 評論0 收藏0

    發(fā)表評論

    0條評論

    閱讀需要支付1元查看
    <