隨筆-4  評論-10  文章-0  trackbacks-0
          2012年8月29日編寫   
          2012年8月29日修改
          1. 線程組,在我們測試方案里面,每個線程模擬一個用戶,執行用戶的登錄、等等等一系列的操作。由于我們的項目是長連接的,如何能實現多個sample公用一個長連接客戶端,考慮了很久,最后實現方法如下:
           1 package tea.client.network;
           2 /**
           3  * @author Teaey
           4  * @creation 2012-8-25
           5  */
           6 public class NetworkClientHolder
           7 {
           8     /**
           9      * 這里使用ThradLocal存儲BaseClient
          10      * 方便一輪測試的每個sample都是由同一個socketChannel發送
          11      * 更真實的模擬用戶
          12      */
          13     private static ThreadLocal<BaseClient> clientHolder = new ThreadLocal<BaseClient>();
          14     public static BaseClient getClient(String ip, String port)
          15     {
          16         BaseClient client = clientHolder.get();
          17         if (null == client)
          18         {
          19             client = new BaseClient(ip, port);
          20             client.connect();
          21             clientHolder.set(client);
          22         }
          23         return client;
          24     }
          25 }
          26 
          代碼中使用thread_local保存Socket客戶端,這樣每個sample中發送數據的客戶端都是從這里拿的,就可以保證長連接的情況下,socket不會重復創建,很好的模擬了用戶。
          當然不單單是鏈接可以保存,所有需要在線程中共享的數據都可以通過這種方法來實現。
          2. 接下來是如何封裝發送請求的客戶端,這里用的netty,具體可以根據項目情況使用mina或者nio都可以。代碼直接明了^_^:
            1 package tea.client.network;
            2 
            3 import java.net.InetSocketAddress;
            4 import java.util.concurrent.Executors;
            5 import org.jboss.netty.bootstrap.ClientBootstrap;
            6 import org.jboss.netty.channel.Channel;
            7 import org.jboss.netty.channel.ChannelFuture;
            8 import org.jboss.netty.channel.ChannelHandlerContext;
            9 import org.jboss.netty.channel.ChannelPipeline;
           10 import org.jboss.netty.channel.ChannelPipelineFactory;
           11 import org.jboss.netty.channel.ChannelStateEvent;
           12 import org.jboss.netty.channel.Channels;
           13 import org.jboss.netty.channel.ExceptionEvent;
           14 import org.jboss.netty.channel.MessageEvent;
           15 import org.jboss.netty.channel.SimpleChannelHandler;
           16 import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
           17 import tea.common.network.ClientDecoder;
           18 import tea.common.network.ClientEncoder;
           19 import tea.common.network.ClientMessage;
           20 
           21 /**
           22  * @author Teaey
           23  * @creation 2012-8-25
           24  */
           25 public class BaseClient
           26 {
           27     public BaseClient(String ip, String port)
           28     {
           29         this.ip = ip;
           30         this.port = port;
           31     }
           32     private String           ip;
           33     private String           port;
           34     private Channel          channel;
           35     private ClientBootstrap  bootstrap;
           36     private Object           syn             = new Object();
           37     private static final int Receive_Timeout = 10000;       //ms
           38     private ClientMessage    response        = null;
           39     public void connect()
           40     {
           41         bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
           42         bootstrap.setOption("tcpNoDelay", true);
           43         bootstrap.setPipelineFactory(new ClientPipelineFactory());
           44         while (true)
           45         {
           46             ChannelFuture future = bootstrap.connect(new InetSocketAddress(ip, Integer.parseInt(port)));
           47             future.awaitUninterruptibly(5000);
           48             if (future.isDone())
           49             {
           50                 channel = future.getChannel();
           51                 if (channel != null && channel.isConnected())
           52                 {
           53                     break;
           54                 }
           55             }
           56         }
           57     }
           58     public void disconnect()
           59     {
           60         if (channel.isConnected())
           61         {
           62             channel.disconnect();
           63         }
           64     }
           65     public boolean isConnected()
           66     {
           67         return channel.isConnected();
           68     }
           69     public void close()
           70     {
           71         if (this.channel.isOpen())
           72         {
           73             this.channel.close();
           74         }
           75         bootstrap.releaseExternalResources();
           76     }
           77     /**
           78      * 發送消息,無需返回
           79      */
           80     public void send(ClientMessage message)
           81     {
           82         channel.write(message);
           83     }
           84     /**
           85      * 發送消息,等待返回
           86      */
           87     public ClientMessage sendWaitBack(ClientMessage message)
           88     {
           89         response = null;
           90         try
           91         {
           92             channel.write(message);
           93             synchronized (syn)
           94             {
           95                 try
           96                 {
           97                     syn.wait(Receive_Timeout);
           98                 } catch (InterruptedException e)
           99                 {
          100                     e.printStackTrace();
          101                 }
          102             }
          103             if (null == response)
          104             {
          105                 System.err.println("Receive response timeout");
          106             }
          107         } catch (Exception e)
          108         {
          109             e.printStackTrace();
          110         }
          111         return response;
          112     }
          113     class ClientPipelineFactory implements ChannelPipelineFactory
          114     {
          115         public ChannelPipeline getPipeline() throws Exception
          116         {
          117             ChannelPipeline p = Channels.pipeline();
          118             p.addLast("frameDecoder", new ClientDecoder());
          119             p.addLast("fremeEncoder", new ClientEncoder());
          120             p.addLast("logicHandler", new ClientMsgHandler());
          121             return p;
          122         }
          123     }
          124     class ClientMsgHandler extends SimpleChannelHandler
          125     {
          126         public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
          127         {
          128             Object obj = e.getMessage();
          129             if (obj instanceof ClientMessage)
          130             {
          131                 ClientMessage msg = (ClientMessage) obj;
          132                 response = msg;
          133                 synchronized (syn)
          134                 {
          135                     syn.notifyAll();
          136                 }
          137             }
          138         }
          139         public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
          140         {
          141             System.out.println("connected server:" + ctx.getChannel());
          142         }
          143         public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
          144         {
          145             System.out.println("disconnected server:" + ctx.getChannel());
          146         }
          147         public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
          148         {
          149             System.out.println("Error in exceptionCaught:" + e.getCause());
          150         }
          151     }
          152 }
          153 
          這段代碼展示了我們的客戶端,這里所有的請求有兩種發送模式,一種是發送并阻塞等待返回(sendWaitBack
          ),第二種就是直接發送(send)。
          3. 有了發送請求的客戶端,那如何能夠更簡單的實現一個協議好讓客戶端發送,再貼一段代碼^_^:
            1 package tea.client.network;
            2 
            3 import org.apache.jmeter.config.Arguments;
            4 import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
            5 import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
            6 import org.apache.jmeter.samplers.SampleResult;
            7 import com.google.protobuf.InvalidProtocolBufferException;
            8 import com.google.protobuf.MessageLite;
            9 
           10 /**
           11  * @author Teaey
           12  * @creation 2012-8-25
           13  */
           14 public abstract class BaseSample extends AbstractJavaSamplerClient
           15 {
           16     public static final String PARAM_IP   = "ip";
           17     public static final String PARAM_PORT = "port";
           18     public static final String VAR_IP     = "${ip}";
           19     public static final String VAR_PORT   = "${port}";
           20     protected BaseClient       client;
           21     public void addParameter(Arguments params)
           22     {
           23     }
           24     /**
           25      * Jmeter獲取消息參數,默認配置ip和port兩個參數
           26      * 如果子類有更多參數,調用super.getDefaultParameters()獲取Arguments后,繼續設置其他方法
           27      */
           28     @Override
           29     public Arguments getDefaultParameters()
           30     {
           31         System.out.println("1.getDefaultParameters");
           32         Arguments params = new Arguments();
           33         params.addArgument(PARAM_IP, VAR_IP);
           34         params.addArgument(PARAM_PORT, VAR_PORT);
           35         addParameter(params);
           36         return params;
           37     }
           38     /**
           39      * runTest的前置方法
           40      */
           41     @Override
           42     public void setupTest(JavaSamplerContext context)
           43     {
           44         System.out.println("2.setupTest:" + context.containsParameter(PARAM_IP));
           45         String ip = context.getParameter(PARAM_IP);
           46         String port = context.getParameter(PARAM_PORT);
           47         this.client = NetworkClientHolder.getClient(ip, port);
           48         System.out.println("thread--->" + Thread.currentThread().getId() + " client--->" + client);
           49     }
           50     /**
           51      * Jmeter調用,用于實際的測試
           52      */
           53     @Override
           54     public SampleResult runTest(JavaSamplerContext context)
           55     {
           56         SampleResult sample = getSample();
           57         sample.sampleStart();
           58         try
           59         {
           60             MessageLite response = doTest();
           61             String msg = response == null ? "" : response.toString();
           62             sample.setResponseMessage(msg);
           63             sample.setSuccessful(true);
           64         } catch (Exception e)
           65         {
           66             sample.setSuccessful(false);
           67             e.printStackTrace();
           68         } finally
           69         {
           70             sample.sampleEnd();
           71         }
           72         return sample;
           73     }
           74     /**
           75      * 獲取本Sample的標簽,子類實現
           76      */
           77     public abstract String getLabel();
           78     /**
           79      * 獲取一個帶標簽的Sample 
           80      */
           81     public SampleResult getSample()
           82     {
           83         SampleResult sample = new SampleResult();
           84         sample.setSampleLabel(getLabel());
           85         return sample;
           86     }
           87     /**
           88      * Jmeter調用,用于
           89      */
           90     @Override
           91     public void teardownTest(JavaSamplerContext context)
           92     {
           93         System.out.println("4.teardownTest");
           94     }
           95     /**
           96      * 需實現,具體測試的方法,調用client的send/sendWithBack發送請求
           97      * 如無返回,放回null即可 
           98      */
           99     public abstract MessageLite doTest() throws InvalidProtocolBufferException;
          100 }
          好的,這里封裝了下AbstractJavaSamplerClient,每個消息默認包含ip和port參數,這可以再jmeter的用戶變量中定義好。為了方便大家添加消息的參數,這里實現了空的
          addParameter(Arguments params)方法,這樣在具體消息中直接重寫這個方法,來添加具體的參數。是不是很方便?^_^,具體協議還需要實現的兩個方法分別是:getLabel和doTest。第一個方法時用于報告顯示的請求名字,一般定義為消息名字+“Label”就OKay。第二個方法就是我們重點重寫的方法,這里再貼段代碼,是一個具體消息的實現:
           1 package tea.client;
           2 
           3 import com.google.protobuf.InvalidProtocolBufferException;
           4 import com.google.protobuf.MessageLite;
           5 import tea.client.network.BaseSample;
           6 import tea.common.network.ClientMessage;
           7 import tea.common.network.RPC.HeartBeat_C2S;
           8 import tea.common.network.RPC.HeartBeat_S2C;
           9 
          10 /**
          11  * @author Teaey
          12  * @creation 2012-8-24
          13  */
          14 public class HeartBeatSample extends BaseSample
          15 {
          16     @Override
          17     public MessageLite doTest() throws InvalidProtocolBufferException
          18     {
          19         HeartBeat_C2S.Builder request = HeartBeat_C2S.newBuilder();
          20         request.setTimestamp(System.currentTimeMillis());
          21         ClientMessage cm = new ClientMessage();
          22         cm.setContent(request.build().toByteArray());
          23         cm.setName("HeartBeat");
          24         ClientMessage sm = client.sendWaitBack(cm);
          25         HeartBeat_S2C response = HeartBeat_S2C.parseFrom(sm.getContent());
          26         return response;
          27     }
          28     @Override
          29     public String getLabel()
          30     {
          31         return "HeartBeatSample";
          32     }
          33 }
          34 
          可以看到doTest的工作就是封裝請求,并拿到父類的client發送,然后返回響應(send方式返回null),Okay,大功告成。
          要說的就是這么多,寫得不好但屬原創,體量作者轉載請標明出處。提意見請留言或者163郵箱:masfay,感謝。
          posted on 2012-08-29 11:44 沖杯茶喝 閱讀(7156) 評論(2)  編輯  收藏

          評論:
          # re: Jmeter“Java請求”使用總結 2015-10-11 17:35 | 打雜程序員
          看了你的blog,受益匪淺~! 有個問題請教你:如果是異步的請求,應該怎樣設置SampleResult? 比如我在runTest里發一個登陸請求,它的登陸回復應該是在messageReceived里,這時runTest函數要阻塞等待么?還是jmeter根本就不適用這種場景?  回復  更多評論
            
          # re: Jmeter“Java請求”使用總結 2015-10-12 12:18 | 沖杯茶喝
          @打雜程序員
          在登錄的Sample里,需要阻塞等待登錄返回的,Jmeter可以這么做,一般單獨作為一個sample,這樣也可以統計出登錄所消耗的時間  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 罗江县| 绩溪县| 都匀市| 纳雍县| 东方市| 临汾市| 毕节市| 得荣县| 泸州市| 高平市| 朝阳区| 普陀区| 出国| 陇川县| 沅陵县| 芜湖县| 瓦房店市| 琼中| 彭阳县| 东平县| 台北县| 米易县| 五莲县| 文山县| 庆元县| 博白县| 葫芦岛市| 施秉县| 峨山| 聊城市| 普宁市| 安塞县| 色达县| 碌曲县| 城市| 宁强县| 洛扎县| 虞城县| 会同县| 太仆寺旗| 昌黎县|