Wednesday, June 1, 2011

RTMP sampler for JMeter to test flash with http

I want to have an integrated performance test environment for both HTTP and RTMP. For HTTP, JMeter is a well known tool, and has a lot of good features. But the problem is that it does not have RTMP sampler.

So I create the RTMP sampler for JMeter. I create the proxy methods using Java Sampler, and from those methods RTMPclient methods are invoked.

I use Red5 to create RTMPclient. There is a good tutorial for customizing the RTMPclient.

public class RTMPJavaSampler extends AbstractJavaSamplerClient {
 
    private static HashMap<String, MyRTMPClient> clientMap = new HashMap<String, MyRTMPClient>();

    public SampleResult runTest(JavaSamplerContext ctx) {
        JMeterVariables vars = JMeterContextService.getContext().getVariables();
        SampleResult sampleResult = new SampleResult();
        boolean ret = false;
        if (ctx.getParameter("MethodName").equals("connect")){
            ret = connect(ctx.getParameter("SessionId"), ctx.getParameter("HostIp"), ctx.getParameter("AppName"), ctx.getParameter("Arg1"), ctx.getParameter("Arg2"));
        } else if (ctx.getParameter("MethodName").equals("disconnect")){
            ret = disconnect(ctx.getParameter("SessionId"));
        }
        if (ret) {
            getLogger().info("Invoke " + ctx.getParameter("MethodName") + " done." );
            sampleResult.setResponseData("test success".getBytes());
            sampleResult.setDataType(SampleResult.TEXT);
            sampleResult.setSuccessful(true);
        } else {
            getLogger().error("Failed to invoke " + ctx.getParameter("MethodName") + ".");
            sampleResult.setResponseData("test failed".getBytes());
            sampleResult.setDataType(SampleResult.TEXT);
            sampleResult.setSuccessful(false);
        }

        sampleResult.setResponseCodeOK();
        sampleResult.setResponseMessageOK();
        return sampleResult;
    }

    @Override
    public Arguments getDefaultParameters() {
        Arguments params = new Arguments();
        params.addArgument("MethodName", "");
        params.addArgument("SessionId", "");
        params.addArgument("HostIp", "");
        params.addArgument("AppName", "");
        params.addArgument("Arg1","");
        params.addArgument("Arg2","");
        return params;
    }

    public boolean connect(String sessionId, String hostIp, String appName, String arg1, String arg2){
        MyRTMPClient client = new MyRTMPClient();  
        client.connect(hostIp, appName, arg1, arg2);
        clientMap.put(sessionId, client);

        while(client.getConnected()==null);
  
        return (client.getConnected());
    }
 
    public boolean disconnect(String sessionId){
        MyRTMPClient client = clientMap.remove(sessionId);
        client.disconnect();
        return true;
    }
}

public class MyRTMPClient extends RTMPClient implements IPendingServiceCallback, INetStreamEventHandler, IEventDispatcher, ClientExceptionHandler {
    private static final Logger logger = LoggingManager.getLoggerForClass();
    private Boolean connected;
    
    public MyRTMPClient(){
        setServiceProvider(this);
        setExceptionHandler(this);
        setStreamEventDispatcher(this);
    }
    
    public void resultReceived(IPendingServiceCall call) {
        Object result = call.getResult();
        if ("connect".equals(call.getServiceMethodName())) {
            if (result instanceof ObjectMap){
                @SuppressWarnings("unchecked")
                String code = ((ObjectMap<String, String>) result).get("code");
                if (StatusCodes.NC_CONNECT_SUCCESS.equals(code)){
                    logger.debug("Connection Success.");
                    connected=true;
                } else {
                    logger.error("resultsReceived: " + code);
                    connected=false;
                }
            }
        } else if ("createStream".equals(call.getServiceMethodName())){
            if (result instanceof Integer) {
                Integer streamIdInt = (Integer) result;
                // onStreamCreated(streamIdInt);
            } else {
                logger.error("Unexpected response for createStream " + result.toString());
            }
        } else {
            logger.info(call.getServiceMethodName());
        }
    }
 
    public void connect(String host, String app, String arg1, String arg2){
        Map<String, Object> defParams = makeDefaultConnectionParams(host, 1935, app);
        HashMap<String, Object>authParams = new HashMap<String, Object>();
        authParams.put("arg1", arg1);
        authParams.put("arg2", arg2);
        Object[] params = new Object[] {authParams};
        connect(host, 1935, defParams, this, params);
        logger.info("Connect done: " + app);
    }
 
    public void onStatus(Object obj){
        @SuppressWarnings("unchecked")
        ObjectMap<String, String> map = (ObjectMap<String, String>) obj;
        if ("status".equals(map.get("level"))){
            logger.debug("onStatus: " + obj.toString());
        } else {
            logger.info("onStatus: " + obj.toString());
        }
    }

    public void onMetaData(Object obj){
        logger.info("onMetaData: " + obj.toString());
    }
 
    public void onStreamEvent(Notify notify) {
        logger.info("onStreamEvent");
    }

    public void dispatchEvent(IEvent event) {
        if (!(event instanceof IRTMPEvent)) {  
            logger.debug("skipping non rtmp event: " + event);  
            return;  
        }  
        IRTMPEvent rtmpEvent = (IRTMPEvent) event;  

        if (rtmpEvent instanceof VideoData) {  
        } else if (rtmpEvent instanceof AudioData) {  
        } else if (rtmpEvent instanceof Notify){
        }        
    }

    public Boolean getConnected() {
        return connected;
    }
}

5 comments:

  1. Seems a very interesting solution!

    Can you tell me how to implement it?
    Where I have to save those classes?

    Cheers

    ReplyDelete
  2. See this page. It shows how to make a custom Java Sampler.

    ReplyDelete
  3. Is there a compiled version of these codes like a plugin?

    ReplyDelete
  4. any change of getting this as a jar?

    ReplyDelete
    Replies
    1. yah...any change of getting this as a jar?

      thanks

      Delete