Red de conocimiento informático - Problemas con los teléfonos móviles - Spring Cloud Gateway cambió accidentalmente un contenedor web y no se puede usar. ¡Soy estúpido y tengo demasiado dinero!

Spring Cloud Gateway cambió accidentalmente un contenedor web y no se puede usar. ¡Soy estúpido y tengo demasiado dinero!

Recientemente, un miembro del equipo modificó las dependencias públicas de algunos microservicios. En una de las dependencias, necesitaba realizar algunas personalizaciones en el contenedor Undertow utilizado por nuestros microservicios, por lo que agregó una dependencia en el contenedor web. Resaca. Sin embargo, en términos generales, esta dependencia del marco subyacente debe tener en cuenta si el contenedor web del proyecto utilizado actualmente es Undertow. El estudiante escribió @Conditional en la clase de configuración:

Sin embargo, el alcance de la dependencia undetow agregada no se configuró como proporcionado, lo que provocó que la dependencia se agregara inmediatamente una vez que se agregó al proyecto. Esto hace que se agregue la dependencia Undertow cada vez que se agrega esta dependencia. Nuestra puerta de enlace utiliza Spring-Cloud-Gateway, lo que hace que el contenedor web reactivo Netty de Spring-Cloud-Gateway sea reemplazado por el contenedor web reactivo de Undertow, lo que genera una serie de problemas de incompatibilidad con Spring-Cloud-Gateway.

Sabemos que la capa inferior de Spring-Cloud-Gateway en realidad se basa en Spring Boot. Primero, echemos un vistazo al principio de elegir qué contenedor web inicializar en Spring Boot: el primer paso es determinar qué WebApplicationType existe en función de si la clase existe:

WebApplicationType

Del código fuente Como se puede ver en , cuando hay WEBFLUX_INDICATOR_CLASS pero no WEBMVC_INDICATOR_CLASS, no hay WebApplicationType, no hay WebApplicationType.CLASS y no hay WEBMVC_INDICATOR_CLASS y JERSEY_INDICATOR_CLASS, entonces se determina que el entorno es REACTIVO. Si todos los SERVLET_INDICATOR_CLASSES están presentes, se considera un entorno SERVLET. De hecho, esto también muestra que si se introducen las dependencias spring-web y spring-webflux, sigue siendo un entorno SERVLET. Sin estas dependencias, tendrá un entorno de contenedor sin red. En Spring-Cloud-Gateway, es un entorno REACTIVO.

Si es un entorno REACTIVO, el contenedor web se creará utilizando el bean de implementación de org.springframework.boot.web.reactive.server.ReactiveWebServerFactory.

Entonces, ¿qué tipo de implementación es? Actualmente hay cuatro implementaciones (Spring-boot 2.7.x):

Cuál se usa realmente depende de los beans realmente registrados en ApplicationContext:

ReactiveWebServerFactoryConfiguration

Como puede ver en el código fuente, cada configuración tiene @ConditionalOnMissingBean(ReactiveWebServerFactory.class) y una condición que determina si la clase existe en el contenedor correspondiente, por ejemplo, @ConditionalOnClass({ Undertow.class }), @ConditionalOnConfiguration( proxyBeanFactory.class }), @ConditionalOnClass({ Undertow.class }) y @Configuration(proxyBeanMethods = false) deben desactivar el proxy entre beans en esta configuración para acelerar la carga.

Dado que cada configuración tiene @ConditionalOnMissingBean (ReactiveWebServerFactory.class), en realidad se garantiza que incluso si se cumplen las condiciones de múltiples configuraciones, al final solo habrá una ReactiveWebServerFactory. Entonces, cuando se cumplan varias condiciones, ¿cuál se cargará primero? Aquí está el código fuente:

ReactiveWebServerFactoryAutoConfiguration

Como puede ver aquí, se importa en el orden de EmbeddedTomcat, EmbeddedJetty, EmbeddedUndertow, EmbeddedNetty y EmbeddedJetty. EmbeddedUndertow, EmbeddedNetty, importados en secuencia: siempre que haya algún contenedor web (como Undertow) en sus dependencias, el contenedor asincrónico finalmente creado será un contenedor basado en ese contenedor web, no un contenedor basado en netty

En primer lugar, Spring

En primer lugar, la documentación oficial de Spring Cloud Gateway dice:

Spring Cloud Gateway solo se puede ejecutar en un entorno Netty. Es por eso. Cuando lo diseñamos originalmente, asumimos que el contenedor solo podía ser Netty, y cuando posteriormente desarrollamos varios filtros integrados y complementos de filtro de Spring Cloud Gateway, había una gran cantidad de código que asumía que el contenedor actual era Netty, por ejemplo. , el cuerpo de caché de las clases de utilidad ServerWebExchangeUtils utilizadas por el filtro:

ServerWebExchangeUtils

Como puede ver en el código fuente, el código asume directamente que BufferFactory en la respuesta es NettyDataBufferFactory. . El contenedor actualmente debería ser DefaultDataBufferFactory, lo cual será una excepción.

Sin embargo, en las versiones posteriores a la v3.0.5, esta fuerte rotación se ha solucionado, consulte /spring-cloud/spring-cloud-gateway/commit/68dcc355119e057af1e4f664c81f77714c5a8a16

En realidad, esto es compatible con todos los contenedores web que allanaron el camino. Entonces, ¿hay planes para hacerlo compatible con todos los contenedores web? Sí, hay un plan y todavía está en marcha. Esto significa que todas las pruebas unitarias deben volver a ejecutarse o incluso rediseñarse. Puede verificar el progreso de la compatibilidad a través del siguiente problema: Admite la ejecución de puerta de enlace en otros contenedores reactivos además de netty #145

.

Ver el progreso de la compatibilidad.