Search code examples
jmeterjmsload-testingextendjmeter-plugins

JMeter custom sampler - share same connections with multiple runTest


I am successful in creating a custom sampler by extending AbstractJavaSamplerClient. In the sampler implementation I am making a JMS connection and sending message to a queue. This is working great. If I configure my Thread Group to 100 with Ramp-up 1, my sampler is pushing 100 messages.

Now what I wanted to do is to make the connection only once at the JMeter startup and then reuse the same connection to send messages on each run.

Can anyone explain how to create a connection at JMeter startup and then share the same connection with the sampler.

Note: I can't use the existing JMS publisher because I want to calculate my response time based on different application event not just calculating the time taken to publish the message to JMS.

Thanks in advance.


Solution

  • You can use testStarted method to initialize connections for all threads. Note that this method runs once, before threads are cloned, so in testStarted you need a connection pool, which threads then can take from. For example a very primitive connection pool would be a map with some sequential ID for a key, and connection object. Each thread would take one connection from that pool, based on thread number:

    So such simple pool could be initialized as:

    @Override
    public void testStarted()
    {
        int maxConnections = getThreadContext().getThreadGroup().getNumThreads(); 
    
        ConcurrentMap<Integer, Object> connections = new ConcurrentHashMap<Integer, Object>();
    
        for(int i = 0; i < maxConnections; i++)
        {
            Object connection = //... whatever you need to do to connect
            connections.put(new Integer(i), connection);
        }
    
        // Put in the context of thread group
        JMeterContextService.getContext().getVariables().putObject("MyConnections", connections);
    }
    

    (connection object could be a more specific type, based on your needs).

    Later you can use it in sample method:

    // Get connections pool from context
    ConcurrentMap<Integer, Object> connections = (ConcurrentHashMap<Integer, Object>) JMeterContextService.getContext().getVariables().getObject("MyConnections");
    // Find connection by thread ID, so each thread goes to a different connection
    connections.get(getThreadContext().getThreadNum());
    

    Here I naively assume a perfect mapping between thread number returned at run-time and initial sequential integer I used for connection initialization. Not the best assumption, could be improved, but it's a valid starting point.

    You can then close and remove connection in testEnded method. This method also runs once, so we close all connections:

    @Override
    public void testEnded()
    {
        for(Entry<Integer, Object> connection : connections.entrySet())
        {
            connection.close(); // or do whatever you need to close it
            connections.remove(connection.getKey());
        }
    }
    

    Or you could just call connections.clear() when all connections are closed.

    Disclosure: I did not test code in this answer directly, but used similar code fragments in the past, and reused them to answer this question. If you find any problems, feel free to update this answer.