ipmi-sel定位最后一条日志记录
带外设备监控
ipmi带外监控传感器相关的指标需要管理员权限,但是管理员权限是可以操纵设备的开关机的,具有一定的危险性,故退而求其次,ipmi-sel监控带外日志,账号权限是操作者权限就能拿到数据,风险大大减低。
监控实现
ipmi-sel有一个定位最后一条日志的命令行参数–tail=count,完整的命令如下
ipmi-sel -h ip -u username -p password --driver-type=LAN_2_0 --output-event-state --interpret-oem-data --entity-sensor-names --tail=1
获取两个偏移量之间的日志命令如下
ipmi-sel -h ip -u username -p password --driver-type=LAN_2_0 --output-event-state --interpret-oem-data --entity-sensor-names --display-range=%s-%s
监控流程,给隔五分钟采集一次
- 当第一次进来时,直接定位最后一条数据,然后将这个最后的偏移量记录下来
- 非第一次进来时,从记录中获取最后的偏移量lastOffSet,执行上面的命令获取最后一条记录curOffSet,将这两个偏移之间的数据采集回去,然后将curOffSet记录留待下次使用。
上线后一开始跑得很顺畅,设备不断接入后问题出现了,有部分设备在执行上面的获取最后一条记录的命令时会一直打印日志,根本停不下来,知道程序超时退出,采集失败。很明显,–tail参数时不完善,不能满足我的需求。
新的想法
既然不能直接拿到最后一条数据,但是我可以通过一定办法不断逼近最后一条数据,只要在允许的时间范围内就可行。实现前提
1、 可以使用两个相同的偏移量来定位某条数据,如下命令可以定位到第一条记录,如果返回为空,说明日志的最后偏移没到那么大。
ipmi-sel -h ip -u username -p password --driver-type=LAN_2_0 --output-event-state --interpret-oem-data --entity-sensor-names --display-range=1-1
2、获取一条记录返回时间短,这样才能在有限的时间内逼近最后一条。
3、日志表有一个边界,带外日志最长可以存放65534条数据。
经测试是可行的,由于学过算法,我很快就想到可以使用二分查找的扩展算法来逼近右边界的数据。
算法原型如下,
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1) + 1;
if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid;
}
}
return nums[right] == target ? right : -1;
}
}
循环条件: left < right
中间位置计算: mid = left + ((right -left) >> 1) + 1
左边界更新:left = mid
右边界更新: right = mid - 1
返回值: nums[right] == target ? right : -1
那么回到具体问题,此时我们想要查找的target就不是数字,而是一条不为空的数据,其实也可以把日志表抽象为一个数组,有数据则为1,没有数据则为2,假如有一个日志表有5条数据,日志表可以存储10条数据,那么数组可以抽象为[1,1,1,1,1,2,2,2,2,2],那么问题就变成了在这个数组中寻找最右边的1.
二分查找java实现
java代码描述如下:
/**
*@param IpmiRequest ipmi的账号密码相关参数
*@param scriptRunner 脚本执行的接口
*@param startId 起始边界 最小为1
*@param endId 结束边界,最大为65534
*/
public int find(IpmiRequest ipmiRequest, ScriptRunner scriptRunner, int startId, int endId) throws Exception{
long startTime = System.currentTimeMillis();
int count = 0;
//一条ipmi记录行
SelBody target = null;
//二分查找右边界
while (startId < endId) {
int midId = startId + (endId - startId)/2 + 1;
//执行ipmi脚本拿到数据,如果为空说明没有拿到数据
target = checkHasData(scriptRunner, ipmiRequest, midId);
if (target == null) {
endId = midId - 1;
} else {
startId = midId;
}
count++;
}
if (log.isDebugEnabled()) {
long costTime = System.currentTimeMillis() - startTime;
log.debug("{}二分查找花费的查找次数是:{},值是:{},花费的时间是:{}s", ipmiRequest.toString(), count, startId, costTime/1000);
}
return startId;
}
二分查找的次数大概log(65534),实验所得平均是16次就能拿到最后一条数据,时间花费是8s,这个时间不短,但是能接受,只是在第一次的时候才定最长的边界,后面有了上一次的偏移量之后可以将右偏移量设置为上一次的偏移加1000,这样拿到最后一条数据的平均次数是log(1000),4s左右就能拿到。这样每次能拿到最新一条的偏移就按照左右边界采集数据了。