主盘探活
通常是指检查存储设备(例如硬盘)是否可读写,但在Java中并没有直接针对硬件级别的磁盘探活API。然而,我们可以模拟一个场景,即检查某个目录或文件是否可以被Java程序正常读写,以此作为主盘活跃的一个间接判断依据。
[Ref] What is @Scheduled
does?
第1步:创建定时任务服务类
构造一个探活线程池,执行探活线程任务
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
@Component
public class StorageHealthyCheckTask {
private static volatile AtomicBoolean isActive = new AtomicBoolean(true);
@Value("${storage.path}")
private String storagePath;
@Value("${storage.needCheck}")
private boolean needCheck;
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
3, 3,
5, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1),
new ThreadFactoryBuilder().setNameFormat("探活检查-%d").setDaemon(true).build(),
new ThreadPoolExecutor.DiscardPolicy());
@Scheduled(cron = "0/5 * * * * ?") // 每分钟执行一次
private void storageHealthyCheck() {
log.info("{}线程 调用:storageHealthyCheck start", Thread.currentThread().getName());
if (!needCheck) {
log.info("no need check");
return;
}
// true表示正常状态,则已知探活
if (BooleanUtils.isTrue(isActive.get())) {
check(Paths.get(storagePath), isActive);
} else {
// false表示失败,则报错
log.error("isActive:{}", false);
}
log.info("{}线程 调用:storageHealthyCheck end \n", Thread.currentThread().getName());
}
private void check(Path path, AtomicBoolean flag) {
try {
Future<Boolean> future = executor.submit(() -> {
try {
log.info("{}线程 测试isReadable", Thread.currentThread().getName());
// true表示有读权限,false表没读权限,超时中断就会异常退出
return Files.isReadable(path);
} finally {
// 只要路径存在且可读, 就可以认为存储服务是健康的
flag.set(true);
}
});
Boolean res = future.get(2, TimeUnit.SECONDS);
log.info("{}线程 isReadable结果: {}", Thread.currentThread().getName(), res);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
log.error("Thread was interrupted while waiting for the check task to complete.", e);
flag.set(false);
} catch (TimeoutException e) {
log.error("Check task did not complete within the timeout of 2 seconds.", e);
flag.set(false);
} catch (CancellationException e) {
log.error("Check task was cancelled before it could complete.", e);
flag.set(false);
} catch (ExecutionException e) {
log.error("An error occurred while executing the check task", e);
flag.set(false);
}
}
}
第2步:在application.yaml中添加定时任务相关的属性
配置支持探活开关,以主盘路径
storage:
path: C:\Users\zhang\Desktop\test
needCheck: true
第3步:添加@EnableScheduling
注解来启用定时任务调度功能
@SpringBootApplication
@MapperScan("com.zhangziwa.practisesvr.mapper")
@EnableScheduling
public class PractisesvrApplication {
public static void main(String[] args) {
SpringApplication.run(PractisesvrApplication.class, args);
}
}
第4步:单独记录探活日志
<RollingFile name="storage_check"
fileName="${LOG_HOME}/storage_check.log"
filePattern="${LOG_HOME}/storage_check_%d{yyyy-MM-dd-HH}_%i.log.gz"
createOnDemand="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1M"/>
</Policies>
<DefaultRolloverStrategy fileIndex="nomax">
<Delete basePath="${LOG_HOME}" maxDepth="2">
<IfFileName glob="*.log.gz">
<IfAny>
<IfAccumulatedFileSize exceeds="10M"/>
<IfAccumulatedFileCount exceeds="100"/>
<IfLastModified age="30d"/>
</IfAny>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<logger name="com.zhangziwa.practisesvr.utils.task.StorageHealthyCheckTask" level="info" additivity="false">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="storage_check"/>
</logger>
第5步:起服务验证