PHP 简易聊天室 利用redis的订阅发布功能

时间:2021-03-29 10:07:03

demo:
http://www.200ok.fun:8083/api/chat/list

前言:
这个种方式太耗redis连接数,每次订阅都会新起一个进程,仅供练手使用,切勿用于生产环境。

原理:
1.PHP提供两个接口,订阅,发布,redis就有提供
2.订阅接口会卡住,不会马上response,直至有发布的消息
3.前端需要在一次订阅请求成功或失败后立即重新发一个订阅请求,以免错漏信息

后台代码(用的laravel框架,只要能调用redis,实现一致即可):

// 订阅接口
public
function subscribe(Request $request) { Redis::subscribe([self::CHATROOMCHANNEL], function ($message) { echo json_encode($this->rtnSucc($message)); exit; }); }
// 发布接口
public function publish(Request $request) { $num = Redis::publish(self::CHATROOMCHANNEL, $request->input('say', '对方没有说话')); if ($num) { return $this->rtnSucc($num); } else { return $this->rtnErr(100, "发送失败"); } } private function rtnSucc($data) { return ["rtn" => 0, "msg" => "", "data" => $data]; } private function rtnErr($rtn, $msg = "") { return ["rtn" => $rtn, "msg" => $msg, "data" => ""]; }

 

前端代码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>200OK ChatRoom</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #input_box {
            width: 92%;
            height: 100px;
            margin: 0 auto;
            display: block;
            border-radius: 2px;
        }

        #btn {
            height: 30px;
            text-align: center;
            font-size: 16px;
            background-color: #E91E63;
            color: #fff;
            margin: 10px 10px 0 10px;
            border-radius: 2px;
            line-height: 30px;
        }

        #content {
            margin: 0 10px;
            font-size: 16px;
            color: #795548;
        }
    </style>
</head>
<body>

<div id="content">
    <p>200 OK ChatRoom!</p>
</div>
<textarea id="input_box" placeholder="输入你想说的话"></textarea>
<div id="btn">发射!</div>
<script src="/js/zepto.js"></script>
<script src="/js/md5.min.js"></script>
<script>
    // dom
    let $input_box = $("#input_box");
    let $content = $("#content");
    let $btn = $("#btn");
    let myName = getRandomName();

    main();

    // 入口
    function main() {
        bindEvent();
        subscribe();
        $content.append(pp("你叫:" + myName));
    }

    // 监听事件
    function bindEvent() {
        $btn.on("click", function () {
            say();
        });

        $input_box.bind('keyup', function (e) {
            if (13 === e.keyCode) {
                say();
            }
        });
    }

    //
    function say() {
        var str = getC().trim();
        if (!str) {
            error("请输入内容");
            return;
        }
        publish(namePrefix() + str, function (rp) {
            $input_box.val("");
            $input_box.focus();
        });
    }

    // 获取输入框内容
    function getC() {
        return $input_box.val();
    }

    // 订阅 回调之后继续订阅
    function subscribe(callback) {
        $.ajax({
            type: 'GET',
            url: '/api/chat/subscribe',
            data: {},
            dataType: 'json',
            timeout: 10000,
            success: function (data) {
                if (0 === data.rtn) {
                    $content.append(pp(data.data));
                }
                subscribe();
            },
            error: function (xhr, type) {
                // 防止雪崩
                setTimeout(function () {
                    subscribe();
                }, 1000);
            }
        });
    }

    // 发布
    function publish(str, callback) {
        $.get('/api/chat/publish?say=' + str, function (response) {
            callback(response);
        });
    }

    function pp(str) {
        return "<p>" + str + "</p>"
    }

    function namePrefix() {
        return myName + ": ";
    }

    // 错误处理
    function error(str) {
        alert(str);
    }

    // 生成随机姓名
    function getRandomName() {
        var familyNames = new Array(
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", "",
            "", "", "", "", "", "", "", "", "", ""
        );
        var givenNames = new Array(
            "子璇", "", "国栋", "夫子", "瑞堂", "", "", "", "国贤", "贺祥", "晨涛",
            "昊轩", "易轩", "益辰", "益帆", "益冉", "瑾春", "瑾昆", "春齐", "", "文昊",
            "东东", "雄霖", "浩晨", "熙涵", "溶溶", "冰枫", "欣欣", "宜豪", "欣慧", "建政",
            "美欣", "淑慧", "文轩", "文杰", "欣源", "忠林", "榕润", "欣汝", "慧嘉", "新建",
            "建林", "亦菲", "", "冰洁", "佳欣", "涵涵", "禹辰", "淳美", "泽惠", "伟洋",
            "涵越", "润丽", "", "淑华", "晶莹", "凌晶", "苒溪", "雨涵", "嘉怡", "佳毅",
            "子辰", "佳琪", "紫轩", "瑞辰", "昕蕊", "", "明远", "欣宜", "泽远", "欣怡",
            "佳怡", "佳惠", "晨茜", "晨璐", "运昊", "汝鑫", "淑君", "晶滢", "润莎", "榕汕",
            "佳钰", "佳玉", "晓庆", "一鸣", "语晨", "添池", "添昊", "雨泽", "雅晗", "雅涵",
            "清妍", "诗悦", "嘉乐", "晨涵", "天赫", "玥傲", "佳昊", "天昊", "萌萌", "若萌"
        );

        var i = parseInt(10 * Math.random()) * 10 + parseInt(10 * Math.random());
        var familyName = familyNames[i];

        var j = parseInt(10 * Math.random()) * 10 + parseInt(10 * Math.random());
        var givenName = givenNames[i];

        return familyName + givenName;
    }
</script>
</body>
</html>