web-dev-qa-db-fra.com

Comment empêcher le serveur netty intégré de démarrer avec spring-boot-starter-webflux?

Je souhaite établir une communication entre une application client et serveur à l'aide de la nouvelle extension Springs réactive webflux.

Pour la gestion des dépendances, j'utilise gradle. Mon fichier build.gradle sur le serveur, ainsi que sur le côté client, est essentiellement:

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")
}

(Il convient de noter que 2.0.0.BUILD - SNAPSHOT est une cible mobile et le problème à résoudre peut simplement disparaître un jour en raison de changements à l'intérieur de la dépendance)

Lorsque je démarre l'application côté serveur, tout démarre bien, y compris le démarrage d'un serveur netty intégré.

Mais lorsque vous démarrez l'application cliente, un serveur netty est également démarré, provoquant une "Java.net.BindException: adresse déjà utilisée", car le serveur netty côté client écoute sur le même port que le serveur netty côté serveur.

Ma question est: Pourquoi netty est-il démarré du côté client en premier lieu et comment puis-je l'empêcher?

Selon la documentation Spring-Boot, Spring essaie de déterminer si la prise en charge Web est requise et configure le contexte de l'application Spring en conséquence.

Et selon les documents, cela peut être remplacé par un appel à setWebEnvironment (false). Mon code de démarrage client ressemble alors à:

@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));
    }
}

Malheureusement, netty est toujours lancé. Je note également que setWebEnvironment (false) est marqué comme obsolète.

Toute aide sur la façon d'empêcher nety de démarrer mais sinon de conserver toutes les dépendances de webflux est appréciée.

Voici un extrait du rapport de configuration automatique:

=========================
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)
14
Frank Kaiser

Le principal problème avec votre code est que vous créez actuellement un SpringApplication, puis vous le personnalisez - pour enfin tout supprimer et exécuter la méthode statique run(Object primarySource, String... args).

Les éléments suivants devraient fonctionner:

@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?
      };
    }
}

Sinon, veuillez exécuter votre application en utilisant le --debug marquez et ajoutez à votre question les parties pertinentes du rapport de configuration automatique, en particulier les configurations automatiques concernant les serveurs.

10
Brian Clozel

Ajout à la réponse @Brian_Clozel:

Vous pouvez désactiver Netty (ou tout autre serveur) en spécifiant à l'intérieur d'un fichier application.yml:

spring.main.web-application-type: none

ou application.properties:

spring.main.web-application-type=none
8
Yan Pak