php与WebSocket结合实时输出日志

流氓凡 技术分享 2021-04-12 368 0

设计思路

后端:通过workerman开启socket,监控会话,发现会话以后,读取当前日志的文件大小并把指针移到文件最后面,开始监控文件变化。

每秒钟刷新20次 然后跳出,有变化返回给前端。(这里如果不跳出会造成进程阻塞,不要在会话时写死循环!!!)

前端:每秒钟一次的心跳策略,获取信息。

PHP实现

通过composer方式安装workman

{
    "require": {
        "walkor/workerman": "^3.5"
    }
}
<?php
require_once './vendor/autoload.php';

use Workerman\Worker;

define("MAX_SHOW", 8192);
$ws_worker = new Worker("websocket://0.0.0.0:8936");

// 启动4个进程对外提供服务
$ws_worker->count = 4;

$ws_worker->onMessage = function ($connection, $data) {
    if ($data == "tom") $connection->send("连接成功。");
    // 定义要查看的log文件路径
    $file_name = "/www/wwwlogs/access.log";
    $connection->send("日志文件:" . $file_name);
    $file_size = filesize($file_name);
    $file_size_new = 0;
    $add_size = 0;
    $ignore_size = 0;
    $fp = fopen($file_name, "r");
    fseek($fp, $file_size);
    $num = 0;
    while (1) {
        $num++;
        clearstatcache();
        $file_size_new = filesize($file_name);
        $add_size = $file_size_new - $file_size;
        if ($add_size > 0) {
            if ($add_size > MAX_SHOW) {
                $ignore_size = $add_size - MAX_SHOW;
                $add_size = MAX_SHOW;
                fseek($fp, $file_size + $ignore_size);
            }
            $connection->send(fread($fp, $add_size));
            $file_size = $file_size_new;
        }
        usleep(50000);
        if ($num > 20) break;
    }
    fclose($fp);
};


// 运行worker
Worker::runAll();

JS代码

socket  WebSocket

<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Creative - Bootstrap 3 Responsive Admin Template">
    <meta name="author" content="GeeksLabs">
    <meta name="keyword" content="Creative, Dashboard, Admin, Template, Theme, Bootstrap, Responsive, Retina, Minimal">
    <title>Show Log</title>

</head>

<body style="background-color:oldlace">
<p>等待任务..</p>
<input value='' id='v' type="text" style="display:none;">
<!--<button type="button" onclick=a() >Click Me!</button>-->
<br/>
<div>
    <textarea readonly id="txt"
              style="width: 100%; height: 635px;background: black none repeat scroll 0% 0%; color: rgb(153, 153, 153);"></textarea>
</div>
</body>
</html>

<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
<script>
    ws = new WebSocket("ws://xxx.xxx.xxx:8936"); //ip地址或者访问域名
    ws.onopen = function () {
        ws.send('tom');
    };
    ws.onmessage = function (e) {
        oTest = document.getElementById("txt");

        oTest.innerHTML = oTest.innerHTML + e.data;
        oTest.scrollTop = oTest.scrollHeight; // 此处实现tail -f 日志展示效果
    };
    window.setInterval(a, 1000); // 心跳
    function a() {
        r = document.getElementById("v");
        ws.send(v.value);
    }
</script>

解决https站点无法使用ws问题

https有证书,那么只能使用wss方式连接服务端。那么wss只需要输入域名即可,剩下的端口转发交给后端Nginx去做就ok。

首先确认服务器的websocket端口是开放的(主要是确认下云服务器的安全组是否开放)。

我们主要利用Nginx伪静态方式转发端口,伪静态如下:

location /socket {
    proxy_pass http://127.0.0.1:8936;           
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
}

然后在js上连接代码调整为域名方式,示例如下:

ws = new WebSocket("wss://baidu.com/socket"); //ip地址或者访问域名

这里需要注意的是/socket 要与 伪静态的/socket 一模一样,否则连接失败

废话不多说了,放上代码片段地址:https://gitee.com/wslmf/websocketlog.git



评论