问题描述
主机状态一直显示有问题,去向动态链接库请求数据时,除了第一台主机访问成功外,以后的每一台主机返回的结果都是9(HOST_NOT_FOUND)
。
研究了很久也没研究明白,最后求助潘老师。
潘老师指出是指针有问题,主机的指针是我们构造出来的,虽然该指针指向对象的name
和context
与动态链接库服务那边的都一样,但是他那里可能是按地址处理的,不是同一个地址,就报主机找不到了。
经过测试,确实是这样。
不能直接构造指针,需要使用同步计算机数据时返回的指针进行操作。
所以,获取计算机状态,操作计算机都需要重新同步一下计算机,将指针同步过来。
假设用户操作很频繁的话,对动态链接库的压力就很大。指不定啥时候就炸了。
虽然我想到了这点,但也没想到好的解决方案。
潘老师最终的设计方案非常好,采用定时任务,当有计算机的操作时,不实时进行操作,而是先存起来,每隔一段时间统一进行操作,从而减轻服务器压力。
实现
设计
对主机进行关机或重启时,不立即执行,而是将其添加到一个要执行的任务列表中,当定时任务触发后,再统一执行。
定时任务
定时任务很简单,之前在计量中写过一个每天晚上定时统计数据的定时任务,这次写这个没什么难度,都不需要看文档了。
注释很详尽,相信聪明的你完全可以理解。
这里的存储计算机状态设置了一个主机名到计算机状态映射的HashMap
,这里没有用ConcurrentHashMap
,因为只有定时任务一个线程进行put
,其他接口调用该Map
只负责查询,不会出现冲突。
@Async
@Scheduled(fixedRate = 10000) // 每隔10s执行
public void hostHandle() {
logger.info("--- 开始执行定时任务 ---");
logger.debug("获取关机重启的订阅者");
Set<String> shutdownSubscribers = this.cloneStringSetAndClear(hostService.getShutdownSubscribers());
Set<String> rebootSubscribers = this.cloneStringSetAndClear(hostService.getRebootSubscribers());
logger.debug("获取主机结构体指针Map");
List<HostStruct.ByReference> byReferenceList = baseService.getHostStructReferenceList();
Map<String, HostStruct.ByReference> byReferenceMap = baseService.getHostStructReferenceMap(byReferenceList);
if (!shutdownSubscribers.isEmpty()) {
logger.info("存在关机订阅,执行关机操作");
for (String name : shutdownSubscribers) {
logger.debug("获取结构体指针并关机");
HostStruct.ByReference byReference = byReferenceMap.get(name);
baseService.shutdown(byReference);
}
}
if (!rebootSubscribers.isEmpty()) {
logger.info("存在重启订阅,执行重启操作");
for (String name : rebootSubscribers) {
logger.debug("获取结构体指针并重启");
HostStruct.ByReference byReference = byReferenceMap.get(name);
baseService.reboot(byReference);
}
}
logger.debug("查询主机列表");
for (HostStruct.ByReference byReference : byReferenceList) {
logger.debug("查询主机指针,同时获取主机信息");
Integer status = baseService.getHostStatus(byReference);
logger.debug("添加到Map中");
hostService.getHostStatusMap().put(Native.toString(byReference.name), status);
}
logger.info("--- 定时任务执行完毕 ---");
}
/**
* 克隆字符串集合并清空原集合
* @param primarySet 原集合
* @return 克隆的字符串集合
*/
private Set<String> cloneStringSetAndClear(Set<String> primarySet) {
logger.debug("新建集合");
Set<String> set = new HashSet<>(primarySet);
logger.debug("清空原集合");
primarySet.clear();
logger.debug("返回");
return set;
}
todo
目前是用Set
存储要操作的主机,但是经过查询,HashSet
、LinkedHashSet
、TreeSet
都是线程不安全的。
这里就怕执行定时任务的时候,突然来了一个关机的指令,两个线程同时访问Set
,定时任务要克隆一个Set
然后把这个清空,关机需要在Set
中添加元素,两个线程如果交替执行,结果就很难说了。
去concurrent
包下找,也没找到合适的。看了看,有第三方库实现的线程安全的Set
,以后引入进来。
这里不知道是不是我用的有问题,是并发场景下不推荐用Set
吗?为什么concurrent
包下没有相关的实现类?
总结
感叹一句:C++
还是难啊!
C++
老师对我的评价是字写得还不错,哈哈哈。佩服C++
工程师!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。