主盘探活
通常是指检查存储设备(例如硬盘)是否可读写,但在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\testneedCheck: 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步:起服务验证