Skip to content

Custom Performance Functions

Nitin Kumar Agarwal edited this page Jan 3, 2014 · 5 revisions

At some point in time you would realize that out of box performance functions may not be enough to get the work done. In such cases you would have to write User Defined Functions(UDFs) User defined functions are the basic building blocks of a run (test flow). Simplest example would be a functionality to make an HTTP call to a web-server.

1.1 Writing First UDF


In this example we will write a UDF which will give us a functionality to make HTTP Get call to a web-server. Later we will use this function to create a run and run it using loader.

Step 1: Prerequisites

Create a Maven Java project and add following repositories and dependencies in pom.xml. Please make sure of executing mvn clean compile package install at loader folder before proceeding further.

<dependency>
      <groupId>com.flipkart.perf</groupId>
      <artifactId>loader-core</artifactId>
      <version>1.0.0</version>
      <scope>provided</scope>
</dependency>

For packaging we can use the maven build plugin

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <excludeScope>provided</excludeScope>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
                        <phase>package</phase> <!-- bind to the packaging phase -->
                        <goals>
                            <goal>assembly</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Step 2: Write the class

For the HTTP calls we can use any HTTP client, here we will use ning client. Lets add dependency for that too.

<dependency>
    <groupId>com.ning</groupId>
    <artifactId>async-http-client</artifactId>
    <version>1.7.13</version>
</dependency>

By design every functionality will be a java class which needs to extend com.flipkart.perf.function.PerformanceFunction and override execute method of the base class. The real function of the class goes in this method.

package com.open.test;

import com.flipkart.perf.core.FunctionContext;
import com.flipkart.perf.function.PerformanceFunction;

public class TestHttpGet extends PerformanceFunction {

	@Override
	public void execute(FunctionContext context) throws Exception {
		// Actual functionality goes here, lets implement it.

	}

}

Lets implement.

package com.open.test;

import java.util.concurrent.Future;

import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
import com.flipkart.perf.core.FunctionContext;
import com.flipkart.perf.function.PerformanceFunction;

public class TestHttpGet extends PerformanceFunction {

	@Override
	public void execute(FunctionContext context) throws Exception {
		String url="http://127.0.0.1/index.html";
		AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
	    Future<Response> f = asyncHttpClient.prepareGet(url).execute();
	    Response r = f.get();
	}

}

Done. Now lets create a package and upload it on loader server and enable it to make HTTPGet calls. Run following command in your project directory

mvn clean compile package

It will clean the project, compile and create a jar. In target directory make sure you have two jar files, out of the two one will have “-jar-with-all-dependencies” as ending. If its not there something failed, you need to fix it.

Step3: Upload This Library on Loader Server Go to Upload Page by clicking on Upload menu. You would land onAlt Image

Browse and select *-jar-with-dependencies Alt Image

Click Upload

After a successful deployment it will show you the list of UDF’s added to the server.
![Alt Image](https://github.com/Flipkart/loader/raw/master/images/upload/UploadLib.png)

1.2 A Closer look on UDF


init() and end()

Other than execute method PerformanceFunction Class also provides two methods init() and end() which can be used as initialization and termination functions. They are executed only once per thread. Lets modify our class and move initialization steps to init.

package com.open.test;

import java.util.concurrent.Future;

import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
import com.flipkart.perf.core.FunctionContext;
import com.flipkart.perf.function.PerformanceFunction;

public class TestHttpGet extends PerformanceFunction {
	
	private String url;
	AsyncHttpClient asyncHttpClient;
	
	@Override
	public void init(FunctionContext context) throws Exception {
		url="http://127.0.0.1/index.html";
		asyncHttpClient = new AsyncHttpClient();
	}

	@Override
	public void execute(FunctionContext context) throws Exception {
	    Future<Response> f = asyncHttpClient.prepareGet(url).execute();
	    Response r = f.get();
	}

	@Override
	public void end(FunctionContext context) throws Exception {
		//Termination steps
	}

}

Similar to init if we have any termination steps we can move it to end() method.

FunctionContext

We can see all the functions in our UDF takes an object of FunctionContext as function parameter. FunctionContext is a wrapper around a Map which has the basic map functionality of saving key value pairs, with additional utility functions like getParameterAsString, getParameterAsMap etc. FunctionContext has two uses :

1.To Pass information among different functions in a group

Lets take a case where we have to make a http post call on a url with some data. In this case we will write two UDFs one to prepare data and one to use the data and make the post call. So first UDF prepared the data, now what?? Here FunctionContext comes in picture after preparing the data we can put it on context object as key value pair and in next udf we can retrieve the data to make the post call.

2.To Pass information on runtime

In our HTTPGet UDF we have hard coded the value of URL, what if we want to pass it at runtime, when I create my workflow. Here again we can use FunctionContext object. Since we haven’t yet created any run/workflow so for now lets see the run schema section where we are passing some key, value pairs. Later we will look in detail.

{
 "functionalityName":"TestHttpGet",
“functionClass”:”com.open.test.TestHttpGet”,
 "dumpData":true,
  "params":{“url”:”http://127.0.0.1/index.html” }
 }

in this function whatever key value we are passing through “params” will be available as key value pair on FunctionContext object. Assuming we are using above snippet in our actual run schema we can modify our init() method further.

@Override
public void init(FunctionContext context) throws Exception {
		url= context.getParameterAsString(“url”);
		asyncHttpClient = new AsyncHttpClient();
	}

description(), inputParameters(), outputParameters()

PerformanceFunction provides three methods which you can override to make your class more verbose, If we look at the functions image, the description, input parameters and output parameters are blank. We can override these methods to add more details. Lets do.

package com.open.test;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.Future;

import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
import com.flipkart.perf.core.FunctionContext;
import com.flipkart.perf.function.FunctionParameter;
import com.open.perf.function.PerformanceFunction;

public class TestHttpGet extends PerformanceFunction {
	
	private String url;
	AsyncHttpClient asyncHttpClient;
	
	@Override
	public void init(FunctionContext context) throws Exception {
		url="http://127.0.0.1/index.html";
		asyncHttpClient = new AsyncHttpClient();
	}

	@Override
	public void execute(FunctionContext context) throws Exception {
	    Future<Response> f = asyncHttpClient.prepareGet(url).execute();
	    Response r = f.get();
	}
	
	@Override
	public void end(FunctionContext context) throws Exception {
		//Termination steps
	}
	
	@Override
	public List<String> description() {
		return Arrays.asList(new String[]{"This method makes a get call for given url"});
	}
	
	@Override 
	public LinkedHashMap<String, FunctionParameter> inputParameters(){
		FunctionParameter url = new FunctionParameter();
		url.setDefaultValue("http://127.0.0.1/index.html").setDescription("makes a GET HTTP Call").
			setMandatory(true).setName("url");
        LinkedHashMap ip = new LinkedHashMap<String, FunctionParameter>();
        ip.put("url", url);
        return ip;
    }
	
	@Override
	public LinkedHashMap<String, FunctionParameter> outputParameters(){
		FunctionParameter status = new FunctionParameter();
		status.setDescription("HTTP Status Code").setMandatory(true).setName("Status Code");
		LinkedHashMap op = new LinkedHashMap<String, FunctionParameter>();
        op.put("Status", status);
        return op;
    }

}

Clone this wiki locally