Skip to content
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

Spring webflux app consumes more resources than non-reactive equivalent app implementation [SPR-15783] #20338

Closed
spring-projects-issues opened this issue Jul 17, 2017 · 9 comments
Assignees
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jul 17, 2017

Ghislain Manib Mbogos opened SPR-15783 and commented

Scenario
For the sake of ascertaining the benefits of the reactive programming we have implemented a spring boot service which fetches data from a MongoDB and returns it via a REST API to the service consumers.
The Spring Boot service exists in two variants;

  1. A non-reactive implementation with spring boot , MongoRepository . This service returns the data as List<Data>
  2. A reactive implementation with spring boot, ReactiveMongoRepository, spring-boot-starter-webflux (Version 2.0.0.M1). This service returns the data as Flux<Data>.

In both implementations the rest controller directly fetches the data from the repository and returns it as List resp. as Flux. No further application logic is executed.

Observed Results
When executing a load/performance test with 100 users it appeared that the non-reactive implementation had a better HTTP throughput and it consumed less CPU and less Threads that the reactive implementation!

Expected Results
we anticipated the reactive version to scale with a small number of thread as mentioned in https://spring.io/blog/2016/07/28/reactive-programming-with-spring-5-0-m1

Example application
https://github.com/gma31/Showcase/tree/ECM-182/services
Information how to start the different service implementation and feed them with test data is provided in corresponding the README files


Attachments:

Issue Links:

0 votes, 6 watchers

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 18, 2017

Rossen Stoyanchev commented

#Ghislain Manib Mbogos, I started the reactiveflightinformation app and then tried to upload airport or airlines data as per the instructions. However i get the error:

java.lang.IllegalStateException: No multipart HttpMessageReader.
	at org.springframework.web.server.adapter.DefaultServerWebExchange.lambda$initMultipartData$3(DefaultServerWebExchange.java:141) ~[spring-web-5.0.0.RC1.jar:5.0.0.RC1]
	at java.util.Optional.orElseThrow(Optional.java:290) ~[na:1.8.0_77]
	at org.springframework.web.server.adapter.DefaultServerWebExchange.initMultipartData(DefaultServerWebExchange.java:141) ~[spring-web-5.0.0.RC1.jar:5.0.0.RC1]
	at org.springframework.web.server.adapter.DefaultServerWebExchange.<init>(DefaultServerWebExchange.java:106) ~[spring-web-5.0.0.RC1.jar:5.0.0.RC1]
        ...

This is not surprising because you don't have a multipart library on the classpath (the only one supported in WebFlux currently is Synchronoss NIO Multpiart). Also looking at the upload AirportController method it is using the MultipartFile abstraction used in Spring MVC with the Servlet API. The corresponding WebFlux abstraction is Part.

So at this point I have a basic question. How have you derived the attached data for the reactive equivalent which seems incomplete?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 19, 2017

Ghislain Manib Mbogos commented

Both services use the same mongo service (DB: test _and Collection: airport). Thus, once the data is uploaded as multipart via the non-reactive flightinfo service it is available in both services.
For the sake of completeness, I will now replace Multipart by the Part in the controllers methods.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 19, 2017

Rossen Stoyanchev commented

Okay I got it. I can move forward.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 19, 2017

Rossen Stoyanchev commented

From what I can see so far, the WebFlux app starts with 14 threads (4-5 due to Reactor Netty, the rest are Mongo related named Thread1, Thread2, etc.). After firing some requests at the server, the Reactor Netty thread count remains stable. The Mongo related thread count begins to grow and Mark Paluch is investigating.

That investigation aside, I have some more general comments.

First a return value of Flux<Airline> in combination with "application/json" behaves the same way as returning a List. The items are collected into a List and then rendered as a JSON array (in this case a large array of 5000+ Airlines) since that is the only way for a browser to read JSON which takes up more memory. Outside a browser you can switch to a streaming mode through "application/stream+json" either via a produces condition on the @RequestMapping or through an Accept header from the client side. From a browser use "text/event-stream".

Second in a simple scenario with fetching data + writing to a response over a fast network, the benefits of the reactive app will not stand out as much. You need to simulate some latency, e.g. HTTP clients that are slow to read the response data. That's where the difference becomes more dramatic because the reactive app never blocks and doesn't hold threads for example to write to the HTTP response.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jul 19, 2017

Mark Paluch commented

Mongo's async driver (the underlying driver for the ReactiveStreams driver) is configured by default to use AsynchronousSocketChannel with default settings. The driver does not use channel grouping, and the default behavior is to install an unbounded thread pool to process responses. The initial pool size matches the number of available processors, that's why you see Thread1…Thread2…ThreadN. Every incoming response requires a worker thread, and the number of threads grows with the number of concurrently used connections.

The used thread pool is not configurable via the driver. The driver allows configuration of the used stream factory, and there is an implementation available that uses netty (NettyStreamFactoryFactory). Using netty will give you a different performance profile as threads in netty's event loops are started lazily.

Regarding throughput, two things play into that:

  1. The Reactive MongoDB driver is in general roughly 20% slower than the blocking/imperative driver when benchmarking a simple find operation regardless the data volume returned by the query.
  2. Spring Data MongoDB itself has a quite static setup cost for reactive operations. Low-volume publishers yield about 25% less throughput, high-volume publishers only 4% less (compared to the blocking/imperative driver). This is because of the initial reactive sequence construction.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 1, 2017

Rossen Stoyanchev commented

I'm scheduling for RC4 since we're looking into this.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 1, 2017

Mark Paluch commented

I filed JAVA-2561 in MongoDB's issue tracker to address unlimited thread pool growth and configuration of a dedicated ExecutorService.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 18, 2017

Rossen Stoyanchev commented

Ghislain Manib Mbogos, to summarize what we have so far -- using a profiler tool shows the number of threads associated with HTTP connections (i.e. Reactor Netty threads) remains constant at 5-6 and all the other threads (Thread1, Thread2, etc) are related to the MongoDB driver for which there is now a ticket.

Do you have any remaining questions or can I close this ticket since there isn't anything specific to Spring WebFlux that we can do?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Sep 8, 2017

Rossen Stoyanchev commented

Resolving for now but feel free to comment further.

@spring-projects-issues spring-projects-issues added type: bug A general bug status: declined A suggestion or change that we don't feel we should currently apply in: data Issues in data modules (jdbc, orm, oxm, tx) in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues removed the type: bug A general bug label Jan 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

2 participants