-
Notifications
You must be signed in to change notification settings - Fork 41.5k
Closed
Description
Description:
Spring webflux internals break during devtools restart when WebClient bean is customized with a custom client connector.
spring boot version: Tested on 2.1.7.RELEASE and 2.2.0.M5 with a fresh kotlin start.spring.io project:
Only added 2 files to what was produced by start.spring.io:
Controller (to show failure case):
@Controller
class TestController(
private val webClient: WebClient
) {
@GetMapping("/test")
fun test(): Mono<String> {
val result = webClient.method(HttpMethod.GET).uri(URI.create("https://postman-echo.com/get?a=42")).retrieve()
return result.bodyToMono(String::class.java)
}
}
Configuration:
@Configuration
class TestConfiguration {
@Bean
fun webClient(webClientBuilder: WebClient.Builder): WebClient {
val tcpClient = TcpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
.doOnConnected {
it.addHandlerLast(ReadTimeoutHandler(60))
}
val connector = ReactorClientHttpConnector(HttpClient.from(tcpClient))
return webClientBuilder.clientConnector(connector).build()
}
}
Replication Steps:
- Create the simple application as described above.
- Hit
localhost:8080/test
. The first time, you will see the data come in. - Change the url in the controller, and rebuild the project.
- Hit
localhost:8080/test
again. This time, there will be a netty error:
2019-08-13 11:21:03.925 ERROR 14689 --- [ctor-http-nio-2] a.w.r.e.AbstractErrorWebExceptionHandler : [68419476] 500 Server Error for HTTP GET "/test"
java.util.concurrent.RejectedExecutionException: event executor terminated
at io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:855) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to GET https://postman-echo.com/get?a=322 [DefaultWebClient]
|_ checkpoint ⇢ Handler com.test.test.TestController#test() [DispatcherHandler]
|_ checkpoint ⇢ HTTP GET "/test" [ExceptionHandlingWebHandler]
Stack trace:
at io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:855) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:340) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:333) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:766) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:472) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:87) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:81) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at reactor.netty.resources.ColocatedEventLoopGroup.register(ColocatedEventLoopGroup.java:69) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:322) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.bootstrap.Bootstrap.doResolveAndConnect(Bootstrap.java:159) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:120) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at reactor.netty.resources.PooledConnectionProvider$PooledConnectionAllocator.lambda$connectChannel$0(PooledConnectionProvider.java:248) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3920) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribeWith(Mono.java:4030) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3899) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3835) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.netty.internal.shaded.reactor.pool.SimplePool.drainLoop(SimplePool.java:200) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.internal.shaded.reactor.pool.SimplePool.drain(SimplePool.java:169) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.internal.shaded.reactor.pool.SimplePool.doAcquire(SimplePool.java:129) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.internal.shaded.reactor.pool.AbstractPool$Borrower.request(AbstractPool.java:334) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.onSubscribe(PooledConnectionProvider.java:503) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.internal.shaded.reactor.pool.SimplePool$QueueBorrowerMono.subscribe(SimplePool.java:324) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.resources.PooledConnectionProvider.disposableAcquire(PooledConnectionProvider.java:212) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.resources.PooledConnectionProvider.lambda$acquire$2(PooledConnectionProvider.java:166) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.lambda$subscribe$0(HttpClientConnect.java:332) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3920) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxRetryPredicate$RetryPredicateSubscriber.resubscribe(FluxRetryPredicate.java:124) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoRetryPredicate.subscribeOrReturn(MonoRetryPredicate.java:51) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:46) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:335) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:149) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1582) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:240) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:73) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:203) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:203) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1582) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen$ThenAcceptInner.onNext(MonoIgnoreThen.java:296) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2138) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen$ThenAcceptInner.onSubscribe(MonoIgnoreThen.java:285) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:160) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:52) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:46) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:149) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:203) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2138) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:137) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1946) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1820) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:171) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3920) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.Mono.subscribe(Mono.java:3920) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:56) ~[reactor-core-3.3.0.M3.jar:3.3.0.M3]
at reactor.netty.http.server.HttpServerHandle.onStateChange(HttpServerHandle.java:64) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.tcp.TcpServerBind$ChildObserver.onStateChange(TcpServerBind.java:226) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:436) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:91) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:161) ~[reactor-netty-0.9.0.M3.jar:0.9.0.M3]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328) ~[netty-codec-4.1.38.Final.jar:4.1.38.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302) ~[netty-codec-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511) ~[netty-transport-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.38.Final.jar:4.1.38.Final]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_212]
A temporary workaround is to not specify a custom client connector, but it'd be nice to be able to keep so we can hook in handlers, etc.
Thanks in advance!
Metadata
Metadata
Assignees
Labels
type: documentationA documentation updateA documentation update