(八)、ZooKeeper 事物

利用Transaction提交創建節點

代碼如下:
/**
     * <p>創建zNode節點, String create(path<節點路徑>, data[]<節點內容>, List(ACL訪問控制列表), CreateMode<zNode創建類型>) </p><br/>
     * <pre>
     *     節點創建類型(CreateMode)
     *     1、PERSISTENT:持久化節點
     *     2、PERSISTENT_SEQUENTIAL:順序自動編號持久化節點,這種節點會根據當前已存在的節點數自動加 1
     *     3、EPHEMERAL:臨時節點客戶端,session超時這類節點就會被自動刪除
     *     4、EPHEMERAL_SEQUENTIAL:臨時自動編號節點
     * </pre>
     * @param path zNode節點路徑
     * @param data zNode數據內容
     * @return 創建成功返回true, 反之返回false.
     */
	public boolean createZNode(String path,String data){
		try {
			String zkPath = MyZooKeeper.zooKeeper.create(path, data.getBytes(),
					acls, CreateMode.PERSISTENT);
			logger.info("ZooKeeper創建節點成功,節點地址:" + zkPath);
			return true;
		} catch (KeeperException e) {
			logger.error("創建節點失敗:" + e.getMessage() + ",path:" + path  ,e);
		} catch (InterruptedException e) {
			logger.error("創建節點失敗:" + e.getMessage() + ",path:" + path  ,e);
		}
		return false;
	}
批量創建多個時,可以引入事物來進行對創建的控制:
public boolean createZNode(List<String> path,List<String> data){
		try {
			Transaction tx = MyZooKeeper.zooKeeper.transaction();
			for (int i = 0,len = path.size(); i < len; i++) {
				createZNode(path.get(i), data.get(i));
			}
			tx.commit();
			return true;
		} catch (InterruptedException e) {
			logger.error("創建節點失敗:" + e.getMessage() + ",path:" + path  ,e);
		} catch (KeeperException e) {
			logger.error("創建節點失敗:" + e.getMessage() + ",path:" + path  ,e);
		}
		return false;
	}

Zookeeper對於事務性的支持 

       它對於事務性的支持主要依賴於四個函數支持主要依賴於四個函數,zoo_create_op_initzoo_delete_op_initzoo_set_op_init以及zoo_check_op_init。每一個函數都會在客戶端初始化一個operation,客戶端程序有義務保留這些operations。當準備好一個事務中的所有操作後,可以使用zoo_multi來提交所有的操作,由zookeeper服務來保證這一系列操作的原子性。也就是說只要其中有一個操作失敗了,相當於此次提交的任何一個操作都沒有對服務端的數據造成影響。Zoo_multi的返回值是第一個失敗操作的狀態信號。

        Zookeeper通過版本號來保證操作的實效性。zoo_set的最後一個形參就是version number。如果提交的數和服務端在該節點的版本號對不上,那麼此次設值操作就失敗了。這可以解決這樣的情況:client1.get, client2.get, client2.set, client1.set。從client1的角度出發,set和get之前操作的變量值已經發生改變了,zookeeper非常負責的保證的最後一次set將不會成功,客戶端可以重新get, set一次。

Zookeeper查看事物日誌

zookeeper的事務日誌通過zoo.cfg文件中的dataLogDir配置項配置,文件如下:


zookeeper提供了查看事務日誌的工具類LogFormatter ,運行:

java -classpath .:slf4j-api-1.6.1.jar:zookeeper-3.4.5.jar org.apache.zookeeper.server.LogFormatter /export1/zookeeper/logs/version-2/log.1000003d2 

將看到格式化的日誌內容類似:
    ZooKeeper Transactional Log File with dbid 0 txnlog format version 2  
    5/12/14 7:06:31 PM CST session 0x145ef610b530000 cxid 0x229e zxid 0x1000003e8 delete '/hbase/region-in-transition/e3d18e83988febe321e563f085210127  
    5/12/14 7:06:31 PM CST session 0x245ef610b52004c cxid 0x0 zxid 0x1000003e9 createSession 40000  
    5/12/14 7:06:31 PM CST session 0x245ef610b52004c cxid 0x3 zxid 0x1000003ea closeSession null  
    5/12/14 7:06:31 PM CST session 0x545f01346c60001 cxid 0x0 zxid 0x1000003eb createSession 40000  
    5/12/14 7:06:31 PM CST session 0x545f01346c60001 cxid 0x3 zxid 0x1000003ec closeSession null  
    5/12/14 7:06:32 PM CST session 0x445f018d5e00002 cxid 0x0 zxid 0x1000003ed createSession 40000  
    5/12/14 7:06:32 PM CST session 0x445f018d5e00002 cxid 0x3 zxid 0x1000003ee closeSession null  
    5/12/14 7:06:32 PM CST session 0x145ef610b530000 cxid 0x22a1 zxid 0x1000003ef setData '/hbase/table/wyp,#ffffffff000146d61737465723a36303030303b36ffffffc765ffffffb0ffffffa5ffffffa3745042554681,5  
    5/12/14 7:06:32 PM CST session 0x145ef610b530000 cxid 0x22a3 zxid 0x1000003f0 delete '/hbase/table-lock/wyp/write-master:600000000000002  
    5/12/14 7:06:32 PM CST session 0x145ef610b530033 cxid 0x0 zxid 0x1000003f1 createSession 40000  
    5/12/14 7:06:32 PM CST session 0x145ef610b530033 cxid 0x3 zxid 0x1000003f2 closeSession null  
LogFormatter 類代碼如下:
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.zookeeper.server;

import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import java.util.zip.Adler32;
import java.util.zip.Checksum;

import org.apache.jute.BinaryInputArchive;
import org.apache.jute.InputArchive;
import org.apache.jute.Record;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zookeeper.server.persistence.FileHeader;
import org.apache.zookeeper.server.persistence.FileTxnLog;
import org.apache.zookeeper.server.util.SerializeUtils;
import org.apache.zookeeper.txn.TxnHeader;

public class LogFormatter {
    private static final Logger LOG = LoggerFactory.getLogger(LogFormatter.class);

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("USAGE: LogFormatter log_file");
            System.exit(2);
        }
        FileInputStream fis = new FileInputStream(args[0]);
        BinaryInputArchive logStream = BinaryInputArchive.getArchive(fis);
        FileHeader fhdr = new FileHeader();
        fhdr.deserialize(logStream, "fileheader");

        if (fhdr.getMagic() != FileTxnLog.TXNLOG_MAGIC) {
            System.err.println("Invalid magic number for " + args[0]);
            System.exit(2);
        }
        System.out.println("ZooKeeper Transactional Log File with dbid "
                + fhdr.getDbid() + " txnlog format version "
                + fhdr.getVersion());

        int count = 0;
        while (true) {
            long crcValue;
            byte[] bytes;
            try {
                crcValue = logStream.readLong("crcvalue");

                bytes = logStream.readBuffer("txnEntry");
            } catch (EOFException e) {
                System.out.println("EOF reached after " + count + " txns.");
                return;
            }
            if (bytes.length == 0) {
                // Since we preallocate, we define EOF to be an
                // empty transaction
                System.out.println("EOF reached after " + count + " txns.");
                return;
            }
            Checksum crc = new Adler32();
            crc.update(bytes, 0, bytes.length);
            if (crcValue != crc.getValue()) {
                throw new IOException("CRC doesn't match " + crcValue +
                        " vs " + crc.getValue());
            }
            TxnHeader hdr = new TxnHeader();
            Record txn = SerializeUtils.deserializeTxn(bytes, hdr);
            System.out.println(DateFormat.getDateTimeInstance(DateFormat.SHORT,
                    DateFormat.LONG).format(new Date(hdr.getTime()))
                    + " session 0x"
                    + Long.toHexString(hdr.getClientId())
                    + " cxid 0x"
                    + Long.toHexString(hdr.getCxid())
                    + " zxid 0x"
                    + Long.toHexString(hdr.getZxid())
                    + " " + TraceFormatter.op2String(hdr.getType()) + " " + txn);
            if (logStream.readByte("EOR") != 'B') {
                LOG.error("Last transaction was partial.");
                throw new EOFException("Last transaction was partial.");
            }
            count++;
        }
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章