El reintento de primavera es una función de energía independiente del lote de Spring, que implementa principalmente volver a intentar y fusirse. Existen restricciones de escenario para el reintento, y no todos los escenarios son adecuados para volver a intentarlo, como la verificación de los parámetros ilegales, las operaciones de escritura, etc. (debe considerar si la escritura es ideMpotent) no son adecuadas para volver a intentarlo. El tiempo de espera de llamadas remotas o la interrupción de la red se pueden volver a intentar. En el marco de gobierno de microservicio, generalmente tiene su propia configuración de reintento y tiempo de espera. Por ejemplo, Dubbo puede establecer reintentos = 1, Tiempo de espera = 500 La llamada falla y volver a intentar solo una vez, y la llamada falla si no regresa después de más de 500 ms. En el reintento de primavera, puede especificar el tipo de excepción que debe volver a intentarlo y establecer el intervalo para cada reintento y si el reintento falla, si continúa reintento o fusible (detenga el reintento).
Diseño e implementación
RetryOperations define la API de reintento. RetryTemplate es la implementación del modo de plantilla de la API, que implementa el reintento y la ruptura del circuito. La API proporcionada es la siguiente:
Interfaz pública RetryOperations {<t, e extiende showable> t ejecute (retryCallback <t, e> retryCallback) lanza E; } // Se han omitido otras API RetryCallback define la operación que debe realizarse. Después de definir la operación, es la cuestión de cómo volver a intentarlo. RetryTemplate ejecuta la lógica de cómo volver a intentar formular diferentes estrategias de reintento. La estrategia de reintento predeterminada es SimpleRetryPlicy , lo que significa que se volverá a intentar 3 veces. Si el reintento es exitoso, no continuará intentándole de nuevo. Entonces, ¿y si el reintento de 3 pies fallara? El proceso termina o devuelve el resultado ascendente. Para devolver el resultado de abajo hacia arriba, debe configurar RecoveyCallBack . Del nombre, puede ver que esta es una interfaz de devolución de llamada ascendente, que es la lógica de ejecución después de que el reintento falló. Además de SimpleRetryPolicy , hay otras estrategias de reintento. Echemos un vistazo a la interfaz RetryPolicy :
Public Interface RetryPolicy extiende serializable {boolean Cancryry (contexto de retryContext); RetryContext Open (retryContext parent); void close (contexto de retryContext); void registreThrowable (contexto de retryContext, lanzable lanzable);} canRetry se llama cada vez que vuelves a intentarlo. La condición de juicio de si puede continuar intentándole de nuevo
Llamado antes de que comience el reintento open , se creará un contexto de reintento en RetryContext , y se guardará la pila de reintento.
registerThrowable se llama cada vez que se vuelve a intentar la excepción (hay una excepción y continuará reintentando)
Tomemos SimpleRetryPolicy como ejemplo. Cuando el número de retrestas alcanza 3 (predeterminado 3 veces), deje de volver a intentar y el número de reintento se guarda en el contexto de reintento.
Proporcione la siguiente implementación de la estrategia de reintento:
Vuelva a intentar la estrategia de alojamiento se refiere a si cada volver al reintento se vuelve inmediatamente o espere un tiempo antes de intentarlo nuevamente. De manera predeterminada, debe especificar la política Backoff BackoffRetryPolicy si necesita configurar un período de espera y luego intentarlo nuevamente. BackoffretryPolicy tiene la siguiente implementación:
Reintento de estado o reintento sin estado
El llamado reintento sin estado se refiere al reintento completado en un contexto de hilo. De lo contrario, si el reintento no se completó en un contexto de hilo es un reintento con estado. La SimpleRetryPolicy anterior era un reintento sin estado porque el reintento se completó en un bucle. Entonces, ¿qué sucede después de eso o necesita ser retumbado en un estado? Por lo general, hay dos situaciones: reversión de transacciones y interruptor de circuito.
DataAccessException es excepcional. El reintento no se puede realizar, pero si se lanzan otras excepciones, puede intentarlo nuevamente.
El fusible significa no manejar el reintento en el bucle actual, sino al modo de reintento global (no contexto de hilo). El interruptor de circuito saltará del bucle, y la información de la pila del contexto del hilo inevitablemente se perderá. Entonces definitivamente es necesario guardar esta información en un "modo global". La implementación actual se coloca en un caché (implementación del mapa). Puede continuar intentando nuevamente después de obtenerlo del caché la próxima vez.
Comienzo rápido
Use @EnableRetry en la clase que debe realizar el reintento, si se establece proxyTargetClass=true , esto usa proxy dinámico CGLIB:
@Configuration@Enableretry (proxyTargetClass = true) @ComponentPublic Class RetryExAMPLE {} Vuelva a intentarlo basado en el número máximo de estrategia de reintentos, si el reintento se repite 3 veces y la excepción aún se lanza, el reintento se detiene y se ejecuta la devolución de llamada ascendente. Por lo tanto, el resultado final de salida es Integer.MAX_VALUE :
private void retryExample3 () lanza la excepción {retryRyTemplate retryryTemplate = new RetryTEmplate (); Simpleretrypolicy simpleretrypolicy = new SimpleRetryPolicy (); simpleretrypolicy.setmaxattempts (3); retryrytemplate.setretryPolicy (simpleretrypolicy); Resultado entero = retryTemplate.ExeCute (nuevo retryCallback <Integer, Exception> () {int i = 0; // Vuelve a royar la operación @Override public Integer dowithcryry (retryContext retryContext) lanza la excepción {log.info ("RET RETRY: {}", retryRyContex.getRyCount (); }, New RecoveryCallback <Integer> () {// Base de devolución de llamada @Override Integer Recover (retryContext retryContext) lanza la excepción {log.info ("después de retry: {}, método de recuperación llamado!" log.info ("resultado final: {}", resultado); } private int len (int i) lanza excepción {if (i <10) arroja una nueva excepción (i + "le 10"); regresar i; }A continuación se describe cómo usar el modo de política de reintento del interruptor de circuito (CircuitBreakerRetryPolicy). Se deben establecer los siguientes tres parámetros:
Circuito de apertura y juicio de cierre:
El código de prueba es el siguiente:
Plantilla de retrytemplate = new RetryTTemplate (); CircuitBreakerRetryPolicy RetryPolicy = new CircuitBreakerRetryPolicy (nueva SimpleRetryPolicy (3)); retrypolicy.SetopentIeout (5000); Ritrypolicy.setResetTimeOut (20000); Template.setRetryPolicy (RitryPolicy); para (int i = 0; i <10; i ++) {//thread.sleep(100); intente {objeto clave = "circuito"; boolean isforcerefresh = false; RedrYSTATE State = new DeFaultretryState (Key, ISForCerefresh); String result = Template.ExeCute (nuevo retryCallback <String, runtimeException> () {@Override public String dowithcryry (retryContext context) lanza runtimeException {log.info ("retryn Count: {}", context.getRictyCount ()); tirar runtimeexception ("timeout"; @Override public String Recover (contexto de retryContext) lanza la excepción {return "predeterminada"; log.info ("resultado: {}", resultado); } catch (Exception e) {System.out.println (e); }} Dado que se establece isForceRefresh = false , el valor de key = "circuit" (es decir, RetryContext) se obtendrá de la memoria caché. Por lo tanto, cuando el reintento falla y this.time < this.openWindow está fusionado, aún puede continuar implementando volver a intentarlo en el modo global ( RetryContext obtenido es el mismo).
Desarrollo de la anotación
Si escribir una RetryTemplate cada vez que tiene un requisito de reintento está demasiado hinchado, el uso de anotaciones puede simplificar enormemente el desarrollo y reducir el código duplicado. Aquí hay un reintento de la estrategia de reintento máximo implementada utilizando anotaciones:
@Cretryable (valor = sqldataException.class, backoff = @backoff (value = 0l)) public String Service3 () lanza SqlDataException {log.info ("Service3 Open"); tirar nueva sqldataException (); } @Recover Public String Recover (sqlDataException ne) {return "sqldataException recope"; }Las anotaciones incluyen:
@Enableretry
@Cretryable
@Recuperar
@Backoff
@Circuitbreaker
@Enableretry: ¿Puedes intentarlo de nuevo? Cuando la propiedad ProxyTargetClass sea verdadera (falso predeterminado), use cglib proxy
@Pryable: el método que la anotación debe volver a intentarlo
@Backoff: Vuelva a intentar la estrategia de retroceso (intente ahora o espere un tiempo antes de intentarlo nuevamente)
@Recover: para usar con métodos. Utilizado como un método "garantizado" cuando @cretryable falla. El método de anotación @Recover debe ser consistente con el método "firma" de @cretryable anotation. El primer parámetro de entrada es la excepción a ser retumbada. Otros parámetros son consistentes con @cryryable. El valor de retorno debe ser el mismo, de lo contrario no se puede ejecutar!
@Circuitbreaker: utilizado para el método, implementar el modo de ruptura del circuito.
Para más ejemplos, bienvenido a mi estrella GitHub (https://github.com/happyxiaofan/springboot-lelarning-example). Gracias
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.