Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent embedded netty server from starting with spring-boot-starter-webflux?

Tags:

I want to establish a communication between a client and server application using Springs new reactive webflux extension.

For dependency management I use gradle. My build.gradle file on the server, as well as on the client side basically is:

buildscript {     repositories {         mavenCentral()         maven { url "https://repo.spring.io/snapshot" }     }     dependencies {         classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.BUILD-SNAPSHOT")     } }  repositories {     mavenCentral()     maven { url "https://repo.spring.io/snapshot" } }  apply plugin: 'java' apply plugin: 'org.springframework.boot' apply plugin: "io.spring.dependency-management"   dependencies {     compile("org.springframework.boot:spring-boot-starter-webflux") } 

(It should be noted that 2.0.0.BUILD-SNAPSHOT is a moving target and the problem at hand may just vanish one day due to changes inside the dependency)

When I start the server side application everything starts up well, including the start of an embedded netty server.

But when start the client application also a netty server is started, causing a "java.net.BindException: Address already in use", because the clientside netty server listens on the same port as the serverside netty server.

My question is: Why is netty started on the client side in the first place and how can I prevent it?

According to the Spring-Boot Documentation Spring tries to determine if Web support is required and configures the Spring Application context accordingly.

And according to the Docs this can be overridden by a call to setWebEnvironment(false). My client startup code then looks like:

@SpringBootApplication(scanBasePackages = { "com.tatics.flux.main" }) public class Client {     public static void main(String[] args) throws Exception {         SpringApplication app = new SpringApplication(Client.class);         app.setWebEnvironment(false);         app.run(Client.class, args);          WebClient webClient = WebClient.create();          Mono<String> result = webClient                 .post()                 .uri("http://localhost:8080/fluxService")                  // This does not work any more: .body("Hallo")                 // and must be replaced by:                 .body(BodyInserters.fromObject("Hallo"))                  .accept(MediaType.TEXT_PLAIN)                 .exchange()                 .flatMap(response -> response.bodyToMono(String.class));     } } 

Unfortunately netty is still started. Also I note that setWebEnvironment(false) is marked as deprecated.

Any help on how to prevent netty from starting but otherwise preserve all webflux-dependencies is appreciated.

Here is an excerpt from the auto-configuration Report:

========================= AUTO-CONFIGURATION REPORT =========================  Positive matches: ----------------- ...  ReactiveWebServerAutoConfiguration matched:   - found ReactiveWebApplicationContext (OnWebApplicationCondition)  ReactiveWebServerAutoConfiguration#defaultReactiveWebServerCustomizer matched:   - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.web.reactive.DefaultReactiveWebServerCustomizer; SearchStrategy: all) did not find any beans (OnBeanCondition)  ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration matched:   - @ConditionalOnClass found required class 'reactor.ipc.netty.http.server.HttpServer'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)   - @ConditionalOnMissingBean (types: org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition)  ReactorCoreAutoConfiguration matched:   - @ConditionalOnClass found required classes 'reactor.core.publisher.Mono', 'reactor.core.publisher.Flux'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)  ...  Negative matches: ----------------- ... ReactiveWebServerConfiguration.JettyAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'org.eclipse.jetty.server.Server' (OnClassCondition)  ReactiveWebServerConfiguration.TomcatAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'org.apache.catalina.startup.Tomcat' (OnClassCondition)  ReactiveWebServerConfiguration.UndertowAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'io.undertow.Undertow' (OnClassCondition)  ...  ReactiveWebServerConfiguration.JettyAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'org.eclipse.jetty.server.Server' (OnClassCondition)  ReactiveWebServerConfiguration.TomcatAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'org.apache.catalina.startup.Tomcat' (OnClassCondition)  ReactiveWebServerConfiguration.UndertowAutoConfiguration:   Did not match:      - @ConditionalOnClass did not find required class 'io.undertow.Undertow' (OnClassCondition) 
like image 327
Frank Kaiser Avatar asked Apr 23 '17 17:04

Frank Kaiser


People also ask

Does spring WebFlux use Netty?

Spring WebFlux is a part of the Spring framework and provides reactive programming support for web applications. If we're using WebFlux in a Spring Boot application, Spring Boot automatically configures Reactor Netty as the default server.

Can we override embedded server in spring boot?

The default Embedded Web Servers in Spring-Boot is Tomcat , but you can easily change it to others.


2 Answers

Addition to @Brian_Clozel answer:

You can disable Netty (or any other server) by specifying inside an application.yml:

spring.main.web-application-type: none 

or application.properties:

spring.main.web-application-type=none 
like image 132
Yan Pak Avatar answered Sep 30 '22 23:09

Yan Pak


The main issue with your code is that you're currently creating a SpringApplication, then you customize it - to finally drop everything and run the static method run(Object primarySource, String... args).

The following should work:

@SpringBootApplication public class Client {      public static void main(String[] args) throws Exception {         SpringApplication app = new SpringApplication(Client.class);         app.setWebApplicationType(WebApplicationType.NONE);         app.run(args);     }      @Bean     public CommandLineRunner myCommandLineRunner() {       return args -> {         // we have to block here, since command line runners don't         // consume reactive types and simply return after the execution         String result = WebClient.create("http://localhost:8080")                 .post()                 .uri("/fluxService")                 .body("Hallo")                 .accept(MediaType.TEXT_PLAIN)                 .retrieve()                 .bodyToMono(String.class)                 .block();         // print the result?       };     } } 

If not, please run your application using the --debug flag and add to your question the relevant parts of the auto-configuration report, especially the auto-configurations dealing with servers.

like image 43
Brian Clozel Avatar answered Sep 30 '22 23:09

Brian Clozel