spring-cloud / spring-cloud-gateway

An API Gateway built on Spring Framework and Spring Boot providing routing and more.
http://cloud.spring.io
Apache License 2.0
4.55k stars 3.33k forks source link

Support running the gateway with other reactive containers besides netty #145

Open muzuro opened 6 years ago

muzuro commented 6 years ago

I am trying to use tomcat instead netty. To achive this i have excluded netty from dependencies and included tomcat, so my build.gradle dependecies:

dependencies {
    compile (group: 'org.springframework.cloud', name: 'spring-cloud-starter-gateway', version: '2.0.0.M4') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-netty'
    }
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: '2.0.0.M7'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux'
}

Hower it is not enough, i get ClassCastException:

java.lang.ClassCastException: org.springframework.core.io.buffer.DefaultDataBufferFactory cannot be cast to org.springframework.core.io.buffer.NettyDataBufferFactory
    at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.lambda$filter$0(NettyWriteResponseFilter.java:61) ~[spring-cloud-gateway-core-2.0.0.M4.jar:2.0.0.M4]
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at reactor.core.publisher.Mono.subscribe(Mono.java:2913) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:167) ~[reactor-core-3.1.1.RELEASE.jar:3.1.1.RELEASE]

Is it possible to use tomcat with gateway instead netty? Thanks!

spencergibb commented 6 years ago

Not currently.

spencergibb commented 6 years ago

I already have code for this that just needs to be formalized. The difficulty will be running all the tests with other containers.

grku2opia commented 6 years ago

@spencergibb can u provide code ?

spencergibb commented 6 years ago

WebClientHttpRoutingFilter and WebClientWriteResponseFilter

ghost commented 6 years ago

@spencergibb How is the progress now? Thank you

spencergibb commented 6 years ago

None so far

ghoddg commented 6 years ago

Hi, I am also facing the same issue and the error is as below

java.lang.ClassCastException: org.springframework.core.io.buffer.DefaultDataBufferFactory cannot be cast to org.springframework.core.io.buffer.NettyDataBufferFactory at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.lambda$filter$0(NettyWriteResponseFilter.java:61) ~[spring-cloud-gateway-core-2.0.0.BUILD-SNAPSHOT.jar:2.0.0.BUILD-SNAPSHOT]

spencergibb commented 6 years ago

@ghoddg this is likely not going to happen in 2.0.0. For now you have to use netty.

ghoddg commented 6 years ago

Hi @spencergibb .. I am starting my api gateway service with tomcat. The service is started properly but when i hit api, it is giving me the above error

Also, my requirement is that I want to deploy it on weblogic as war and then also it should work is it possible?

spencergibb commented 6 years ago

No

ghoddg commented 6 years ago

@spencergibb Thanks for update.

ghoddg commented 6 years ago

@spencergibb , from above communication, i got understanding that, API Gateway Service with Spring Cloud Gateway will not work with Tomcat as well as Weblogic for now and it will work only with netty. is my understanding correct?

dharezlak commented 6 years ago

Hello, I have a similar problem without Tomcat nor Jetty in classpath. The error I am getting is the following:

java.lang.ClassCastException: org.springframework.core.io.buffer.DefaultDataBufferFactory cannot be cast to org.springframework.core.io.buffer.NettyDataBufferFactory
    at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.lambda$filter$0(NettyWriteResponseFilter.java:61) ~[spring-cloud-gateway-core-2.0.0.M6.jar:2.0.0.M6]
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.Mono.subscribe(Mono.java:3006) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:167) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.ignoreDone(MonoIgnoreThen.java:185) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreInner.onComplete(MonoIgnoreThen.java:234) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:245) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:130) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.FluxRetryPredicate$RetryPredicateSubscriber.onComplete(FluxRetryPredicate.java:107) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:140) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at reactor.ipc.netty.channel.PooledClientContextHandler.fireContextActive(PooledClientContextHandler.java:84) ~[reactor-netty-0.7.3.RELEASE.jar:0.7.3.RELEASE]
    at reactor.ipc.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:551) ~[reactor-netty-0.7.3.RELEASE.jar:0.7.3.RELEASE]
    at reactor.ipc.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:134) ~[reactor-netty-0.7.3.RELEASE.jar:0.7.3.RELEASE]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310) ~[netty-codec-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:297) ~[netty-codec-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:413) ~[netty-codec-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) ~[netty-codec-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1412) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:943) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:141) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459) ~[netty-transport-4.1.20.Final.jar:4.1.20.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886) ~[netty-common-4.1.20.Final.jar:4.1.20.Final]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_151]

Is there another dependency that could cause this problem?

My Spring boot 2.0.0.RC1 dependencies are the following:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-wiremock</artifactId>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <scope>test</scope>
</dependency>
dharezlak commented 6 years ago

I just noticed that this error occurs only in the testing mode. When run normally the gateway forwarding works fine.

dharezlak commented 6 years ago

Another thing I noticed is that when I use WebClient in tests instead of WebTestClient all is fine. Should using WebTestClient in spring-gateway based applications be prohibited?

spencergibb commented 6 years ago

It's not. It is used to test the gateway

dharezlak commented 6 years ago

@spencergibb It is indeed. The source of my problem was using the @AutoConfigureWebTestClient annotation. Without it my tests are working fine. When I use it I get the ClassCastException mentioned earlier. Do you think this calls for a separate issue?

drdamour commented 6 years ago

are u open to pr's for tomcat support? for 2.0 or 2.1

spencergibb commented 6 years ago

@drdamour I already have code https://github.com/spring-cloud/spring-cloud-gateway/issues/145#issuecomment-358772643. The trouble is testing everything.

drdamour commented 6 years ago

yeah i assumed you meant unit test and thus PR.

what type of testing are you referring to then?

spencergibb commented 6 years ago

Basically all the integration tests. Anything that spins up a container then makes real network calls thru the gateway.

drdamour commented 6 years ago

k...how are you hoping to structure the tests...duality for every netty test have a tomcat test. or just abstract away the backing implementation in all the tests?

or is there some prior art you are looking to follow?

spencergibb commented 6 years ago

My guess is rerunning all the tests with some system properties. Copied tests is prohibitive. Most have a base class already.

drdamour commented 6 years ago

how will that work with your maven circle build? just 2+ surefire/failsafe executions?

spencergibb commented 6 years ago

jenkins is our canonical build

drdamour commented 6 years ago

ok doesn't change anything right? still just calls maven goals (i'm assuming) and i'm guessing you want to test all reactive containers supported on each build?

spencergibb commented 6 years ago

There's another problem of including tomcat, jetty etc...

drdamour commented 6 years ago

def could make the netty container the default profile and have other profiles that bring in the others & exclude netty..but the build couldn't be done as one command AFAIK (don't think you can switch profiles/classpaths with extra executions of surefire or failsafe). Never tried though..maybe. could do the tests as other modules..but that'd either mean sharing a test jar from core or copy paste tests over.

good problem...

Yuzer-Ly commented 6 years ago

Maybe you can try to cancel the dependency ---providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')

SachinSharmaa commented 5 years ago

I am also facing the same issue and i do not want to migrate my services from tomcat to netty. Any idea, when can we expect the fix?

spencergibb commented 5 years ago

There is no timeline

SachinSharmaa commented 5 years ago

I need my gateway to support SSE. I am not sure if it is the right place to ask, but is it possible to enable SSE in spring cloud netflix zuul by using RxJava ?

spencergibb commented 5 years ago

This is not the place to ask questions about zuul

SachinSharmaa commented 5 years ago

Can i redirect to services running in tomcat from spring cloud gateway running in netty ?

spencergibb commented 5 years ago

Yes, just like you could use nginx or Apache to forward as well

crazythinking commented 5 years ago

same problem when i running in undertow.

spencergibb commented 5 years ago

No need for comments like that as Netty is the only supported runtime at the moment.

bidadh commented 5 years ago

I'm having the same issue when I try to test using spring-cloud-contract stub runners.

I believe SCC stub runner uses Wiremock underneath but have no idea what to do atm

Actually, I'm blocked testing my gateway using SCC Stub Runners

any idea?

ryanjbaxter commented 5 years ago

Not sure if "Writing Tests Section" in this guide helps https://spring.io/guides/gs/gateway/

If not you might want to open an issue in SCC.

bidadh commented 5 years ago

Well, as far as I can see in the logs and looking into the debug logs etc. SCC does his part with Wiremock and I can see it picks up the right response and returns that back but when it comes to the gateway I receive this error

bidadh commented 5 years ago

I guess SCG missing a filter, not sure but it could be. I might have to spend some time to see if I can create my own filter to resolve this but don't think so

spencergibb commented 5 years ago

The filters already exist (See WebClient*Filter).

bidadh commented 5 years ago

I cannot find any in 2.1.1.RELEASE

spencergibb commented 5 years ago

This mode is not supported yet, hence this issue, but they are there https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientHttpRoutingFilter.java

bidadh commented 5 years ago

Thanks @spencergibb adding this bean resolves 1 issue. the response status code is not 500 anymore but responseBody is null!

any ideas?

Actually, I see it ignored the body for GET

spencergibb commented 5 years ago

You need https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java as well

bidadh commented 5 years ago

Adding latter bean results in same 500 response as before

spencergibb commented 5 years ago

This mode is not supported yet

There are likely bugs.

bidadh commented 5 years ago

OK. thanks

any plan to support ?

spencergibb commented 5 years ago

When we do you'll see updates here