唔,前两天想起来以前挖的坑,准备重写一下,让幻想互联节点页面显示服务器真正的实时状态(如负载,网络速率等)。听说这样可以让页面更实用一些?

那么首先要做最基本的实现,嗯,也就是数据的收集。。

在N年前用过NodeQuery,算是比较方便的一个Liunx系统资源监控工具吧。

他是基于客户端Agent主动提交大部分数据(如CPU使用,负载,网络速率,硬盘空间、进程使用等),和服务器定时Ping(仅做延迟检测等)来实现数据收集。

然后存储于它的数据库中,每次只需打开它的页面就能优雅的显示性能信息。

But,这个页面不像UptimeRobot,提供Public页面,也就是非登录用户可以查看的页面。。

(顺带吐槽一下。UptimeRobot那页面用的IIS+PHP-CGI,经常莫名50x boom。而且绑域名的Public的页面已经炸了。。。。)

如果要给用户显示节点信息,更不能使用其页面,因为我们可能只需要知道服务器实时网速,而不需要其他内容,打开额外的一个页面十分不友好。

以前用过某Panel,服务端会统计节点的网速等等信息。

然而部署实现略复杂。。

最近发现NodeQuery提供了API,而且Agent客户端部署,几乎是一键完成的,只需要wget相应脚本就能自动部署,完全不需要关系其安装、自启什么的。。。

废话到这啦。

NodeQuery提供的API十分简单,只需要简易GET就可以获取相应数据,输出为JSON。

先给个简单的实现:


$server_id = 服务器ID;
$nodequery_api_url = 'https://nodequery.com/api/servers/'.$server_id.'?api_key=你的APIKEY';
if($nodequery_api_url){
$nodequery_api_callback_json = file_get_contents($nodequery_api_url);
$nodequery_result_array=json_decode($nodequery_api_callback_json,true);
echo "<pre style=\"font-family: Microsoft YaHei, Microsoft YaHei Light \">";
$rx_speed= round($nodequery_result_array['data']['0']['current_rx'] / 1024 / 180, 2)."KB/s";
$tx_speed= round($nodequery_result_array['data']['0']['current_tx'] / 1024 / 180, 2)."KB/s";
$nodequery_result = $Speed."KB/s";
echo "RESULT:[Rx_Speed:".$rx_speed."][Tx_Speed:".$tx_speed."]";
echo "</pre>\n";

这样可以获取并显示服务器的速率。

先说下这里头有个坑。。

官方API给的current_rx,窝一开始还以为是byte/s或b/s之类的。。拿计算器对照官方页面显示的MB/s换算了半天,也没换算通2333。

比如API的数值为:3485884326,官方页面上显示的就是35.87MB/s。

除了N次1024,感觉很迷,就是得不出来。。

官方版面也有人提到这个问题,但是并没有正确的答复。。。

最后发现!这数值是自上一次Query之后的增值,也就是3分钟之内传输的数据量。

所以就是180秒咯,然后再除个1024,就可以得出KB/s了,稳。

So,现在可以正常显示了。

但,NodeQuery服务器好像是意大利的?全国Ping 300+。。。

连过去贼慢,容易卡线程。

So,如果写到数据库缓存一下,那就比较丝滑了。

因为NodeQuery数据每个节点是180秒才更新一次,所以没必要实时Get,缓存个30秒为佳(不同节点,时间不同步)。

首先是可以写到MySQL,但是要建数据库和表,略麻烦,为了一个小实现去建表www。。

于是乎想起之前有见过用Redis缓存的程式。。印象中是基于内存的数据库,查询快,而且不用写到硬盘(一般情况)。

那就去搜一下Redis的实例咯。。

服务端用Oneinstack配置的,所以默认安装了Redis。

经过几分钟的摸索,想出这么一个模式。

需要记录两个字符串,一个记录最后一次的获取时间,一个记录获取到的记录。

获取时间就直接用Unix时间戳了,最为方便,可以直接减得时间差,设定30秒内读取缓存,极为方便。

获取到的记录就直接把拿到的JSON往里写,要调用的时候再解析,这样保证数据的完整,要什么拿什么(一次请求可以获得多个服务器的性能参数)
(本例获取的是单台服务器的2333,有获取多台的API)

先试一下服务器娘是否工作正常,存一个时间进去,然后读出来。


//NodeQuery数据获取与缓存
$redis_nodequery = new Redis();
$redis_nodequery->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully</br>";
//查看服务是否运行
echo "Server is running: " . $redis_nodequery->ping();
$redis_nodequery->set("redis_nodequery_last_query_time", time());
// 获取存储的数据并输出
echo "</br>Stored string in redis:: " . $redis_nodequery->get("redis_nodequery_last_query_time");

按照窝的时间,结果为:


Connection to server sucessfully
Server is running: +PONG
Stored string in redis:: 1493387558

写入的时间可以正常读取出来,如果木有,请检查下Redis的配置是否正确。。。

这时候查着查着,发现Redis实际上是有持久化储存的,会定时/定量写入硬盘。。

大部分Redis应用不会强制需要上次的数据,我们可以关闭这个功能,并且把表清空一下。

配置文件中。吧三个默认的save给#,注释掉,然后打个save "",就行。

至于配置文件在哪,不太清楚Oneinstack是yum安装的还是怎么装的。。。窝也找了一好,除了用系统搜索以外,可以直接去查redis的服务文件,可以直接看到conf的位置。

清空数据的方法:先redis-cli进入,然后flushall即可。

配置文件改好记得service redis-server restart一下。

嗯,于是经过短暂滴摸索于研究,顺带解决一些可能请求超时的问题:


<?php
header("Content-Type: text/html; charset=UTF-8");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); 
header("Cache-Control: no-cache, must-revalidate"); 
header("Pramga: no-cache");

ini_set("display_errors", "On");
error_reporting(E_ALL^E_NOTICE^E_WARNING);

//时间环境

ini_set('date.timezone','Asia/Shanghai');
date_default_timezone_set('Asia/Shanghai');

//调试环境
$server_id = 服务器ID数字;

//Redis获取NodeQuery数据与缓存
$redis_nodequery = new Redis();
$redis_nodequery->connect('127.0.0.1', 6379);
//查看服务是否运行
echo "Server is running: " . $redis_nodequery->ping() . '</br>';
//获取Redis存储数据
if ((time() - $redis_nodequery->get("redis_nodequery_last_query_time")) < 30){
 //Good,use cache.
 echo 'Good,use cache.</br>';
 $nodequery_api_callback_json=$redis_nodequery->get("redis_nodequery_last_query_content");
 //调试输出
 //echo $redis_nodequery->get("redis_nodequery_last_query_content");
}else{
 //Cache Timeout.
 echo 'Cache Timeout.</br>';
 $nodequery_api_url = 'https://nodequery.com/api/servers/'.$server_id.'?api_key=你的APIKEY';
 if($nodequery_api_url){
 $nodequery_file_get_contents_opts = array(
 'http'=>array( 
 'method'=>"GET", 
 'timeout'=>1, 
 ),
 'https'=>array( 
 'method'=>"GET", 
 'timeout'=>1, 
 )
 ); 
 $nodequery_file_get_contents_opts_context = stream_context_create($nodequery_file_get_contents_opts); 
 $nodequery_content = file_get_contents($nodequery_api_url, false, $nodequery_file_get_contents_opts_context);
 if($nodequery_content){
 echo 'API Query Request Success.</br>';
 $nodequery_api_callback_json=$nodequery_content;
 $redis_nodequery->set("redis_nodequery_last_query_content", $nodequery_content);
 $redis_nodequery->set("redis_nodequery_last_query_time", time());
 }else{
 echo 'API Query Request Failed,try to use old cache.</br>';
 $nodequery_api_callback_json=$redis_nodequery->get("redis_nodequery_last_query_content");
 }
 }
 //调试输出
 //echo $redis_nodequery->get("redis_nodequery_last_query_content");
}
//获取Redis存储数据结束

//获取时间
echo "Query Time:".date("Y-m-d H:i:s",$redis_nodequery->get("redis_nodequery_last_query_time"));

$nodequery_result_array=json_decode($nodequery_api_callback_json,true);
echo "<pre style=\"font-family: Microsoft YaHei, Microsoft YaHei Light \">";
//print_r($nodequery_result_array);
echo "</br>";
$rx_speed= round($nodequery_result_array['data']['0']['current_rx'] / 1024 / 180, 2)."KB/s";
$tx_speed= round($nodequery_result_array['data']['0']['current_tx'] / 1024 / 180, 2)."KB/s";
echo "RESULT:[Rx_Speed:".$rx_speed."][Tx_Speed:".$tx_speed."]";
echo "</pre>\n";

?>

其中$nodequery_file_get_contents_opts来设定获取的超时,窝设置的是1s。

如果因为网络获取失败或超时,会自动调用上一次的结果,避免客户端请求时间过长或显示失败。
通过判断获取成功后再存入和更新获取时间,保持原子性。

唔,那就先这样啦,获取成功妥妥滴。
准备再折腾成Web API给客户中心用,顺便要加个验证免得恶意拉取(反正不会请求源,其实也无所谓了?)
其实这是被动的,还可以做成Cron,用户可以完全避免等待请求时间的问题,或做成触发式。
调了下路由,给获取API指定走国际线路了,应该会快一点w。


升空的焰火,从下面看还是从侧面看?