Gatling JMS 场景不会终止

Posted

技术标签:

【中文标题】Gatling JMS 场景不会终止【英文标题】:Gatling JMS scenario does not terminate 【发布时间】:2022-01-18 19:30:21 【问题描述】:

我正在尝试通过 rabbitmq 代理加载测试一个简单的请求/回复场景。

import com.rabbitmq.jms.admin.RMQConnectionFactory;
import io.gatling.javaapi.core.ScenarioBuilder;
import io.gatling.javaapi.core.Simulation;
import io.gatling.javaapi.jms.JmsProtocolBuilder;

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import java.util.Collections;
import java.util.UUID;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.jms.JmsDsl.*;

public class ReplySimulation extends Simulation 

    ConnectionFactory connectionFactory = connectionFactory();

    JmsProtocolBuilder jmsProtocolBuilder = jms.connectionFactory( connectionFactory ).usePersistentDeliveryMode().listenerThreadCount( 1 );

    ScenarioBuilder scn = scenario( "Reply Test" ).repeat( 1 ).on(
        exec(
            jms( "request" )
                .requestReply()
                .queue( "requests" ).replyQueue( "loadtest-" + UUID.randomUUID() )
                .textMessage( "Message Body" )
                .jmsType( "textMessage" )
                .check( simpleCheck( message -> true ) ) ) );

    
        setUp( scn.injectOpen(atOnceUsers( 1 ) )).protocols( jmsProtocolBuilder );
    

    public ReplySimulation() throws JMSException 

    

    private ConnectionFactory connectionFactory() throws JMSException 

        RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
        connectionFactory.setUsername( "guest" );
        connectionFactory.setUsername( "guest" );
        connectionFactory.setUris( Collections.singletonList( "amqp://localhost:5672" ) );
        connectionFactory.setVirtualHost( "loadtest" );
        return connectionFactory;
    


此方案向定义的队列发送一条简单的文本消息,然后在定义的replyQueue 上等待答复。请求队列的另一端是简单的消息侦听器,它读取 JMSReplyTo 字段并将消息发送到该目的地。

在 rabbitmq UI 中,我可以看到有一条消息发布到回复队列,并且有一个消费者 ack。所以这告诉我往返已完成。

但由于某种原因,加特林场景一直在运行,给我这样的输出:



================================================================================
2021-12-15 16:38:01                                          15s elapsed
---- Requests ------------------------------------------------------------------
> Global                                                   (OK=0      KO=0     )


---- Reply Test ----------------------------------------------------------------
[--------------------------------------------------------------------------]  0%
          waiting: 0      / active: 1      / done: 0     
================================================================================

所以在我看来检查没有触发,否则它应该是 active: 0 / done: 1。我尝试让检查返回 falsenull,甚至抛出 RuntimeException 或添加错误输出。什么都没有改变,所以看起来检查确实没有被执行,或者各种输出都以某种方式被吞没了。

simulation.log 除了两个标题行之外不包含任何其他内容,并且在执行中添加 maxDuration 确实会停止该场景,但随后会失败并出现 ArithmeticException,因为发生被零除。

到目前为止,我发现的所有示例看起来都非常相似,所以我看不出有什么问题,但显然有些东西是行不通的。

编辑:这是场景中使用的回显服务

public class DeskclientEcho 

    private enum Parameter 
        CONNECTIONS, EXCHANGE, PASSWORD, QUEUE, SSL( 0 ), USER, VHOST;

        private final int argCount;

        Parameter() 

            this( 1 );
        

        Parameter( int argCount ) 

            this.argCount = argCount;
        
    

    private static final Map<Parameter, String> params = new HashMap<>();

    public static void main( String[] args ) throws JMSException, NoSuchAlgorithmException 

        for ( int i = 0; i < args.length; i++ ) 
            Parameter key = switch ( args[i] ) 
                case "-c", "--connections" -> Parameter.CONNECTIONS;
                case "-e", "--exchange" -> Parameter.EXCHANGE;
                case "-p", "--password" -> Parameter.PASSWORD;
                case "-q", "--queue" -> Parameter.QUEUE;
                case "-s", "--ssl" -> Parameter.SSL;
                case "-u", "--user" -> Parameter.USER;
                case "-v", "--vhost" -> Parameter.VHOST;
                default -> throw new IllegalArgumentException("Unknown parameter :" + args[i]);
            ;
            i += key.argCount;
            params.put( key, args[i] );
        

        RMQConnectionFactory factory = new RMQConnectionFactory();
        factory.setUris( Arrays.stream( params.get( Parameter.CONNECTIONS ).split( "," ) ).filter( Objects::nonNull ).toList() );

        if ( params.containsKey( Parameter.SSL ) ) 
            factory.useSslProtocol();
            factory.setUseDefaultSslContext( true );
            factory.setHostnameVerification( true );
        

        Optional.ofNullable(params.get(Parameter.USER)).ifPresent( factory::setUsername );
        Optional.ofNullable(params.get(Parameter.PASSWORD)).ifPresent( factory::setPassword );
        Optional.ofNullable(params.get(Parameter.VHOST)).ifPresent( factory::setVirtualHost );

        RMQDestination destination = new RMQDestination();
        destination.setAmqp( false );
        destination.setDestinationName( params.get( Parameter.QUEUE ) );
        destination.setAmqpExchangeName( params.get( Parameter.EXCHANGE ) );
        destination.setAmqpQueueName( params.get( Parameter.QUEUE ) );

        Connection connection = factory.createConnection();
        connection.start();
        Session session = connection.createSession( false, Session.AUTO_ACKNOWLEDGE );

        MessageConsumer consumer = session.createConsumer( destination );

        consumer.setMessageListener( message -> 
            System.out.println("Message received");
            try 
                Destination replyTo = message.getJMSReplyTo();
                for (int i = 0 ; i < 10 ; i++) 
                    session.createProducer( replyTo ).send( new RMQTextMessage() );
                
                System.out.println("Message sent");
             catch ( JMSException e ) 
                e.printStackTrace();
            

         );

    


有了这个pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>loadtest</groupId>
    <artifactId>deskclient-echo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq.jms</groupId>
            <artifactId>rabbitmq-jms</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>


</project>

作为代理,使用默认的 docker 镜像就足够了:

docker run -d rabbitmq

然后创建所需的虚拟主机或更改模拟以使用默认虚拟主机并启动回显应用程序。

模拟使用这个pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>loadtest</groupId>
    <artifactId>deskclient-requests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>io.gatling</groupId>
                <artifactId>gatling-maven-plugin</artifactId>
                <version>4.0.1</version>
                <configuration>
                    <simulationClass>ReplySimulation</simulationClass>
                </configuration>
            </plugin>
        </plugins>

    </build>


    <dependencies>
        <dependency>
            <groupId>io.gatling.highcharts</groupId>
            <artifactId>gatling-charts-highcharts</artifactId>
            <version>3.7.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.rabbitmq.jms</groupId>
            <artifactId>rabbitmq-jms</artifactId>
            <version>2.3.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

有人知道我在这里缺少什么吗?

【问题讨论】:

如果不提供示例 AMQP 应用程序进行测试,几乎不可能进行调查。你能考虑提供一个吗? @StéphaneLANDELLE 我添加了更多代码示例,如果您需要更多信息,请告诉我 【参考方案1】:

核心问题是您没有在回显服务中定义任何方式让 Gatling 关联出站消息(请求)和入站消息(响应)。

相反,您回复的是 new RMQTextMessage(),因此 JMSMessageID 完全不同,没有 JMSCorrelationID。

见https://gatling.io/docs/gatling/reference/current/jms/#other-options。

在您的情况下,您可能应该使用 matchByCorrelationId 并相应地传播它:

Message response = new RMQTextMessage();
response.setJMSCorrelationID(message.getJMSCorrelationID());
session.createProducer(replyTo).send(response);

注意:Gatling JMS 支持目前仅支持检查(请求、响应)对,因此无法像 for (int i = 0 ; i &lt; 10 ; i++) 循环那样检查一对多响应。

【讨论】:

非常感谢,ActiveMQ 示例现在可以使用它了!这有点令人困惑,因为文档中的示例没有使用任何匹配策略。再加上requestReply 默认为每次调用使用一个新的临时队列这一事实,我相信该检查将应用于收到的任何消息。这显然不是真的 ;-) 现实世界的应用程序填充消息 ID 或相关 ID,并且一些 JMS 提供程序会自动完成。

以上是关于Gatling JMS 场景不会终止的主要内容,如果未能解决你的问题,请参考以下文章

Gatling 场景响应时间

如何按顺序而不是同时运行gatling场景?

使用 Gatling 将场景模块化以按顺序运行

如何使用 gatling 进行 10000 QPS 的负载测试

负载测试应用程序调用外部 http 服务

Gatling执行官,有会议