-
Notifications
You must be signed in to change notification settings - Fork 1
RabbitMQ messages loadbalancing #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3dbb780
701d5ca
41fab65
235bf33
661d6a5
3b351fa
7a31da1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package org.gridsuite.loadflow.server.config; | ||
|
|
||
| import org.springframework.amqp.rabbit.listener.MessageListenerContainer; | ||
| import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; | ||
| import org.springframework.cloud.stream.config.ListenerContainerCustomizer; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
|
|
||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
|
|
||
| @Configuration | ||
| public class RabbitConsumerConfig { | ||
| /* | ||
| * RabbitMQ consumer priority: | ||
| * https://www.rabbitmq.com/docs/consumer-priority | ||
| * | ||
| * Each container creates exactly one AMQP consumer with prefetch=1 and its own priority. | ||
| * When dispatching messages, RabbitMQ always selects the highest-priority consumer | ||
| * that is available. | ||
| */ | ||
| @Bean | ||
| public ListenerContainerCustomizer<MessageListenerContainer> customizer() { | ||
| /* | ||
| * Using AtomicInteger as in org/springframework/cloud/stream/binder/rabbit/RabbitMessageChannelBinder.java | ||
| * We expect cloud stream to call our customizer exactly once in order for each container so it will produce a sequence of increasing priorities | ||
| */ | ||
| AtomicInteger index = new AtomicInteger(); | ||
| return (container, destination, group) -> { | ||
| if (container instanceof SimpleMessageListenerContainer smlc && Objects.equals(group, "loadflowGroup")) { | ||
| smlc.setConsumerArguments(Map.of("x-priority", index.getAndIncrement())); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -545,9 +545,33 @@ public static String getNextLimitName(LimitViolationInfos limitViolationInfos, N | |
| return temporaryLimit != null ? temporaryLimit.getName() : null; | ||
| } | ||
|
|
||
| /* | ||
| * Spring Cloud Stream does not allow customizing each consumer within a single listener | ||
| * container (i.e. when concurrency = N) | ||
| * | ||
| * Since we need to customize each consumer individually, we simulate "concurrency = N" | ||
| * by creating N listener containers, each with concurrency = 1. | ||
| * | ||
| * This requires defining one Consumer bean per container, which explains | ||
| * the duplicated methods below. | ||
| */ | ||
| @Bean | ||
| @Override | ||
| public Consumer<Message<String>> consumeRun() { | ||
| public Consumer<Message<String>> consumeRun1() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment here to explain the design and why we do it
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
| return super.consumeRun(); | ||
| } | ||
|
|
||
| @Bean | ||
| public Consumer<Message<String>> consumeRun2() { | ||
| return super.consumeRun(); | ||
| } | ||
|
|
||
| @Bean | ||
| public Consumer<Message<String>> consumeRun3() { | ||
| return super.consumeRun(); | ||
| } | ||
|
|
||
| @Bean | ||
| public Consumer<Message<String>> consumeRun4() { | ||
| return super.consumeRun(); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,15 +4,25 @@ spring: | |
|
|
||
| cloud: | ||
| function: | ||
| definition: consumeRun;consumeCancel | ||
| definition: consumeRun1;consumeRun2;consumeRun3;consumeRun4;consumeCancel | ||
| stream: | ||
| bindings: | ||
| consumeRun-in-0: | ||
| # Spring Cloud Stream does not allow customizing each consumer within a single listener | ||
| # container (i.e. when concurrency = N) | ||
| # | ||
| # Since we need to customize each consumer individually, we simulate "concurrency = N" | ||
| # by creating N listener containers, each with concurrency = 1. | ||
| consumeRun1-in-0: &consumeRunConfig | ||
| destination: ${powsybl-ws.rabbitmq.destination.prefix:}loadflow.run | ||
| group: loadflowGroup | ||
| consumer: | ||
| concurrency: 4 | ||
| max-attempts: 1 | ||
| consumeRun2-in-0: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment here quickly reexplaining the design and why we do it this way
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
| <<: *consumeRunConfig | ||
| consumeRun3-in-0: | ||
| <<: *consumeRunConfig | ||
| consumeRun4-in-0: | ||
| <<: *consumeRunConfig | ||
| publishRun-out-0: | ||
| destination: ${powsybl-ws.rabbitmq.destination.prefix:}loadflow.run | ||
| publishResult-out-0: | ||
|
|
@@ -28,7 +38,8 @@ spring: | |
| output-bindings: publishRun-out-0;publishResult-out-0;publishCancel-out-0;publishStopped-out-0;publishCancelFailed-out-0 | ||
| rabbit: | ||
| bindings: | ||
| consumeRun-in-0: | ||
| # See comment on spring.cloud.stream.bindings.consumeRun1-in-0 | ||
| consumeRun1-in-0: &consumeRunRabbitConfig | ||
| consumer: | ||
| auto-bind-dlq: true | ||
| dead-letter-exchange: ${powsybl-ws.rabbitmq.destination.prefix:}loadflow.run.dlx | ||
|
|
@@ -37,6 +48,12 @@ spring: | |
| quorum: | ||
| enabled: true | ||
| delivery-limit: 2 | ||
| consumeRun2-in-0: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another comment here to see the previous one on the design
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
| <<: *consumeRunRabbitConfig | ||
| consumeRun3-in-0: | ||
| <<: *consumeRunRabbitConfig | ||
| consumeRun4-in-0: | ||
| <<: *consumeRunRabbitConfig | ||
| powsybl-ws: | ||
| database: | ||
| name: loadflow | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,10 @@ spring: | |
| hibernate: | ||
| #to turn off schema validation that fails (because of clob types) and blocks tests even if the the schema is compatible | ||
| ddl-auto: none | ||
| cloud: | ||
| function: | ||
| # disable consumeRun2/3/4 during test - all of them receive the "loadflowGroup" messages otherwise | ||
| definition: consumeRun1;consumeCancel | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HMm that's a shame. It's because of the testbinder instead of rabbitmq binder ? Maybe add more detail explaining this, and why it's good to just disable the other functions, say that the beans are created but not used, etc. Should we add back the concurrency 4 during test also or something else ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checking TestChannelBinderProvisioner.provisionConsumerDestination, ConsumerProperties as well as group seem to be ignored |
||
|
|
||
| logging: | ||
| level: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add comment here that we are inspired with the atomicinteger capture by the lambda by spring cloud stream when it sets the consumerTagPrefix of the different consumers of a container
Or at least a comment saying that we expect spring cloud stream to call our customizer (lambda) exactly once in order for each container so it will produce a sequence of increasing priorities
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed