【问题标题】:Proper way to monitor/control a server remotely over http in realtime通过 http 实时监控/控制服务器的正确方法
【发布时间】:2015-05-06 16:09:51
【问题描述】:

在我的客户端(带有浏览器的手机)上,我想查看服务器 CPU、RAM 和 HDD 的统计数据,并从各种日志中收集信息。

我正在使用 ajax 轮询。

在客户端每 5 秒 (setInterval) 我调用一个 PHP 文件:

  1. 扫描包含 N 条日志的文件夹

  2. 读取每条日志的最后一行

  3. 将其转换为 JSON

问题:

  1. 每 5 秒打开一次新连接。
  2. 多个 AJAX 调用。
  3. 请求标头(它们也是数据,因此会消耗带宽
  4. 响应标头 (^)
  5. 使用 PHP 每 5 秒读取一次文件。即使没有任何改变。

最终的 JSON 数据小于 5 KB,但是我每 5 秒发送一次,并且每次都有 headers 和新连接,所以基本上每 5 秒,我必须发送 5-10 KB 才能得到 5 KB 为 10-20 KB。

如果我让应用保持打开状态,这些是每分钟 60 sec / 5 sec = 12 的新连接和每小时大约 15 MB 的流量。

假设我有 100 个用户,我让他们监视/控制我的服务器,一小时内的传出流量约为 1.5 GB。

更不用说 PHP 服务器每 5 秒读取多个文件 100 次。

我需要在服务器上每 5 秒读取一次这些日志的最后几行,并可能将它们写入一个文件,然后我想仅在数据发生更改时才将这些数据推送到客户端。


使用 PHP 的 SSE(服务器发送事件)

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while(true){
 echo "id: ".time()."\ndata: ".ReadTheLogs()."\n\n";
 ob_flush();
 flush();
 sleep(1);
}

在这种情况下,与第一个用户建立连接后 连接保持打开状态(PHP 不是为此而设计的),因此我节省了一些空间(请求标头、响应标头)。这项工作在我的服务器上工作,但大多数服务器不允许长时间保持连接打开。

还有多个用户,我多次阅读日志。(减慢我的旧服务器) 而且我无法控制服务器...我需要使用 ajax 来发送命令...

我需要 WebSockets!!!


node.js 和 websockets

使用 node.js,据我了解,我可以在不消耗大量资源的情况下完成所有这些操作 资源和带宽。连接保持打开,因此没有不必要的标题,我可以接收和发送数据。它可以很好地处理多个用户。

这就是我需要你帮助的地方。

  1. node.js 服务器应该在后台更新,如果文件被修改,则每 5 秒存储一次日志数据。或者操作系统应该使用 (iwatch,dnotify...)

  2. 只有在发生变化时才应该推送数据。

  3. 日志的读取应仅在 5 秒后发生一次...因此不会由每个用户触发。


这是我找到的第一个例子......并修改......

var ws=require("nodejs-websocket");
var server=ws.createServer(function(conn){

    var data=read(whereToStoreTheLogs);
    conn.sendText(data)// send the logs data to the user 
    //on first connection.

    setTimeout(checkLogs,5000);
    /*
     here i need to continuosly check if the logs are changed.
     but if i use setInterval(checkLogs,5000) or setTimeout
     every user invokes a new timer and so having lots of timers on the server
     can i do that in background?         
    */
    conn.on("text",function(str){
     doStuff(str); // various commands to control the server.
    })
    conn.on("close",function(code,reason){
     console.log("Connection closed")
    })
}).listen(8001);

var checkLogs=function(){
 var data=read(whereToStoreTheLogs);
 if(data!=oldData){
  conn.sendText(data)
 }
 setTimeout(checkLogs,5000);
}

上面的脚本将是通知服务器,但我还需要找到一种解决方案来将这些多个日志的信息存储在某个位置,并在每次发生更改时在后台执行此操作。

您将如何保持带宽低以及服务器资源。

你会怎么做?

编辑 顺便提一句。有没有办法将这些数据同时流式传输到所有客户?

编辑

关于日志:我还希望能够缩放更新之间的时间膨胀......我的意思是,如果我读取 ffmpeg 的日志,我会尽可能每秒更新一次......但是当没有转换处于活动状态时。 . 我需要每 5 分钟获取一次基本机器信息……等等……

目标: 1.在某处读取和存储日志数据的高性能方式(仅当客户端连接...[mysql,文件,可以将此信息存储在ram中(使用node.js??)])。 2. 将数据流式传输到各种客户端(同时)的高性能方式。 3.能够向服务器发送命令..(双向) 4. 使用网络语言(js、php...)、lunix 命令(在多台机器上很容易实现的东西).. 如果需要免费软件。

最好的方法是:

根据当前活动将日志读取到系统内存同时连续地流式传输,并且已经打开连接,通过webSockets连接到各种客户端。

我不知道有什么可以更快的。

更新

node.js 服务器已启动并运行,使用http://einaros.github.io/ws/ webSocketServer 实现,因为它似乎是最快的。 我在@HeadCode 的帮助下编写了以下代码,以正确处理客户端情况并尽可能降低流程。检查广播循环中的各种内容。现在推送和客户端处理处于良好状态。

var 
wss=new (require('ws').Server)({port:8080}),
isBusy,
logs,
clients,
i,
checkLogs=function(){
 if(wss.clients&&(clients=wss.clients.length)){
  isBusy||(logs=readLogs()/*,isBusy=true*/);
  if(logs){
   i=0;
   while(i<clients){
    wss.clients[i++].send(logs)
   }
  }
 } 
};
setInterval(checkLogs,2000);

但是 atm 我正在使用一种非常糟糕的方式来解析日志.. (nodejs->httpRequest->php).. 哈哈。经过一番谷歌搜索后,我发现我完全可以将 linux 软件的输出直接流式传输到 nodejs 应用程序......我没有检查......但也许这将是最好的方法。 node.js 还有一个文件系统 api,我可以在其中读取日志。 linux有自己的文件系统api。

readLogs()(可以是异步的)功能我仍然不满意。

  1. nodejs 文件系统?
  2. linuxSoftware->nodejs 输出实现
  3. linux 文件系统 api。

请记住,我需要扫描各种文件夹中的日志,然后以某种方式解析输出的数据,并且每 2 秒解析一次。

ps.:如果 logReading 系统是异步的,我将 isBusy 添加到服务器变量中。

编辑

答案不完整。

缺失:

  1. 一种在某处读取、解析和存储日志的高效方法(linux 文件系统 api 或 nodejs api,因此我直接存储到系统内存中)
  2. 说明是否可以将数据直接传输给多个用户。 显然 nodejs 通过客户端循环,因此(我认为)发送多次数据。

顺便说一句,如果没有客户端并重新启动 apache 端的新连接,是否有可能/值得关闭节点服务器。 (例如:如果我连接到 apache 托管的 html 文件,则脚本会再次启动 nodejs 服务器)。这样做会进一步减少内存泄漏???对吧?

编辑

在对 websockets 进行了一些试验(一些视频在 cmets 中)之后,我学到了一些新东西。 Raspberry PI 有可能使用一些 CPU DMA 通道来处理诸如 PWM 之类的高频东西……我需要以某种方式了解它是如何工作的。

当使用传感器和类似的东西时,我应该将所有内容存储在 RAM 中,nodejs 已经这样做了?? (在脚本内的变量中)

websocket 仍然是最佳选择,因为它现在基本上可以从任何设备轻松访问,只需使用浏览器。

【问题讨论】:

  • 您是否考虑过使用nagios或其他监控工具?
  • 您这样做是因为您确实需要监控服务器,还是出于某种学术/娱乐目的?
  • @Keo 我正在监控 ffmpeg 的日志。
  • @Willem D'haeseleer 我喜欢在业余时间学习新东西。如果这就是您的要求,请不要用这个赚钱..我的ubuntu无头服务器在一个带有ffmpeg的虚拟机中...当我最终在转换时正确读取这些日志时,我发现了它需要大量资源......而且我认为我可以编写 js node.js 会很好。
  • 免费吗?资源少?更新频率?它是否通过 websockets 输出 json?顺便说一句,我喜欢自己写……使用本机命令……不要使用插件。我不使用 jquery 或类似的东西来使一切变得简单。-

标签: javascript php ajax node.js


【解决方案1】:

您需要构建一个推送通知服务器。当新数据更新时,所有正在监听的客户端都会收到推送通知。有关更多信息,请参阅此答案:PHP - Push Notifications

至于如何更新数据,我建议使用基于操作系统的工具来触发 PHP 脚本(命令行),该脚本将生成一个“推送”json 文件到当前正在侦听的任何客户端。任何新客户端登录到“listen”都将获得当前可用的 json,直到它被更新。

这样一来,您就不会受到 100 个用户使用 100 个连接以及每 5 秒轮询一次服务器的带宽的限制,并且只有在他们需要知道有更新时才会更新。

【讨论】:

  • 是的,我正在尝试使用现代技术编写自己的推送服务器。 php 对于 websockets 和 push stuff 等实时应用程序来说并不是最好的。顺便说一句,我的想法是在需要时将日志推送到系统内存中,这样它甚至不需要读取创建的 logfileinfo ..
  • 是的,这只是一个例子。签出:socket.io/demos/chat这是你想要的。
【解决方案2】:

如何读取所有日志信息(通过 IPMINagios 或其他方式)并按计划创建输出文件的服务。然后任何想要连接的人都可以读取 this 输出,而不是敲击服务器日志。基本上对服务器日志进行一次点击,然后其他人都只是阅读网页。

这很容易实现。

顺便说一句:Nagios 有一个很好的免费

【讨论】:

  • 你认为 naigos 在后台做什么???你认为它发明了各种日志??? naigos 还需要以某种方式读取日志。顺便说一句,我不想​​使用 1000 美元的软件。再次阅读问题。
  • Nagios 有各种各样的连接器。如果您想跳过该部分,只需使用预定服务直接读取 IPMI 数据(或 SNMP,如果需要)。让 Nagios 参与循环只是处理其中的一部分。
  • 重点是安装第三方软件意味着进一步使用系统资源。可能有许多无用的(对我而言)后台进程,而且监控系统也可能没有与 websockets 等最新技术保持同步。但最重要的是我试图自己创造它。另外,据我所知,这些 snmp ipmi 不是为多个(许多)用户设计的。它们很有趣,但不是我需要的。它也应该在 arm 处理器等其他平台上工作......
  • 这就是为什么 node.js 应该比 snmp 和 ipmi 更好。但也许我错了……与 node.js 相比,您是否还有其他一些来源可以显示性能(具有多个用户)?
  • Websockets 不是新技术。这不是一个新问题。如果您不喜欢 Nagios,那么您可能会更喜欢 growing list 的工具。
【解决方案3】:

我没有使用 nodejs-websocket,但它看起来会接受一个 http 连接并进行升级以及创建服务器。如果您关心的只是接收文本/json,那么我想这很好,但在我看来,您可能希望同时提供一个网页。

这是一种使用 express 和 socket.io 来实现您所询问的方法:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.use(express.static(__dirname + '/'));
app.get('/', function(req, res){
    res.sendfile('index.html');
});

io.on('connection', function(socket){

    // This is where we should emit the cached values of everything
    // that has been collected so far so this user doesn't have to
    // wait for a changed value on the monitored host to see
    // what is going on.
    // This code is based on something I wrote for myself so it's not
    // going to do anything for you as is.  You'll have to implement
    // your own caching mechanism.
    for (var stat in cache) {
        if (cache.hasOwnProperty(stat)) {
            socket.emit('data', JSON.stringify(cache[stat]));
        }
    }
});

http.listen(3000, function(){
    console.log('listening on *:3000');
});

(function checkLogs(){
    var data=read(whereToStoreTheLogs);
    if(data!=oldData){
        io.emit(data)
    }
    setTimeout(checkLogs,5000);
})();

当然,checkLogs 功能必须由您自己充实。我只是在这里剪切并粘贴它以作为上下文。对 io 对象的 emit 函数的调用会将消息发送给所有连接的用户,但 checkLogs 函数只会触发一次(然后继续调用自身),而不是每次有人连接时触发。

在您的 index.html 页面中,您可以拥有类似的内容。它应该包含在底部的 html 页面中,就在结束 body 标记之前。

<script src="/path/to/socket.io.js"></script>
<script>

    // Set up the websocket for receiving updates from the server
    var socket = io();

    socket.on('data', function(msg){
        // Do something with your message here, such as using javascript
        // to display it in an appropriate spot on the page.
        document.getElementById("content").innerHTML = msg;
    });

</script>

顺便说一下,查看 Nodejs 文档,了解各种检查系统资源的内置方法 (https://nodejs.org/api/os.html)。

这里还有一个更符合您想要的解决方案。将此用于您的 html 页面:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>WS example</title>
</head>
<body>
<script>
var connection;
window.addEventListener("load", function () {
    connection = new WebSocket("ws://"+window.location.hostname+":8001")
    connection.onopen = function () {
        console.log("Connection opened")
    }
    connection.onclose = function () {
        console.log("Connection closed")
    }
    connection.onerror = function () {
        console.error("Connection error")
    }
    connection.onmessage = function (event) {
        var div = document.createElement("div")
        div.textContent = event.data
        document.body.appendChild(div)
    }
});
</script>
</body>
</html>

并将其用作您的 Web 套接字服务器代码,最近调整为使用“tail”模块(如在这篇文章中找到:How to do `tail -f logfile.txt`-like processing in node.js?),您必须使用 npm 安装它(注意:tail 使用 fs .watch,不保证在任何地方都一样):

var ws = require("nodejs-websocket")
var os = require('os');
Tail = require('tail').Tail;
tail = new Tail('./testlog.txt');

var server = ws.createServer(function (conn) {
    conn.on("text", function (str) {
        console.log("Received " + str);
    });
    conn.on("close", function (code, reason) {
        console.log("Connection closed");
    });
}).listen(8001);


setInterval(function(){ checkLoad(); }, 5000);


function broadcast(mesg) {
    server.connections.forEach(function (conn) {
        conn.sendText(mesg)
    })
}

var load = '';
function checkLoad(){
    var new_load = os.loadavg().toString();
    if (new_load === 'load'){
        return;
    }
    load = new_load;
    broadcast(load);
}

tail.on("line", function(data) {
    broadcast(data);
});

显然这是非常基本的,您必须根据需要进行更改。

【讨论】:

  • “checkLogs 功能必须由你来充实”是什么意思?
  • 肉...我不能在 socket.io 中使用本机语法吗...“new WebSocket("ws://url:port");" ?
  • 客户端 html 使用 apache 或 nginx 呈现。我只会将节点用于数据部分。所以只是一个小的json文件
  • @cocoo 我更新了我的答案以包含我认为正是您正在寻找的东西。
  • 谢谢,虽然 node.js 现在一切正常,但我很好奇我是否可以进一步减少我刚刚发布的 checkLogs 循环示例中发生的广播数据。重复我,使用某种流,因为现在它通过客户端循环,因此多次发送数据。所以每 2 秒可以说 (5kb*clients)
【解决方案4】:

我最近使用 Munin 做了一个类似的实现。 Munin 是一个很棒的服务器监控工具,也是开源的,还提供了REST API。有几个插件可用于监控服务器的 CPU、HDD 和 RAM 使用情况。

【讨论】:

    【解决方案5】:

    只回答你的这些问题:

    1. 将数据流式传输到各种客户端(同时)的高性能方式。
    2. 能够向服务器发送命令..(双向)
    3. 使用网络语言(js、php...)、lunix 命令(在多台机器上很容易实现的东西).. 如果需要免费软件。

    我会推荐 CometD 项目简化的 Bayeux 协议。有多种语言的实现,并且以最简单的形式非常易于使用。

    Meteor 大体相似。它是一个应用程序开发框架,而不是一系列库,但它解决了同样的问题。

    【讨论】:

    • comet 使用 ajax ... ajax 每次都需要发送请求标头对吗? websockets没有?我错了吗?
    • 顺便说一句,是否有一些基准,例如 nodejs vs comet 或 meteor?
    • No CometD 不使用 ajax,它使用来自客户端的长请求。类似于 Ajax,但请求更少且响应更快(以保持连接打开为代价,即使没有待处理的流量)。我不知道基准,但是如果您发现任何基准,请更新答案,我相信它们对其他人有用。
    【解决方案6】:

    一些建议:

    • 用于图表的穆宁
    • NetSNMP(Munin 使用,但您也可以使用 Bash 和 Cron 构建陷阱,在警报中发送 SMS 文本)
    • Pingdom 用于远程警报服务器对 ping 和 HTTP 检查的响应情况。它可以向您发送短信或拨打电话,并具有呼叫升级规则。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-21
      • 2013-03-24
      • 2016-11-04
      • 2015-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多