【Zookeeper】ソースコード分析の持続化(二)のFileSnap

26786 ワード

一、前言
前編の博文はすでにFileTxnLogのソースコードを分析して、今続いて持続化中のFileSnapを分析して、それは主にスナップショットの相応するインタフェースを提供します.
二、SnapShotソースコード分析
SnapShotはFileTxnLogの親であり、インタフェースタイプであり、その方法は以下の通りである.
public interface SnapShot {
    
    /**
     * deserialize a data tree from the last valid snapshot and 
     * return the last zxid that was deserialized
     * @param dt the datatree to be deserialized into
     * @param sessions the sessions to be deserialized into
     * @return the last zxid that was deserialized from the snapshot
     * @throws IOException
     */
    //     
    long deserialize(DataTree dt, Map sessions) 
        throws IOException;
    
    /**
     * persist the datatree and the sessions into a persistence storage
     * @param dt the datatree to be serialized
     * @param sessions 
     * @throws IOException
     */
    //    
    void serialize(DataTree dt, Map sessions, 
            File name) 
        throws IOException;
    
    /**
     * find the most recent snapshot file
     * @return the most recent snapshot file
     * @throws IOException
     */
    //      snapshot  
    File findMostRecentSnapshot() throws IOException;
    
    /**
     * free resources from this snapshot immediately
     * @throws IOException
     */
    //     
    void close() throws IOException;
} 

説明:SnapShotは、逆シーケンス化、シーケンス化、最新のsnapshotファイルの検索、リソースの解放の4つの方法しか定義されていないことがわかります.
三、FileSnapソースコード分析
FileSnapはSnapShotインタフェースを実現し、主にストレージ、シーケンス化、逆シーケンス化、対応するsnapshotファイルへのアクセスとして使用されます.
3.1クラスの属性
public class FileSnap implements SnapShot {
    // snapshot    
    File snapDir;
    //         
    private volatile boolean close = false;
    //    
    private static final int VERSION=2;
    // database id
    private static final long dbId=-1;
    // Logger
    private static final Logger LOG = LoggerFactory.getLogger(FileSnap.class);
    // snapshot     (  class     )
    public final static int SNAP_MAGIC
        = ByteBuffer.wrap("ZKSN".getBytes()).getInt();
}

説明:FileSnapの主なプロパティには、IDがオフになっているかどうかが含まれています.
3.2クラスのコア関数
  1.deserialize関数
関数の署名は次のとおりです.
public long deserialize(DataTree dt,Mapsessions)は、SnapShotのdeserialize関数の実装である.ソースコードは次のとおりです.
    public long deserialize(DataTree dt, Map sessions)
            throws IOException {
        // we run through 100 snapshots (not all of them)
        // if we cannot get it running within 100 snapshots
        // we should  give up
        //   100    snapshot  
        List snapList = findNValidSnapshots(100);
        if (snapList.size() == 0) { //  snapshot  ,    
            return -1L;
        }
        // 
        File snap = null;
        //       
        boolean foundValid = false;
        for (int i = 0; i < snapList.size(); i++) { //   snapList
            snap = snapList.get(i);
            //    
            InputStream snapIS = null;
            CheckedInputStream crcIn = null;
            try {
                LOG.info("Reading snapshot " + snap);
                //      snapshot  
                snapIS = new BufferedInputStream(new FileInputStream(snap));
                //   
                crcIn = new CheckedInputStream(snapIS, new Adler32());
                InputArchive ia = BinaryInputArchive.getArchive(crcIn);
                //     
                deserialize(dt,sessions, ia);
                //       Checksum
                long checkSum = crcIn.getChecksum().getValue();
                //       val 
                long val = ia.readLong("val");
                if (val != checkSum) { //     ,   ,    
                    throw new IOException("CRC corruption in snapshot :  " + snap);
                }
                //   
                foundValid = true;
                //     
                break;
            } catch(IOException e) {
                LOG.warn("problem reading snap file " + snap, e);
            } finally { //    
                if (snapIS != null) 
                    snapIS.close();
                if (crcIn != null) 
                    crcIn.close();
            } 
        }
        if (!foundValid) { //             
            throw new IOException("Not able to find valid snapshots in " + snapDir);
        }
        //         zxid
        dt.lastProcessedZxid = Util.getZxidFromName(snap.getName(), "snapshot");
        return dt.lastProcessedZxid;
    }

説明:deserializeは主に逆シーケンス化として使用され、逆シーケンス化結果をdtおよびsessionsに保存します.その大まかな手順は以下の通りである.
①合法的なsnapshotファイルを100個取得し、snapshotファイルをzxidで降順ソートし、②
②100個のsnapshotファイルを巡り、zxid最大からそのファイルを読み取り、対応するInputArchiveを作成して③に進む
③deserialize(dt,sessions,ia)関数を呼び出して逆シーケンス化操作を完了し、④に進む
④ファイルから読み取ったChecksumが新生のChecksumと等しいかどうかを検証し、等しくなければ異常を投げ出し、そうでなければ⑤
⑤ループを飛び出し、対応する入力フローを閉じ、ファイル名から対応するzxidを解析して返します.
⑥100個のsnapshotファイルを巡回しても検証に合格したファイルが見つからない場合は、例外を放出します.
deserialize関数ではfindNValidSnapshotsと同名のdeserialize(dt,sessions,ia)関数が呼び出され、findNValidSnapshots関数のソースコードは次のとおりです.
    private List findNValidSnapshots(int n) throws IOException {
        //   zxid snapshot        
        List files = Util.sortDataDir(snapDir.listFiles(),"snapshot", false);
        int count = 0;
        List list = new ArrayList();
        for (File f : files) { //   snapshot  
            // we should catch the exceptions
            // from the valid snapshot and continue
            // until we find a valid one
            try {
                //         ,  snapshot        
                //    snapshot    ; snapshot     
                if (Util.isValidSnapshot(f)) {
                    //      
                    list.add(f);
                    //      
                    count++;
                    if (count == n) { //   n     
                        break;
                    }
                }
            } catch (IOException e) {
                LOG.info("invalid snapshot " + f, e);
            }
        }
        return list;
    }

説明:この関数は主にN個の合法的なsnapshotファイルを検索して降順に並べ替えた後に返され、UtilのisValidSnapshot関数は主にファイル名とファイルの末尾記号が「/」であるかどうかからsnapshotファイルが合法かどうかを判断する.ソースコードは次のとおりです.
    public static boolean isValidSnapshot(File f) throws IOException {
        //        snapshot  ,   false
        if (f==null || Util.getZxidFromName(f.getName(), "snapshot") == -1)
            return false;

        // Check for a valid snapshot
        //       
        RandomAccessFile raf = new RandomAccessFile(f, "r");
        try {
            // including the header and the last / bytes
            // the snapshot should be atleast 10 bytes
            if (raf.length() < 10) { //       10   ,  false
                return false;
            }
            //           
            raf.seek(raf.length() - 5);
            byte bytes[] = new byte[5];
            int readlen = 0;
            int l;
            while(readlen < 5 &&
                  (l = raf.read(bytes, readlen, bytes.length - readlen)) >= 0) { //          bytes 
                readlen += l;
            }
            if (readlen != bytes.length) {
                LOG.info("Invalid snapshot " + f
                        + " too short, len = " + readlen);
                return false;
            }
            ByteBuffer bb = ByteBuffer.wrap(bytes);
            int len = bb.getInt();
            byte b = bb.get();
            if (len != 1 || b != '/') { //       "/",   
                LOG.info("Invalid snapshot " + f + " len = " + len
                        + " byte = " + (b & 0xff));
                return false;
            }
        } finally {
            raf.close();
        }

        return true;
    }

deserialize(dt,sessions,ia)関数のソースコードは次のとおりです.
    public void deserialize(DataTree dt, Map sessions,
            InputArchive ia) throws IOException {
        FileHeader header = new FileHeader();
        //      header
        header.deserialize(ia, "fileheader");
        if (header.getMagic() != SNAP_MAGIC) { //         
            throw new IOException("mismatching magic headers "
                    + header.getMagic() + 
                    " !=  " + FileSnap.SNAP_MAGIC);
        }
        //      dt、sessions
        SerializeUtils.deserializeSnapshot(dt,ia,sessions);
    }

説明:この関数は主に逆シーケンス化を行い、逆シーケンス化結果をheaderとsessionsに保存します.その中でheaderの魔数が等しいかどうかを検証します.
  2.serialize関数
関数署名は次のとおりです:protected void serialize(DataTree dt,Map sessions,OutputArchive oa,FileHeader header)throws IOException
    protected void serialize(DataTree dt,Map sessions,
            OutputArchive oa, FileHeader header) throws IOException {
        // this is really a programmatic error and not something that can
        // happen at runtime
        if(header==null) //     null
            throw new IllegalStateException(
                    "Snapshot's not open for writing: uninitialized header");
        //  header   
        header.serialize(oa, "fileheader");
        //  dt、sessions   
        SerializeUtils.serializeSnapshot(dt,oa,sessions);
    }

説明:この関数は主にdt、sessions、headerをシーケンス化するために使用されます.ここで、headerが空であるかどうかを確認し、次にheader、sessions、dtをシーケンス化します.
  3.serialize関数
関数署名は次のとおりです.public synchronized void serialize(DataTree dt,Map sessions,File snapShot)throws IOException
    public synchronized void serialize(DataTree dt, Map sessions, File snapShot)
            throws IOException {
        if (!close) { //    
            //    
            OutputStream sessOS = new BufferedOutputStream(new FileOutputStream(snapShot));
            CheckedOutputStream crcOut = new CheckedOutputStream(sessOS, new Adler32());
            //CheckedOutputStream cout = new CheckedOutputStream()
            OutputArchive oa = BinaryOutputArchive.getArchive(crcOut);
            //      
            FileHeader header = new FileHeader(SNAP_MAGIC, VERSION, dbId);
            //    dt、sessions、header
            serialize(dt,sessions,oa, header);
            //       
            long val = crcOut.getChecksum().getValue();
            //    
            oa.writeLong(val, "val");
            //   "/"
            oa.writeString("/", "path");
            //     
            sessOS.flush();
            crcOut.close();
            sessOS.close();
        }
    }

説明:header、sessions、dtをローカルsnapshotファイルにシーケンス化し、最後に「/」文字を書き込むために使用します.この方法は同期的であり,すなわちスレッドが安全である.
四、まとめ
FileSnapソースコードは比較的簡単で、主にsnapshotファイルを操作するために使用されています.皆さんの視聴にも感謝しています.