首先写代码之前梳理一下流程:
启动一个定时任务: 1、定时探测日志源目录 2、获取需要采集的文件 3、移动这些文件到一个待上传临时目录 4、遍历待上传目录中各文件,逐一传输到HDFS的目标路径,同时将传输完成的文件移动到备份目录 启动一个定时任务: 探测备份目录中的备份数据,检查是否已超出最长备份时长,如果超出,则删除 2、规划各种路径(全都写在配置文件里PropertyHolderLazy.getProps()) 1、日志源路径 2、待上传临时目录 3、备份目录 1、HDFS存储路径 2、HDFS中的文件的前缀 3、HDFS中的文件的后缀
建定时任务:
Timer timer = new Timer(); timer.schedule(new CollectTask(), 0, 60*60*1000L);//定时采集
timer.schedule(new BackupCleanTask(), 0, 60*60*1000L);定时清除
获取配置文件里的信息(顺带复习下单例模式[懒汉式/饿汉式]):
为什么用单例模式:许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
基本的实现思路
单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
//懒汉式——考虑了线程安全
private static Properties prop = null;
public static Properties getProps() throws Exception {
if (prop == null) {
synchronized (PropertyHolderLazy.class) {//
if (prop == null) {
prop = new Properties();
prop.load(PropertyHolderLazy.class.getClassLoader().getResourceAsStream("collect.properties"));
}
}
}
return prop;
}
//饿汉式
private static Properties prop = new Properties();
static {
try {
prop.load(PropertyHolderHungery.class.getClassLoader().getResourceAsStream("collect.properties"));
} catch (Exception e) {
}
}
public static Properties getProps() throws Exception {
return prop;
}
定时采集:
public class CollectTask extends TimerTask {
@Override
public void run() {
/**
* ——定时探测日志源目录 ——获取需要采集的文件 ——移动这些文件到一个待上传临时目录
* ——遍历待上传目录中各文件,逐一传输到HDFS的目标路径,同时将传输完成的文件移动到备份目录
*/
try {
// 获取配置参数
Properties props = PropertyHolderLazy.getProps();
// 构造一个log4j日志对象
Logger logger = Logger.getLogger("logRollingFile");
// 获取本次采集时的日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
String day = sdf.format(new Date());
File srcDir = new File(props.getProperty(Constants.LOG_SOURCE_DIR));
// 列出日志源目录中需要采集的文件
File[] listFiles = srcDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.startsWith(props.getProperty(Constants.LOG_LEGAL_PREFIX))) {
return true;
}
return false;
}
});
// 记录日志
logger.info("探测到如下文件需要采集:" + Arrays.toString(listFiles));
// 将要采集的文件移动到待上传临时目录
File toUploadDir = new File(props.getProperty(Constants.LOG_TOUPLOAD_DIR));
for (File file : listFiles) {
FileUtils.moveFileToDirectory(file, toUploadDir, true);
}
// 记录日志
logger.info("上述文件移动到了待上传目录" + toUploadDir.getAbsolutePath());
// 构造一个HDFS的客户端对象
FileSystem fs = FileSystem.get(new URI(props.getProperty(Constants.HDFS_URI)), new Configuration(), "root");
File[] toUploadFiles = toUploadDir.listFiles();
// 检查HDFS中的日期目录是否存在,如果不存在,则创建
Path hdfsDestPath = new Path(props.getProperty(Constants.HDFS_DEST_BASE_DIR) + day);
if (!fs.exists(hdfsDestPath)) {
fs.mkdirs(hdfsDestPath);
}
// 检查本地的备份目录是否存在,如果不存在,则创建
File backupDir = new File(props.getProperty(Constants.LOG_BACKUP_BASE_DIR) + day + "/");
if (!backupDir.exists()) {
backupDir.mkdirs();
}
for (File file : toUploadFiles) {
// 传输文件到HDFS并改名access_log_
Path destPath = new Path(hdfsDestPath + "/" + UUID.randomUUID() + props.getProperty(Constants.HDFS_FILE_SUFFIX));
fs.copyFromLocalFile(new Path(file.getAbsolutePath()), destPath);
// 记录日志
logger.info("文件传输到HDFS完成:" + file.getAbsolutePath() + "-->" + destPath);
// 将传输完成的文件移动到备份目录
FileUtils.moveFileToDirectory(file, backupDir, true);
// 记录日志
logger.info("文件备份完成:" + file.getAbsolutePath() + "-->" + backupDir);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
定时清除(24h)
public class BackupCleanTask extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
long now = new Date().getTime();
try {
// 探测本地备份目录
File backupBaseDir = new File("d:/logs/");
File[] dayBackDir = backupBaseDir.listFiles();
// 判断备份日期子目录是否已超24小时
for (File dir : dayBackDir) {
long time = sdf.parse(dir.getName()).getTime();
if(now-time>24*60*60*1000L){
FileUtils.deleteDirectory(dir);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
(主要是java基础,和HDFS相关不大)