+ 首页>>技能>>性能测试>>内容

技能Zookeeper读写性能测试500+次围观

关于Zookeeper,之前有过几篇文章已经介绍过了,因此本文不赘述。 本次小编对单机部署的Zookeeper的读、写进行了一次简单性能测试性能测试脚本由java完成,具体请看代码清单:

package com.kiven.test;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.log4j.PropertyConfigurator;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class Test{
    /**
     * server列表, 以逗号分割
     */
    protected static String hosts = "172.16.217.148:2181";
    /**
     * 连接的超时时间, 毫秒
     */
    private final int SESSION_TIMEOUT = 5000;
    private CountDownLatch connectedSignal = new CountDownLatch(1);
    protected static ZooKeeper zk;
    private static String nodePath = "/Test/test1";

    //static String data = "a very long string about data to set to zookeeper";
    static int threads = 10;    //线程数
    static int runs = 1000;     //迭代次数
    static int start = 0;       //none

    static int size = 1024*4;   //写入数据的大小,单位:字节
    static byte[] testdata;     //测试数据

    public static void main(String[] args) throws Exception {
    PropertyConfigurator.configure("log4j.properties");
    //生成写入的数据,大小size字节
    testdata = new byte[size];
    for(int i=0;i<size;i++){
        testdata[i] = 'A';
    }

    Test test = new Test();
    //连接
    test.connect();

    WorkerStat[] statArray = new WorkerStat[threads];
    Thread[] threadArray = new Thread[threads];

    WorkerStat mainStat = new WorkerStat();
    mainStat.runs = runs * threads;

    long begin = System.currentTimeMillis();
    for (int i = 0; i < threads; i++) {
        statArray[i] = new WorkerStat();
        statArray[i].start = start + i * runs;
        statArray[i].runs = runs;
        threadArray[i] = new SetterThread(statArray[i]);
        threadArray[i].start();
    }
    for (int i = 0; i < threads; i++) {
        threadArray[i].join();
    }
    mainStat.setterTime = System.currentTimeMillis() - begin;

    begin = System.currentTimeMillis();
    for (int i = 0; i < threads; i++) {
        threadArray[i] = new GetterThread(statArray[i]);
        threadArray[i].start();
    }
    for (int i = 0; i < threads; i++) {
        threadArray[i].join();
    }
    mainStat.getterTime = System.currentTimeMillis() - begin;

    WorkerStat totalStat = new WorkerStat();

    System.out.println("Test over!!");
    System.out.println("Thread("+threads+")\truns\tset time(ms)\tget time(ms)");

    for (int i = 0; i < threads; i++) {
        totalStat.runs = totalStat.runs + statArray[i].runs;
        totalStat.setterTime = totalStat.setterTime + statArray[i].setterTime;
        totalStat.getterTime = totalStat.getterTime + statArray[i].getterTime;
    }
    System.out.println("Total\t\t" + totalStat.runs + "\t"+ totalStat.setterTime + "\t\t" + totalStat.getterTime);
    System.out.println("Avg\t\t" + runs + "\t" + totalStat.setterTime/ threads + "\t\t" + totalStat.getterTime / threads);      
    System.out.println("TPS\t\t\t" + 1000 * totalStat.runs/ totalStat.setterTime + "\t\t" + 1000 * totalStat.runs/ totalStat.getterTime);
    System.out.println("\nMain\t\t" + mainStat.runs + "\t"+ mainStat.setterTime + "\t\t" + mainStat.getterTime);
    System.out.println("TPS\t\t" + 1000 * mainStat.runs/ mainStat.setterTime + "\t\t" + 1000 * mainStat.runs/ mainStat.getterTime);
    }

    private static class WorkerStat {
    public int start, runs;
    public long setterTime, getterTime;

    public WorkerStat() {
        start = runs = 0;
        setterTime = getterTime = 0;
    }
    }

    private static class SetterThread extends Thread {
    private WorkerStat stat;

    SetterThread(WorkerStat stat) {
        this.stat = stat;
    }

    public void run() {
        long begin = System.currentTimeMillis();
        for (int i = stat.start; i < stat.start + stat.runs; i++) {
            //写入节点数据
            try {
            zk.setData(nodePath, testdata, -1);
            } catch (Exception e) {
            e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        stat.setterTime = end - begin;
    }
    }

    private static class GetterThread extends Thread {
    private WorkerStat stat;
    GetterThread(WorkerStat stat) {
        this.stat = stat;
    }
    public void run() {
        long begin = System.currentTimeMillis();
        for (int i = stat.start; i < stat.start + stat.runs; i++) {
            //读取节点数据
            try {
            zk.getData(nodePath, false, null);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        }
        long end = System.currentTimeMillis();
        stat.getterTime = end - begin;
    }
    }

    //===============================================================================
    /**
     * 连接zookeeper server
     */
    public void connect() throws Exception {
        zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new ConnWatcher());
        // 等待连接完成
        connectedSignal.await();
    }

    /**
     * 
     * @author Kiven
     *
     */
    public class ConnWatcher implements Watcher{
        public void process(WatchedEvent event) {
            // 连接建立, 回调process接口时, 其event.getState()为KeeperState.SyncConnected
            if (event.getState() == KeeperState.SyncConnected) {
                // 放开闸门, wait在connect方法上的线程将被唤醒
                connectedSignal.countDown();
            }
        }
    }

    /**
        以下为各个参数的详细说明:
        path. znode的路径.
        data. 与znode关联的数据.
        acl. 指定权限信息, 如果不想指定权限, 可以传入Ids.OPEN_ACL_UNSAFE.
        指定znode类型. CreateMode是一个枚举类, 从中选择一个成员传入即可. 
    */

    /**
     * 创建持久化节点
     */
    public void create(String Path, byte[] data) throws Exception {
        zk.create(Path, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("创建节点:"+Path);
        System.out.println("=================");
    }

    /** 
     *  
     *获取节点信息 
     *@author kiven
     *@createDate 2013-01-16 15:17:22 
     *@param path 
     *@throws KeeperException 
     *@throws InterruptedException 
     */  
    public void getChild(String path) throws KeeperException, InterruptedException{     
        try{  
            List list=zk.getChildren(path, false);  
            if(list.isEmpty()){
                System.out.println(path+"中没有节点");
            }else{
                System.out.println(path+"中存在节点");  
                for(String child:list){
                    System.out.println("节点为:"+child);
                }
            }
        }catch (KeeperException.NoNodeException e) {  
            e.printStackTrace();
        }
    }

    /**
     * 设置节点数据
     * @throws Exception
     */
    public void setData(String path,String data) throws Exception{
        zk.setData(path, data.getBytes(), -1);
        System.out.println("set Data:"+"testSetData");
    }

    /**
     * 读取节点数据
     * @throws Exception
     */
    public void getData() throws Exception{
        System.out.println("get Data:");
        zk.getData(nodePath, false, null);
    }

    /**
     * 删除节点
     * @param path
     * @throws Exception
     */
    public void delete(String path) throws Exception{
        System.out.println("删除节点:"+path);
        //如果版本号与znode的版本号不一致,将无法删除,是一种乐观加锁机制;如果将版本号设置为-1,不会去检测版本,直接删除;
        zk.delete(path, -1);
    }

    /**
     * 关闭连接
     */
    public void close() {
        try {
            zk.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

相关功能代码中都已经注释清楚。我们看一下测试下来的结果:
写入数据:
Zookeeper读写性能测试

读取数据:
Zookeeper读写性能测试

由上两张图我们可以得知:

  • 1、写入(set)数据时,单个线程执行是最快的,而读取(get)数据则是4或5个线程并发是最快的。
  • 2、每个节点存储的数据不能超过1M(在Zookeeper默认配置的情况下,如果写入的数据为1M会报错,无法写入数据,所以所有应用写入的数据必须<1M)
  • 3、单个写入数据的体积越大,处理速度越慢,响应时间越长。
  • 4、会管应用中写入的数据最大的是16方画面合成的时候,此时写入的数据可能大于4K,另外16方画面合成时候zk节点存放的数据(json)是包含大量空格和换行的(在zk节点中,空格和换行也是占大小的),因此建议压缩json数据。
  • 5、测试过程中产生的log文件对磁盘的消耗和占用较大,建议定时删除历史log和shapshot

+ 猜你喜欢...

===== 关于 DiggerPlus =====
DiggerPlus是国内新锐测试人垂直内容博客,专注于挖掘测试工具,普及测试技术,专注于测试人能力提升,博客上线后就受到广大测试人的热烈追捧;此外,DiggerPlus深度整合评测资源,揭秘科技数据和真相,让读者更懂科技,更好地把玩科技。 我们始终保持"独立,客观,严谨,优秀"的优良作风,努力为读者带来源源不断的优质内容。想访问更多酷毙的测试人网站?赶紧来DiggerPlus测试人网址导航看看吧!

===== DiggerPlus Team =====
DiggerPlus Team是DiggerPlus官方发起的团队,这是一群热爱科技,热爱测试,喜欢深度挖掘的热血测试人,他们是评测师,测试专家。 我们欢迎优秀的测试人加入DiggerPlus Team。 加入DiggerPlus,可以成为我们的认证Dper,认证Dper拥有DiggerPlus独一无二的专栏并得到个人展示。

===== DiggerPlus 官方读者群 =====
DiggerPlus官方读者群(一)

+ 关于本文作者

我也很想让世界变得更美好,可是上帝却不开放源代码。

的专栏 | 专栏作者 | 访问Kiven的主页

+ 已有13个评论

Copyright © 2014 DiggerPlus. 95 queries in 0.894 seconds.
使用合作网站账号快速登录,更多精彩等着您: 开源中国