Do not block by default (#8580)

Currently, many timeouts in the project are like `-1` or other negative value
with a meaning to wait indefinitely.

According to distributed systems design and bad demo developing experience
it is not OK to block forever.

* Rework most of the timeouts in the framework to be `30` seconds.
Only one remained as `1` seconds is a `PollingConsumer` where it is
better to not block even for those 30 seconds when no messages in the queue,
but let the polling task be rescheduled.
* Remove the `MessagingGatewaySupport.replyTimeout` propagation down to the
`PollingConsumer` correlator where it was a `-1` before and blocked
the polling thread on the `Queue.poll()`.
This fixed the problem with a single thread in a pool for auto-configured `TaskScheduler`.
Now with 1 seconds wait time we are able to switch to other scheduled tasks
even with only 1 thread in the pool
This commit is contained in:
Artem Bilan
2023-03-21 17:43:00 -04:00
committed by GitHub
parent fcb06bac61
commit 1bec420fd1
58 changed files with 284 additions and 340 deletions

View File

@@ -431,7 +431,7 @@ It serves only as an indicator of whether to discard or send to the output or re
Optional (the default is `false`).
NOTE: This attribute might more properly be called `send-partial-result-on-timeout`, because the group may not actually expire if `expire-groups-upon-timeout` is set to `false`.
<9> The timeout interval to wait when sending a reply `Message` to the `output-channel` or `discard-channel`.
Defaults to `-1`, which results in blocking indefinitely.
Defaults to `30` seconds.
It is applied only if the output channel has some 'sending' limitations, such as a `QueueChannel` with a fixed 'capacity'.
In this case, a `MessageDeliveryException` is thrown.
For `AbstractSubscribableChannel` implementations, the `send-timeout` is ignored .

View File

@@ -80,7 +80,7 @@ Optional.
This attribute is not available inside a `Chain` element.
Optional.
<7> Specifies the maximum amount of time (in milliseconds) to wait when sending a reply message to the output channel.
Defaults to `-1` -- blocking indefinitely.
Defaults to `30` seconds.
This attribute is not available inside a `Chain` element.
Optional.
<8> Defines a poller.
@@ -150,7 +150,7 @@ This setting is useful when Message can be "`claimed`" only once.
It defaults to `false`.
Optional.
<8> Specifies the maximum amount of time (in milliseconds) to wait when sending a reply message to the output channel.
It defaults to `-1` -- blocking indefinitely.
It defaults to `30` seconds.
This attribute is not available inside a `Chain` element.
Optional.
<9> Defines a poller.

View File

@@ -328,7 +328,7 @@ Optional.
<9> Maximum amount of time in milliseconds to wait when sending a message to the channel, if the channel might block.
For example, a queue channel can block until space is available, if its maximum capacity has been reached.
Internally, the `send()` timeout is set on the `MessagingTemplate` and ultimately applied when invoking the send operation on the `MessageChannel`.
By default, the `send() timeout is set to '-1', which can cause the send operation on the `MessageChannel`, depending on the implementation, to block indefinitely.
By default, the `send()` timeout is set to '30'.
Optional.
<10> Boolean value indicating whether any payload that implements `Cloneable` should be cloned prior to sending the message to the request channel for acquiring the enriching data.
The cloned version would be used as the target payload for the ultimate reply.

View File

@@ -48,7 +48,7 @@ With this configuration defined, the `cafeService` can now be injected into othe
See the <<./samples.adoc#samples,"`Samples`">> Appendix for an example that uses the `gateway` element (in the Cafe demo).
The defaults in the preceding configuration are applied to all methods on the gateway interface.
If a reply timeout is not specified, the calling thread waits indefinitely for a reply.
If a reply timeout is not specified, the calling thread waits for a reply for 30 seconds.
See <<gateway-no-response>>.
The defaults can be overridden for individual methods.
@@ -480,14 +480,16 @@ This means that there might be a chance that a message that was initiated by a g
Some service activator method might result in an exception, thus providing no reply (as we do not generate null messages).
In other words, multiple scenarios can cause a reply message to never come.
That is perfectly natural in messaging systems.
However, think about the implication on the gateway method. The gateway's method input arguments were incorporated into a message and sent downstream.
However, think about the implication on the gateway method.
The gateway's method input arguments were incorporated into a message and sent downstream.
The reply message would be converted to a return value of the gateway's method.
So you might want to ensure that, for each gateway call, there is always a reply message.
Otherwise, your gateway method might never return and hang indefinitely.
Otherwise, your gateway method might never return and hang indefinitely if `reply-timeout` is set to negative value.
One way to handle this situation is by using an asynchronous gateway (explained later in this section).
Another way of handling it is to explicitly set the `reply-timeout` attribute.
Another way of handling it is to rely on a default `reply-timeout` as a `30` seconds.
That way, the gateway does not hang any longer than the time specified by the `reply-timeout` and returns 'null' if that timeout does elapse.
Finally, you might want to consider setting downstream flags, such as 'requires-reply', on a service-activator or 'throw-exceptions-on-rejection' on a filter. These options are discussed in more detail in the final section of this chapter.
Finally, you might want to consider setting downstream flags, such as 'requires-reply', on a service-activator or 'throw-exceptions-on-rejection' on a filter.
These options are discussed in more detail in the final section of this chapter.
NOTE: If the downstream flow returns an `ErrorMessage`, its `payload` (a `Throwable`) is treated as a regular downstream error.
If there is an `error-channel` configured, it is sent to the error flow.
@@ -841,7 +843,7 @@ You should understand that the reply message (if produced) is sent to a reply ch
===== Downstream Component Returns 'null'
Sync Gateway -- single-threaded::
If a component downstream returns 'null' and no `reply-timeout` has been configured, the gateway method call hangs indefinitely, unless a `reply-timeout` has been configured or the `requires-reply` attribute has been set on the downstream component (for example, a service activator) that might return 'null'.
If a component downstream returns 'null' and the `reply-timeout` has been configured to negative value, the gateway method call hangs indefinitely, unless the `requires-reply` attribute has been set on the downstream component (for example, a service activator) that might return 'null'.
In this case, an exception would be thrown and propagated to the gateway.
Sync Gateway -- multi-threaded::
The behavior is the same as the previous case.
@@ -849,7 +851,7 @@ The behavior is the same as the previous case.
===== Downstream Component Return Signature is 'void' While Gateway Method Signature Is Non-void
Sync Gateway -- single-threaded::
If a component downstream returns 'void' and no `reply-timeout` has been configured, the gateway method call hangs indefinitely unless a `reply-timeout` has been configured.
If a component downstream returns 'void' and the `reply-timeout` has been configured to negative value, the gateway method call hangs indefinitely.
Sync Gateway -- multi-threaded::
The behavior is the same as the previous case.
@@ -861,8 +863,9 @@ Sync Gateway -- multi-threaded::
The behavior is the same as the previous case.
IMPORTANT: You should understand that, by default, `reply-timeout` is unbounded.
Consequently, if you do not explicitly set the `reply-timeout`, your gateway method invocation might hang indefinitely.
Consequently, if you set the `reply-timeout` to negative value, your gateway method invocation might hang indefinitely.
So, to make sure you analyze your flow and if there is even a remote possibility of one of these scenarios to occur, you should set the `reply-timeout` attribute to a "'safe'" value.
It is `30` seconds by default.
Even better, you can set the `requires-reply` attribute of the downstream component to 'true' to ensure a timely response, as produced by the throwing of an exception as soon as that downstream component returns null internally.
However, you should also realize that there are some scenarios (see <<long-running-process-downstream,the first one>>) where `reply-timeout` does not help.
That means it is also important to analyze your message flow and decide when to use a synchronous gateway rather than an asynchronous gateway.
@@ -873,11 +876,6 @@ Likewise, when dealing with a Filter, you can set the `throw-exception-on-reject
In both of these cases, the resulting flow behaves like it contain a service activator with the 'requires-reply' attribute.
In other words, it helps to ensure a timely response from the gateway method invocation.
NOTE: `reply-timeout` is unbounded for `<gateway/>` elements (created by the `GatewayProxyFactoryBean`).
Inbound gateways for external integration (WS, HTTP, and so on) share many characteristics and attributes with these gateways.
However, for those inbound gateways, the default `reply-timeout` is 1000 milliseconds (one second).
If a downstream asynchronous hand-off is made to another thread, you may need to increase this attribute to allow enough time for the flow to complete before the gateway times out.
IMPORTANT: You should understand that the timer starts when the thread returns to the gateway -- that is, when the flow completes or a message is handed off to another thread.
At that time, the calling thread starts waiting for the reply.
If the flow was completely synchronous, the reply is immediately available.

View File

@@ -899,7 +899,7 @@ For the _HTTP Outbound Gateway_, the XML Schema defines only the _reply-timeout_
The _reply-timeout_ maps to the _sendTimeout_ property of the _org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler_ class.
More precisely, the property is set on the extended `AbstractReplyProducingMessageHandler` class, which ultimately sets the property on the `MessagingTemplate`.
The value of the _sendTimeout_ property defaults to "-1" and will be applied to the connected `MessageChannel`.
The value of the _sendTimeout_ property defaults to `30` seconds and will be applied to the connected `MessageChannel`.
This means, that depending on the implementation, the Message Channel's _send_ method may block indefinitely.
Furthermore, the _sendTimeout_ property is only used, when the actual MessageChannel implementation has a blocking send (such as 'full' bounded QueueChannel).

View File

@@ -985,7 +985,6 @@ Optional.
<3> Lets you specify how long this gateway waits for the reply message to be sent successfully before throwing an exception.
Keep in mind that, when sending to a `DirectChannel`, the invocation occurs in the sender's thread.
Consequently, the failing of the send operation may be caused by other components further downstream.
By default, the gateway waits indefinitely.
The value is specified in milliseconds.
Optional.
<4> Indicates whether this procedure's return value should be included.

View File

@@ -926,7 +926,6 @@ If this attribute is not defined, the request message must have a `replyChannel`
Optional.
<3> Specifies the time the gateway waits to send the result to the reply channel.
Only applies when the reply channel itself might block the send operation (for example, a bounded `QueueChannel` that is currently full).
By default, the gateway waits indefinitely.
The value is specified in milliseconds.
Optional.
====

View File

@@ -69,16 +69,15 @@ Optional.
<6> A reference to a `MessageGroupStore` that can be used to store groups of messages under their correlation key until they are complete.
Optional.
(The default is a volatile in-memory store.)
<7> Whether, upon the expiration of the group, the ordered group should be sent out (even if some of the messages are missing).
<7> Whether, upon the expiration of the group, the ordered group should be sent out (even if some messages are missing).
Optional.
(The default is false.)
See <<./aggregator.adoc#reaper,Managing State in an Aggregator: `MessageGroupStore`>>.
<8> The timeout interval to wait when sending a reply `Message` to the `output-channel` or `discard-channel`.
Defaults to `-1`, which blocks indefinitely.
It is applied only if the output channel has some 'sending' limitations, such as a `QueueChannel` with a fixed 'capacity'.
In this case, a `MessageDeliveryException` is thrown.
The `send-timeout` is ignored for `AbstractSubscribableChannel` implementations.
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expired task leads this task to be rescheduled.
Optional.
<9> A reference to a bean that implements the message correlation (grouping) algorithm.
The bean can be an implementation of the `CorrelationStrategy` interface or a POJO.

View File

@@ -380,7 +380,6 @@ This attribute defaults to `false`.
`timeout`::
The `timeout` attribute specifies the maximum amount of time in milliseconds to wait when sending messages to the target Message Channels.
By default, the send operation blocks indefinitely.
[[router-common-parameters-top]]
===== Top-Level (Outside a Chain)

View File

@@ -139,13 +139,12 @@ By default, the `send()` blocks for one second.
It applies only if the output channel has some 'sending' limitations -- for example, a `QueueChannel` with a fixed 'capacity' that is full.
In this case, a `MessageDeliveryException` is thrown.
The `send-timeout` is ignored for `AbstractSubscribableChannel` implementations.
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expired task leads this task to be rescheduled.
Optional.
<10> Lets you specify how long the scatter-gather waits for the reply message before returning.
By default, it waits indefinitely.
By default, it waits for `30` seconds.
'null' is returned if the reply times out.
Optional.
It defaults to `-1`, meaning to wait indefinitely.
<11> Specifies whether the scatter-gather must return a non-null value.
This value is `true` by default.
Consequently, a `ReplyRequiredException` is thrown when the underlying aggregator returns a null value after `gather-timeout`.
@@ -214,4 +213,3 @@ This way errors from the `AggregatingMessageHandler` are going to be propagated
For successful operation, a `gatherResultChannel`, `originalReplyChannel` and `originalErrorChannel` headers must be transferred back to replies from scatter recipient subflows.
In this case a reasonable, finite `gatherTimeout` must be configured for the `ScatterGatherHandler`.
Otherwise, it is going to be blocked waiting for a reply from the gatherer forever, by default.

View File

@@ -33,6 +33,9 @@ See <<./zip.adoc#zip,Zip Support>> for more information.
- The `MessageFilter` now emits a warning into logs when message is silently discarded and dropped.
See <<./filter.adoc#filter, Filter>> for more information.
- The default timeout for send and receive operations in gateways and replying channel adapters has been changed from infinity to `30` seconds.
Only one left as a `1` second is a `receiveTimeout` for `PollingConsumer` to not block a scheduler thread too long and let other queued tasks to be performed with the `TaskScheduler`.
[[x6.1-web-sockets]]
=== Web Sockets Changes