INT-3433 Delay Failed IOs
JIRA: https://jira.spring.io/browse/INT-3433` If a read fails due to insufficient threads, delay the read for (default 100ms) - do not re-enable OP_READ until that time has elapsed. Avoids spinning the CPU. INT-3433 More Polishing If the assembler couldn't execute a new assembler after assembling the current message (when it detected there is more data), in the finally block it would "continue" only if the socket was still open. The test case closes the socket after sending 4 messages so when this condition occurred, the assembler failed to continue and data was left in the buffer. Remove the `isOpen()` check in the finally block and always continue if there's not another assembler running and there's data available. INT-3433 More Polishing We can still get starvation if the selector is in a long wait (in select()) when a read is delayed. Whenever a read is delayed, wake the selector so its next select will use the readDelay timeout. INT-3433 Reference Docs Also remove Thread.yield(). Revert redundant boolean return from `TcpNioConnection#checkForAssembler()`
This commit is contained in:
committed by
Artem Bilan
parent
69e20fc7db
commit
3c063a265b
@@ -1001,6 +1001,9 @@
|
||||
the <classname>CallerRunsPolicy</classname> (<code>CALLER_RUNS</code> when
|
||||
using the <code><task/></code> namespace) and the queue capacity is small.
|
||||
</para>
|
||||
<para>
|
||||
The following does not apply if you are not using a fixed thread pool.
|
||||
</para>
|
||||
<para>
|
||||
With NIO connections there are 3 distinct task types; the IO Selector processing
|
||||
is performed on one dedicated thread - detecting events, accepting new connections,
|
||||
@@ -1028,30 +1031,34 @@
|
||||
</para>
|
||||
<para>
|
||||
We must avoid the selector (or reader) threads performing the
|
||||
assembly task to avoid this deadlock.
|
||||
assembly task to avoid this deadlock. It is desirable to use seperate
|
||||
pools for the IO and assembly operations.
|
||||
</para>
|
||||
<para>
|
||||
Two classes are provided by the framework to avoid this problem. The
|
||||
<classname>CompositeExecutor</classname> allows the configuration
|
||||
The framework providers a
|
||||
<classname>CompositeExecutor</classname>, which allows the configuration
|
||||
of two distinct executors; one for performing IO operations, and
|
||||
one for message assembly. The <classname>CallerBlocksPolicy</classname>
|
||||
(which should be configured for the first task executors) will suspend
|
||||
the IO operation until an assembler thread is available (or a timeout
|
||||
occurs). In this environment, an IO thread can never
|
||||
one for message assembly. In this environment, an IO thread can never
|
||||
become an assembler thread, and the deadlock cannot occur.
|
||||
Example configuration of the composite executor is shown below. The
|
||||
<code>maxPoolSize</code> (or <code>queueCapacity</code>)
|
||||
of the assembler executor should be slightly
|
||||
larger than those on the IO executor.
|
||||
</para>
|
||||
<para>
|
||||
In addition, the task executors should be configured to use a
|
||||
<classname>AbortPolicy</classname> (ABORT when using <code><task></code>).
|
||||
When an IO cannot be completed, it is deferred for a short time and
|
||||
retried continually until it can be completed and an assembler
|
||||
allocated.
|
||||
</para>
|
||||
<para>
|
||||
Example configuration of the composite executor is shown below.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[@Bean
|
||||
private CompositeExecutor compositeExecutor() {
|
||||
ThreadPoolTaskExecutor ioExec = new ThreadPoolTaskExecutor();
|
||||
ioExec.setCorePoolSize(4);
|
||||
ioExec.setMaxPoolSize(8);
|
||||
ioExec.setMaxPoolSize(10);
|
||||
ioExec.setQueueCapacity(0);
|
||||
ioExec.setThreadNamePrefix("io-");
|
||||
ioExec.setRejectedExecutionHandler(new CallerRunsPolicy());
|
||||
ioExec.setRejectedExecutionHandler(new AbortPolicy());
|
||||
ioExec.initialize();
|
||||
ThreadPoolTaskExecutor assemblerExec = new ThreadPoolTaskExecutor();
|
||||
assemblerExec.setCorePoolSize(4);
|
||||
@@ -1062,6 +1069,14 @@ private CompositeExecutor compositeExecutor() {
|
||||
assemblerExec.initialize();
|
||||
return new CompositeExecutor(ioExec, assemblerExec);
|
||||
}]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<bean id="myTaskExecutor" class="org.springframework.integration.util.CompositeExecutor">
|
||||
<constructor-arg ref="io"/>
|
||||
<constructor-arg ref="assembler"/>
|
||||
</bean>
|
||||
|
||||
<task:executor id="io" pool-size="4-10" queue-capacity="0" rejection-policy="ABORT" />
|
||||
<task:executor id="assembler" pool-size="4-10" queue-capacity="0" rejection-policy="ABORT" />]]></programlisting>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="myTaskExecutor" class="org.springframework.integration.util.CompositeExecutor">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
|
||||
@@ -1070,9 +1085,7 @@ private CompositeExecutor compositeExecutor() {
|
||||
<property name="maxPoolSize" value="8" />
|
||||
<property name="queueCapacity" value="0" />
|
||||
<property name="rejectedExecutionHandler">
|
||||
<bean class="org.springframework.integration.util.CallerBlocksPolicy">
|
||||
<constructor-arg value="10000" />
|
||||
</bean>
|
||||
<bean class="java.util.concurrent.ThreadPoolExecutor.AbortPolicy" />
|
||||
</property>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
|
||||
Reference in New Issue
Block a user