Patrón: Disyuntor
Contexto
Ha aplicado la arquitectura de Microservicios.Los servicios a veces colaboran al manejar las solicitudes.Cuando un servicio invoca sincrónicamente a otro, siempre existe la posibilidad de que el otro servicio no esté disponible o exhiba una latencia tan alta que sea esencialmente inutilizable.Recursos preciosos, como hilos, podrían consumirse en la persona que llama mientras espera a que el otro servicio responda.Esto podría llevar al agotamiento de los recursos, lo que haría que el servicio de llamadas no pudiera manejar otras solicitudes.El fallo de un servicio puede potencialmente conectarse en cascada a otros servicios a lo largo de la aplicación.
Problema
¿Cómo evitar que un fallo de red o servicio se conecte en cascada a otros servicios?
Solución
Un cliente de servicio debe invocar un servicio remoto a través de un proxy que funcione de manera similar a un disyuntor eléctrico.Cuando el número de fallos consecutivos cruza un umbral, el disyuntor se dispara y, durante un período de tiempo de espera, todos los intentos de invocar el servicio remoto fallarán inmediatamente.Después de que expire el tiempo de espera, el disyuntor permite el paso de un número limitado de solicitudes de prueba.Si esas solicitudes tienen éxito, el disyuntor reanuda el funcionamiento normal.De lo contrario, si hay un error, el período de tiempo de espera comienza de nuevo.
Ejemplo
RegistrationServiceProxy
de la aplicación de ejemplo de Microservicios es un ejemplo de un componente, que está escrito en Scala, que utiliza un disyuntor para gestionar fallos al invocar un servicio remoto.
@Componentclass RegistrationServiceProxy @Autowired()(restTemplate: RestTemplate) extends RegistrationService { @Value("${user_registration_url}") var userRegistrationUrl: String = _ @HystrixCommand(commandProperties=Array(new HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="800"))) override def registerUser(emailAddress: String, password: String): Either = { try { val response = restTemplate.postForEntity(userRegistrationUrl, RegistrationBackendRequest(emailAddress, password), classOf) response.getStatusCode match { case HttpStatus.OK => Right(response.getBody.id) } } catch { case e: HttpClientErrorException if e.getStatusCode == HttpStatus.CONFLICT => Left(DuplicateRegistrationError) } }}
El @HystrixCommand
organiza para llamadas a registerUser()
se ejecute mediante un interruptor de circuito.
La funcionalidad del disyuntor se habilita con la anotación @EnableCircuitBreaker
en la clase UserRegistrationConfiguration
.
@EnableCircuitBreakerclass UserRegistrationConfiguration {
Contexto resultante
Este patrón tiene los siguientes beneficios:
- Los servicios manejan el error de los servicios que invocan
Este patrón tiene los siguientes problemas:
- Es difícil elegir valores de tiempo de espera sin crear falsos positivos ni introducir una latencia excesiva.
- El chasis de microservicios puede implementar este patrón
- Una puerta de enlace de API usará este patrón para invocar servicios
- Un enrutador de detección del lado del servidor puede usar este patrón para invocar servicios
Véase también
- Netflix Hystrix es un ejemplo de biblioteca que implementa este patrón