smallrye / smallrye-reactive-messaging

SmallRye Reactive Messaging
http://www.smallrye.io/smallrye-reactive-messaging/
Apache License 2.0
241 stars 179 forks source link

Slow RabbitMQ producer #1739

Open pstrumni opened 2 years ago

pstrumni commented 2 years ago

Hi, I'm trying to compare multiple frameworks (spring, micronaut and quarkus) by RabbitMQ integration support. I run simple benchmark which simply publish numbers from 0 to 100 000 . While spring and micronaut need max 2 seconds to publish messages, quarkus with smallrye-reactive-messaging need at least 10 seconds.

I have suspected, that it could be related with https://github.com/smallrye/smallrye-reactive-messaging/issues/220 issue, but setting waitForWriteCompletion=false doesn't change anything.

Sample code:

@Outgoing("numbers-out")
public Multi<String> numbers() {
  return Multi.createFrom().range(0, 100_000).map(String::valueOf);
}

and configuration:

rabbitmq-host=localhost
rabbitmq-port=5672
rabbitmq-username=myuser
rabbitmq-password=mypassword

mp.messaging.outgoing.numbers-out.connector=smallrye-rabbitmq
mp.messaging.outgoing.numbers-out.exchange.name=numbers
mp.messaging.outgoing.numbers-out.exchange.type=direct
mp.messaging.outgoing.numbers-out.default-routing-key=numbers-routing-key

I've tried also another piece of code, but also run really slow:

public void sendNumbers() {
    for (int i = 0; i < 100_000; ++i) {
      emitter.send(String.valueOf(i));
    }
}

I'm using quarkus version 2.6.1.Final with jdk11.

Why publishing messages using quarkus with smallrye is so slow?

cescoffier commented 2 years ago

You will have to tell us more. The emitter is using a buffer to handle back-pressure, so you are not sending immediately, but write it to a buffer and the connector poll the items from the buffer.

pstrumni commented 2 years ago

Thanks for reply. Could you tell me how to send messages immediately?

I have following code:

@ApplicationScoped
public class NumbersClient {

  @OnOverflow(OnOverflow.Strategy.NONE)
  @Channel("numbers-out")
  Emitter<String> numbersEmitter;

  public void send(final int i) {
    numbersEmitter.send(String.valueOf(i));
  }
}
@Path("/")
public class TestRestController {

  private final NumbersClient numbersClient;

  public TestRestController(final NumbersClient numbersClient) {
    this.numbersClient = numbersClient;
  }

  @Path("/test1")
  @GET
  public Response test1() {
    numbersClient.send(1);

    return Response.accepted().build();
  }
}

I run apache jmeter thread pool with size 100, each 1000 requests. Without @OnOverflow(OnOverflow.Strategy.NONE) it throws exception: SRMSG00034: Insufficient downstream requests to emit item

With @OnOverflow(OnOverflow.Strategy.NONE) it runs without exception, but very slow.

cescoffier commented 2 years ago

Yes, you can use an unbounded buffer (risky).

With 'none' it just blocks which is also wrong.

I would not use jmeter to do benchmark as it differs from coordinated omissions.

pstrumni commented 2 years ago

So there is no other way to simply send message to broker from non reactive code?

cescoffier commented 2 years ago

You can use any rabbitmq client directly, or the vertx rabbitmq client (it will work in native).

pstrumni commented 2 years ago

Why the buffer is being processed so slow? Can I do something to improve performance without switching to other library?

pstrumni commented 2 years ago

I've investigated why publishing messages by quarkus (with smallrye reactive messaging) is so slow and noticed that it uses only one channel to send messages to RabbitMQ while spring and micronaut use multiple channels (32 in micronaut and ~200 in spring).

Why don't you use multiple channels for publishing messages to RabbitMQ?

cescoffier commented 2 years ago

@kdubb any idea?

pstrumni commented 2 years ago

@cescoffier , @kdubb any updates?

cescoffier commented 10 months ago

@ozangunalp did a few experiment. Seems like it got fixed, but I will let him comment.