性能测试Jmeter压测ZooKeeper-自定义java请求

本文详细介绍如何使用Java自定义请求配合JMeter进行ZooKeeper的性能测试,包括Java操作ZooKeeper的基础代码示例,以及如何在JMeter中集成自定义的Java请求,实现ZooKeeper的压力测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   要想通过自定义java请求来压测ZooKeeper,那么我们就需要做两件事情,第一我们需要知道java如何操作ZooKeeper,第二就是怎么能将我们写的jar包让jmeter识别,首先我们先来干第一件事。

一、java操作ZooKeeper

以下的代码是网上百度找到的,经过了一点点修改(对于测试其它的可以找其它的测试代码)

package com.comtop.ZookApi;

import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

/**
 * Zookeeper base学习笔记
 */
public class ZookeeperBase {

    /** zookeeper地址 */
    static final String CONNECT_ADDR = "10.10.3.136:32372";
    /** session超时时间 */
    static final int SESSION_OUTTIME = 2000;// ms
    /** 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号 */
    static final CountDownLatch connectedSemaphore = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {

        ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME,
                new Watcher() {
                    public void process(WatchedEvent event) {
                        // 获取事件的状态
                        KeeperState keeperState = event.getState();
                        EventType eventType = event.getType();
                        // 如果是建立连接
                        if (KeeperState.SyncConnected == keeperState) {
                            if (EventType.None == eventType) {
                                // 如果建立连接成功,则发送信号量,让后续阻塞程序向下执行
                                System.out.println("zk 建立连接");
                                connectedSemaphore.countDown();
                            }
                        }
                    }
                });

        // 进行阻塞
        connectedSemaphore.await();

        System.out.println("..");
//         创建父节点
//         zk.create("/testRoot", "testRoot".getBytes(), Ids.OPEN_ACL_UNSAFE,
//         CreateMode.PERSISTENT);

//         创建子节点
//         zk.create("/testRoot/children", "children data".getBytes(),
//         Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // 获取节点洗信息
//         byte[] data = zk.getData("/testRoot/children", false, null);
//         System.out.println(new String(data));
//         System.out.println(zk.getChildren("/testRoot", false));
        // 修改节点的值
//         zk.setData("/testRoot/children", "modify data root".getBytes(), -1);
//         byte[] data = zk.getData("/testRoot/children", false, null);
//         System.out.println(new String(data));
// 如果存在节点则删除节点
         if (null != zk.exists("/testRoot/children", false)) {
             // 删除节点
             zk.delete("/testRoot/children", -1);
             System.out.println(zk.exists("/testRoot/children", false));
         }
        zk.close();

    }

}

 

二、自定义java请求

一、环境准备
        1、新建一个java工程
        2、导入jar包:ApacheJMeter_core.jar     ApacheJMeter_java.jar    (该包在本地C:\apache-jmeter-3.0\lib\ext下,当然路径取决于本地环境)
                这两个jar是使用jmeter最基础的jar,能够让你的代码在jmeter中运行起来,如果在写代码的过程中需要其他的jar,自行导入。
 
二、写代码前该知道的
        1、如果想要让你的代码在jmeter中运行起来,在创建类的时候需要去继承AbstractJavaSamplerClient抽象类或者是实现JavaSamplerClient接口
            AbstractJavaSamplerClient抽象类是JavaSamplerClient接口的子类,当你不需要复写所有的需要复写的方法时,那么你只需要去继承AbstractJavaSamplerClient抽象类即可。
 
            如果你选择了实现JavaSamplerClient接口,那么你需要复写的方法有:
                         public SampleResult runTest(JavaSamplerContext context) {}
                         public void setupTest(JavaSamplerContext context) {}
                         public void teardownTest(JavaSamplerContext context) {}
                         public Arguments getDefaultParameters() {}
            这4个方法就必须要复写,但是如果选择继承AbstractJavaSamplerClient这个抽象类,那么只需要复写你需要的方法即可。
 
        2、方法说明:
                    public Arguments getDefaultParameters() {}
                            这个方法由Jmeter在进行添加javaRequest时第一个运行,它决定了你要在GUI中默认显示出哪些属性。
                    public void setupTest(JavaSamplerContext context) {}
                            这个方法相当于loadrunner中的init,我们可以用它来进行一些初始化的动作。
                    public SampleResult runTest(JavaSamplerContext context) {}
                            这个方法相当于loadrunner中的action,我们的核心测试代码就在这里了。
                    public void teardownTest(JavaSamplerContext context) {}
                            这个方法相当于loadrunner中的end,收尾的工作可以由它来做。
 
        3、除了以上2点,我们一般还需要去实现Serializable,序列化标记接口,这样可以让我们的类去实现序列化。
 
整体代码如下:
package com.comtop.ZkApiJM;
 
 
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
 
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;

import java.util.concurrent.CountDownLatch;

import javax.management.RuntimeErrorException;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper.States;
import org.apache.zookeeper.ZooKeeper;

//public Arguments getDefaultParameters();设置可用参数及的默认值;
//public void setupTest(JavaSamplerContext arg0):每个线程测试前执行一次,做一些初始化工作;
//public SampleResult runTest(JavaSamplerContext arg0):开始测试,从arg0参数可以获得参数值;
//public void teardownTest(JavaSamplerContext arg0):测试结束时调用;

public class ZkjavaRequest extends AbstractJavaSamplerClient implements Serializable  {
 
    /** zookeeper地址 */
    private String conn_addr;    //存储用户输入的zk地址
//    测试用的数据
//    private static String conn_addr1;
//    private static String session_timeout1;
//    private static String zk_father1;
//    private static String zk_children1;
//    private static String zk_context1;
//    private static String resultData1;
    
    private static final String ConnAddrName="conn_addr" ;    //设置GUI页面显示的变量名称
    //设置GUI页面默认显示的变量值,默认值为空
    private static final String ConnAddrValueDefault="10.10.3.136:32372";
    
    /** session超时时间 */
    private String session_timeout;    //存储用户输入的session超时时间(单位ms)
    
    private static final String SessTimeName="session_timeout" ;    //设置GUI页面显示的变量名称
    //设置GUI页面默认显示的变量值,默认值为空
    private static final String SessTimeValueDefault="5000";

    //zk父节点
    private  String zk_father = "test";    //存储用户输入的zk父节点
    private static final String ZkFatherName="zk_father" ;    //设置GUI页面显示的变量名称
    //设置GUI页面默认显示的变量值,默认值为空
    private static final String ZkFatherValueDefault="/test";
    
    /** 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号 */
    static final CountDownLatch connectedSemaphore = new CountDownLatch(1);
    
    //zk连接对象
    private ZooKeeper zk;
    
    // resultData变量用来存储响应的数据,目的是显示到查看结果树中。
    private String resultData;

    // 测试代码
//    public static void main(String[] args) {
//        haha();
//    }
//
//    public static void haha(){
//        conn_addr1 = "10.10.3.136:32372";
//        session_timeout1 = "2000";
//        zk_father1 = "/test1";
//        zk_children1 = "/test2";
//        zk_context1 = "test2";
//        try {
//            ZooKeeper zk = new ZooKeeper(conn_addr1, Integer.valueOf(session_timeout1),
//                    new Watcher() {
//                        public void process(WatchedEvent event) {
//                            // 获取事件的状态
//                            KeeperState keeperState = event.getState();
//                            EventType eventType = event.getType();
//                            // 如果是建立连接
//                            if (KeeperState.SyncConnected == keeperState) {
//                                if (EventType.None == eventType) {
//                                    // 如果建立连接成功,则发送信号量,让后续阻塞程序向下执行
//                                    System.out.println("zk 建立连接");
//                                    connectedSemaphore.countDown();
//                                }
//                            }
//                        }
//                    });
//            connectedSemaphore.await();
//            // 如果存在父节点则不创建父节点
//             if (null == zk.exists(zk_father1, false)) {
//                 //               创建父节点
//               zk.create(zk_father1, zk_father1.getBytes(), Ids.OPEN_ACL_UNSAFE,
//               CreateMode.PERSISTENT);
//             }
//          // 创建子节点
//              zk.create(zk_father1+zk_children1, zk_context1.getBytes(),
//              Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//              // 获取节点洗信息
//            byte[] data = zk.getData(zk_father1+zk_children1, false, null);
//            resultData1 = new String(data);
//            System.out.println(new String(data));
//            System.out.println(zk.getChildren("/testRoot", false));
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
//
//}
  
    /*
     * 这个方法用来控制显示在GUI页面的属性,由用户来进行设置。
     * 此方法不用调用,是一个与生命周期相关的方法,类加载则运行。
     * (non-Javadoc)
     * @see org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient#getDefaultParameters()
     */
    
    @Override
    public Arguments getDefaultParameters() {
        System.out.println("读取属性值");
        Arguments params = new Arguments();
        params.addArgument("conn_addr",String.valueOf(ConnAddrValueDefault));
        params.addArgument("session_timeout", String.valueOf(SessTimeValueDefault));
        params.addArgument("zk_father", String.valueOf(ZkFatherValueDefault));
        params.addArgument("zk_children","");
        params.addArgument("zk_context","");
        return params;
    }
 
    /**
     * 初始化方法,初始化性能测试时的每个线程
     * 实际运行时每个线程仅执行一次,在测试方法运行前执行,类似于LoadRunner中的init方法
     */
 
    @Override
    public void setupTest(JavaSamplerContext jsc) {
        conn_addr = jsc.getParameter(ConnAddrName, ConnAddrValueDefault);
        session_timeout = jsc.getParameter(SessTimeName, SessTimeValueDefault);
        zk_father = jsc.getParameter(ZkFatherName, ZkFatherValueDefault);
        try {
            zk = new ZooKeeper(conn_addr, Integer.valueOf(session_timeout),
                    new Watcher() {
                        public void process(WatchedEvent event) {
                            // 获取事件的状态
                            KeeperState keeperState = event.getState();
                            EventType eventType = event.getType();
                            // 如果是建立连接
                            if (KeeperState.SyncConnected == keeperState) {
                                if (EventType.None == eventType) {
                                    // 如果建立连接成功,则发送信号量,让后续阻塞程序向下执行
                                    System.out.println("zk 建立连接");
                                    connectedSemaphore.countDown();
                                }
                            }
                        }
                    });
            if (States.CONNECTING == zk.getState()) { 
                connectedSemaphore.await();
            }
            // 如果存在父节点则不创建父节点
             if (null == zk.exists(zk_father, false)) {
                 //               创建父节点
               zk.create(zk_father, zk_father.getBytes(), Ids.OPEN_ACL_UNSAFE,
               CreateMode.PERSISTENT);
             }
        } catch (Exception e) {
            // TODO Auto-generated catch block
//            e.printStackTrace();
            throw new RuntimeException(e);
        }
        // 进行阻塞
//        System.out.println("..");
         
    }
 
    @Override
    public SampleResult runTest(JavaSamplerContext arg0) {
        String zk_children = arg0.getParameter("zk_children");    //获取zk_children
        String zk_context = arg0.getParameter("zk_context");    //获取zk_context
//        System.out.println(zk_context+" "+zk_children);
        /*
         * SampleResult这个类是用来将测试结果输出到查看结果树中的。  
         * 并且也是用来控制事务的开始和结束的。  
         */
    
        SampleResult results = new SampleResult();
        results.setSampleLabel("zk节点测试:"+zk_father);
 
        try{ 
            // 事务开始标记           
            results.sampleStart();
            // 创建子节点
            zk.create(zk_father+zk_children, zk_context.getBytes(),
                    Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            // 获取节点洗信息
            byte[] data = zk.getData(zk_father+zk_children, false, null);
//            System.out.println(new String(data));
            resultData = new String(data);
            if(null == resultData){
                results.setSuccessful(false);
                results.setResponseData("zk result is null",null);
                results.setDataType(SampleResult.TEXT);
            }
            results.setSuccessful(true);
        }catch(Exception e){
            results.setSuccessful(false);
            results.setResponseData(e.toString(),null);
            results.setDataType(SampleResult.TEXT);
            e.printStackTrace();
            return results;
        }finally{
            //标记事务结束
            results.sampleEnd();
        }
//        System.out.println("写入成功");
//        results.setSuccessful(true);
        results.setResponseData(resultData,null);//将数据打印到查看结果树当中
        results.setDataType(SampleResult.TEXT);
        return results;
    }

    /**
     * 测试结束方法,结束测试中的每个线程
     * 实际运行时,每个线程仅执行一次,在测试方法运行结束后执行,类似于Loadrunner中的End方法
     */
    
    public void teardownTest(JavaSamplerContext arg0) {
        try {
            zk.close();
            System.out.println("关闭");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
}
    

代码及依赖包如下:

 

转载于:https://www.cnblogs.com/leixiaobai/p/10245991.html

Version: Applicable to JMeter 2.8+(适用JMeter版本) 以下为官方说明: Prepare a simple IdGenerator For security reasons, ZK generates ID randomly. However to perform a record & reply test, it is mandatory to fix the IDs of the components. The IdGenerator here is used to create a predictable component id when testing. ———— 官方描述地址 http://blog.zkoss.org/index.php/2013/08/06/zk-jmeter-plugin/ 编译后的class文件,是需要配合jar包一起用的,用来自动关联uuid和data,jar包里面只解决了dtid。因为官方提供源码,需要自己编译后才可以用,我这里是已经编译好的class。(提供给那些没有条件编译的朋友) ———— 使用方法: 1.解后classes\foo\jmtest目录 直接覆盖在 被的ZK平台对应目录中(WEB-INF下)。 2.修改zk.xml文件,添加下面内容(WEB-INF下) foo.jmtest.SimpleIdGenerator 3.安装ZK的JMeter插件 把缩包中的 zk-jmeter-plugin-0.8.0.jar 文件,复制到 JMETER_HOME\lib\ext. ———— 安装完整,重新打开JMeter,可以添加ZK http 代理,录制脚本试试。 下面录制步骤: Prepare a Test Plan 1.Create a Thread Group in Test Group. 2.Add HTTP Cookie Manager in Thread Group 3.Add HTTP Request Defaults in Thread Group. 4.Add Transaction Controller in Thread Group. 5.Edit HTTP Request Defaults, set the Server Name and Port Number. 6.Create a ZK HTTP Proxy Server (Add > Non-Test Elements) in WorkBench, and edit it as follows Set Target Controller to Test Plan > Thread Group > Transaction Controller 7.Start the ZK HTTP Proxy Server
<think>嗯,用户想了解Java高并发技术的实现方式和最佳实践。首先,我需要回忆一下Java中处理高并发的常用方法。基础部分可能包括线程池、锁机制这些。然后,高级部分可能涉及无锁编程、并发容器,还有像CompletableFuture这样的工具。用户可能已经知道一些基本概念,但需要深入的最佳实践。比如,线程池的配置参数,如何避免死锁,或者如何选择合适的并发工具类。另外,分布式系统下的高并发处理,比如使用Redis或分库分表,这些也是重要的点。需要注意用户提到的“最佳实践”,所以应该给出具体的建议,比如使用线程池时不要用Executors直接创建,而是通过ThreadPoolExecutor自定义参数。还有锁的优化,比如缩小同步块的范围,或者使用读写锁。可能用户的实际应用场景是Web服务,处理大量并发请求,所以需要提到NIO、Netty这样的框架,以及Spring中的异步处理。另外,性能监控和试工具也是必不可少的,比如JMH和Arthas。 还要考虑Java新版本中的特性,比如虚拟线程在Java21中的引入,这可能是一个重要的点,可以提升并发性能。同时,内存模型和原子类的使用也是关键。需要确保回答结构清晰,分点说明,从基础到高级,再到分布式和工具支持。最后生成的相关问题要覆盖用户可能进一步询问的方向,比如具体技术的深入,性能优化,或者实际案例。</think>### Java高并发技术实现方式与最佳实践 #### 一、基础实现方式 1. **线程池管理** - 使用`ThreadPoolExecutor`自定义核心参数,避免直接使用`Executors`创建固定类型线程池 - 关键参数配置示例: ```java new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler) ``` - 推荐使用有界队列(如`ArrayBlockingQueue`)防止资源耗尽[^2] 2. **锁机制优化** - 优先使用`synchronized`的偏向锁/轻量级锁特性(JDK6+优化) - 明确锁范围:将同步代码块缩小到必要的最小范围 - 读写分离场景使用`ReentrantReadWriteLock` #### 二、高级并发工具 1. **无锁编程** - 原子类:`AtomicInteger`、`AtomicReference`等基于CAS实现 - `LongAdder`替代`AtomicLong`(高并发写场景性能更优)[^3] 2. **并发容器** - 使用`ConcurrentHashMap`代替同步的`HashMap` - 队列选择: - 单生产者-单消费者:`LinkedBlockingQueue` - 高吞吐场景:`Disruptor`环形队列 3. **异步编程** - `CompletableFuture`实现链式异步调用: ```java CompletableFuture.supplyAsync(() -> fetchData()) .thenApply(data -> process(data)) .exceptionally(ex -> handleError(ex)); ``` - Project Reactor响应式编程(Spring WebFlux支持) #### 三、分布式场景实践 1. **分布式锁** - Redis实现(Redlock算法) - ZooKeeper顺序节点方案 2. **流量控制** - 令牌桶算法:`RateLimiter`(Guava实现) - 滑动窗口计数(Redis+Lua实现) 3. **数据分片** - 数据库分库分表(ShardingSphere) - 缓存分片(Redis Cluster) #### 四、性能保障体系 1. **监控工具** - JDK Mission Control分析线程状态 - Arthas在线诊断 2. **工具** - JMH微基准- JMeter分布式 3. **JVM优化** - 设置合理的堆大小(避免频繁GC) - 选择低停顿垃圾收集器(ZGC/Shenandoah) #### 五、新特性应用 1. **虚拟线程(JDK21+)** ```java Thread.startVirtualThread(() -> { /* 任务逻辑 */ }); ``` 2. **结构化并发(JDK21+)** 通过`StructuredTaskScope`管理线程生命周期
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值