您当前的位置: 首页 >  zookeeper

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Zookeeper源码解析-自动清理事务日志、快照日志功能分析

恐龙弟旺仔 发布时间:2021-10-20 19:18:25 ,浏览量:0

前言:

自Zookeeper-3.4.0版本开始,就提供了自动清理事务日志和快照日志的功能。

我们可以想一下,如果不清理这些日志会怎样?貌似短期也不会怎样,但是由于这些日志是直接落入当前磁盘的,所以长期以往,磁盘肯定会被占满,导致zookeeper服务无法正常提供。

本文就介绍下这个自动清理日志的功能。

1.配置自动清理

配置的方式很简单,就是在zoo.cfg中添加以下两个配置即可,示例如下:

# 保存3个快照,3个日志文件
autopurge.snapRetainCount=3
# 间隔1个小时执行一次清理
autopurge.purgeInterval=1

autopurge.snapRetainCount指定的是最多保留几个日志文件

autopurge.purgeInterval指定的是多久去清理一次

需要注意的是:这个配置默认是不开启的,所以需要我们手动添加。

2.源码分析清理功能实现

那么这个清理功能时如何实现的呢?我们直接在源码中查找配置出现的地方,最终发现在DatadirCleanupManager中有相关的使用,那么我们就来分析下这个类

2.1 DatadirCleanupManager结构分析
public class DatadirCleanupManager {

    // 任务状态
    public enum PurgeTaskStatus {
        NOT_STARTED, STARTED, COMPLETED;
    }

    // 默认状态
    private PurgeTaskStatus purgeTaskStatus = PurgeTaskStatus.NOT_STARTED;

    // 快照日志路径
    private final String snapDir;

    // 事务日志路径
    private final String dataLogDir;

    // 最多保留的日志文件数
    private final int snapRetainCount;

    // 执行频率
    private final int purgeInterval;

    // 调度器
    private Timer timer;
 
    // 构造器
    public DatadirCleanupManager(String snapDir, String dataLogDir, int snapRetainCount,
            int purgeInterval) {
        this.snapDir = snapDir;
        this.dataLogDir = dataLogDir;
        this.snapRetainCount = snapRetainCount;
        this.purgeInterval = purgeInterval;
        LOG.info("autopurge.snapRetainCount set to " + snapRetainCount);
        LOG.info("autopurge.purgeInterval set to " + purgeInterval);
    }
}

那么这个构造器是在哪里被调用的呢?

源码一通调用,发现入口是:QuorumPeerMain.initializeAndRun()方法,具体如下

public class QuorumPeerMain {
	protected void initializeAndRun(String[] args) throws ConfigException, IOException {
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
            config.parse(args[0]);
        }

        // 在这里开启自动清理任务
        DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config
                .getDataDir(), config.getDataLogDir(), config
                .getSnapRetainCount(), config.getPurgeInterval());
        purgeMgr.start();
        ...
        }
}
2.2 DatadirCleanupManager.start()
public class DatadirCleanupManager {
 
    public void start() {
        // 任务状态及参数校验
        if (PurgeTaskStatus.STARTED == purgeTaskStatus) {
            LOG.warn("Purge task is already running.");
            return;
        }
        if (purgeInterval  0) {
            // 方法处理就是:清理这num个文件之外的其他文件
            purgeOlderSnapshots(txnLog, snaps.get(numSnaps - 1));
        }
    }
    
    static void purgeOlderSnapshots(FileTxnSnapLog txnLog, File snapShot) {
        // 最新的zxid(num个文件中最小的那个)
        // 只要比这个zxid小的文件都需要被清理
        final long leastZxidToBeRetain = Util.getZxidFromName(
                snapShot.getName(), PREFIX_SNAPSHOT);
        // retainedTxnLogs中是需要被保留的日志
        final Set retainedTxnLogs = new HashSet();
        retainedTxnLogs.addAll(Arrays.asList(txnLog.getSnapshotLogs(leastZxidToBeRetain)));
        
        class MyFileFilter implements FileFilter{
            private final String prefix;
            MyFileFilter(String prefix){
                this.prefix=prefix;
            }
            public boolean accept(File f){
                if(!f.getName().startsWith(prefix + "."))
                    return false;
                if (retainedTxnLogs.contains(f)) {
                    return false;
                }
                long fZxid = Util.getZxidFromName(f.getName(), prefix);
                if (fZxid >= leastZxidToBeRetain) {
                    return false;
                }
                return true;
            }
        }
        // files中添加的是zxid比leastZxidToBeRetain小的所有事务日志文件,是需要被删除的
        List files = new ArrayList();
        File[] fileArray = txnLog.getDataDir().listFiles(new MyFileFilter(PREFIX_LOG));
        if (fileArray != null) {
            files.addAll(Arrays.asList(fileArray));
        }

        // fileArray中添加的是zxid比leastZxidToBeRetain小的所有快照文件,需要被删除
        fileArray = txnLog.getSnapDir().listFiles(new MyFileFilter(PREFIX_SNAPSHOT));
        if (fileArray != null) {
            files.addAll(Arrays.asList(fileArray));
        }

        // 删除老文件
        for(File f: files)
        {
            final String msg = "Removing file: "+
                DateFormat.getDateTimeInstance().format(f.lastModified())+
                "\t"+f.getPath();
            LOG.info(msg);
            System.out.println(msg);
            if(!f.delete()){
                System.err.println("Failed to remove "+f.getPath());
            }
        }
    }
}
总结:

代码不算复杂,直接保留对应数目的文件后,直接把其他的全部删除。

这个其他的意思是通过zxid进行比较过的,zxid较小的所有文件(事务日志和快照日志的后缀就是zxid,所以这里直接通过zxid进行比较是合适的)。

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.0668s