Prueba de interfaz idempotente

Este artículo explica principalmente qué es la idempotencia y cómo garantizar la idempotencia cuando ocurren problemas de idempotencia. Espero que este artículo sea útil para los lectores;

1. Idempotencia de interfaz

La llamada idempotencia significa que llamar a un método o interfaz varias veces no cambiará el estado del negocio y puede garantizar que los resultados de llamadas únicas y repetidas sean consistentes;

2. ¿Bajo qué circunstancias se debe considerar la idempotencia?

En nuestro desarrollo, la operación principal es CRUD, donde las operaciones de lectura y eliminación son naturalmente idempotentes, nuestra principal preocupación son las operaciones nuevas y de actualización;

Escenarios de uso de idempotencia:

1. Envío repetido de front-end

Por ejemplo, si agrega una nueva función de producto, hace clic en el botón Guardar y el front-end hace clic en guardar varias veces seguidas, el back-end recibirá múltiples interfaces de solicitud. Si no se realiza la idempotencia, se crearán múltiples registros repetidamente. y aparecerán datos sucios;

Esto es lo que llamamos el problema de cómo evitar envíos repetidos en el front-end;

2. Los usuarios deslizan pedidos maliciosamente

Por ejemplo: en la función de votación del usuario, los usuarios envían repetidamente votos para un usuario (llamando a la interfaz), lo que puede hacer que la interfaz reciba información de votación enviada repetidamente por los usuarios, lo que hará que los resultados de la votación sean seriamente inconsistentes con los hechos. ;

3. Reintento del tiempo de espera de la interfaz

Cuando llamamos a una interfaz de terceros, la llamada puede fallar debido a la red y otras razones, por lo que agregaremos un mecanismo de reintento fallido a la llamada de la interfaz (Spring puede implementar el mecanismo de reintento a través de la anotación @Retryable)

Dado que pueden producirse reintentos, es posible que se produzcan llamadas repetidas a la interfaz. Si no se realiza la idempotencia al volver a llamar en este momento, pueden aparecer datos sucios;

4. Consumo repetido de mensajes 

Cuando se utiliza middleware de mensajes MQ, existe un mecanismo de reintento tanto en el lado de producción como en el lado del consumidor, lo que significa que el mismo mensaje puede consumirse repetidamente.

Escenarios donde la interfaz de prueba es idempotente:

1. Las fluctuaciones de la red pueden provocar solicitudes repetidas;

2. Los usuarios repiten operaciones y pueden realizar múltiples pedidos sin querer durante las operaciones;

3. Utilice un mecanismo de reintento fallido o de tiempo de espera;

4. Actualiza la página repetidamente. 

5. Utilice el botón Atrás del navegador para repetir la operación anterior, lo que resultará en el envío repetido del formulario;

6. Utilice el historial del navegador para enviar el formulario repetidamente;

7. Solicitudes HTTP repetidas desde el navegador;

8. Ejecución repetida de tareas programadas;

3. Cómo garantizar la idempotencia de la interfaz

Manera elemental:

1. Determine si los datos existen antes de insertarlos;

Esto es lo más básico y debe hacerse en el desarrollo. Antes de insertar o actualizar, se juzgará si ya existe en la base de datos actual, si existe, no se permite la inserción repetida, si no existe, se puede insertar.

2. Realice algún control interactivo en la parte frontal

Por ejemplo, después de que el usuario hace clic en el botón Guardar, el botón aparecerá atenuado o en el proceso de carga, y no se podrá volver a hacer clic ni saltar a otras páginas, lo que puede evitar que una gran parte del front-end se envíe repetidamente. ;

¿Cómo garantizar la idempotencia en condiciones de alta concurrencia?

1. Basado en un bloqueo pesimista

2. Basado en bloqueo optimista

3. Basado en el código de estado

4. Basado en índice único

5. Basado en cerraduras distribuidas

6. Basado en token

etc. En el uso real, las cerraduras distribuidas básicas se usan más comúnmente;

A continuación presentamos los principios de cada método uno por uno:

1. Basado en un bloqueo pesimista

Definición: cuando desee modificar un dato en la base de datos, para evitar que otros lo modifiquen al mismo tiempo, la mejor manera es bloquear los datos directamente para evitar la concurrencia;

En el caso de alta concurrencia, un negocio se ejecutará dos veces, lo que podemos lograr mediante un bloqueo pesimista, es decir, agregando el campo for update a la declaración de consulta SQL.

Tenga en cuenta aquí:

1) Para una fila de datos bloqueada, un determinado campo debe estar indexado; de lo contrario, la tabla se bloqueará; como order_no, agregue un índice;

2) El bloqueo pesimista bloquea una fila de datos durante la misma operación de transacción. El bloqueo pesimista tiene un rendimiento deficiente, por lo que generalmente no se recomienda utilizar el bloqueo pesimista para este propósito;

2. Basado en bloqueo optimista

Definición: El bloqueo optimista es muy optimista. Cada vez que va a obtener los datos, piensa que otros no los modificarán, por lo que no los bloqueará. Sin embargo, al actualizar, juzgará si otros han actualizado los datos durante este período. Puede utilizar el mecanismo de número de versión.

El llamado bloqueo optimista consiste en agregar un campo de versión (número de versión) a la tabla.

La idempotencia de la operación de actualización se controla a través del número de versión. El usuario consulta los datos a modificar, el sistema devuelve los datos a la página y coloca el número de versión de los datos en el campo oculto. El usuario modifica los datos y hace clic en enviar. , y el número de versión es Se envía una pasada al backend, y el backend utiliza el número de versión como condición de actualización;

Nota: Lo que puede garantizar el bloqueo optimista es la idempotencia de la operación de actualización. Si su actualización en sí es una operación idempotente o la operación de instalación, no puede utilizar el bloqueo optimista.

3. Basado en el código de estado

Muchas tablas de negocios tienen estados, como la tabla de pedidos. Generalmente, un pedido tiene 1 creación de pedido , 2 confirmación de pedido , pago de 3 pedidosfinalización de 4 pedidos , cancelación de 5 pedidos y otros procesos de pedido. Cuando actualizamos la estado del pedido

En la primera solicitud,  el  estado de confirmación del pedido se cambió con éxito a  pago del pedido y el número de filas afectadas por el resultado de la ejecución de SQL fue 1.

En la segunda solicitud, también quiero  cambiar el  estado de confirmación del pedido a  pago del pedido , pero el número de filas afectadas por el resultado de la ejecución de SQL es 0. Si es 0, entonces podemos devolver el éxito directamente. No es necesario realizar operaciones comerciales posteriores para garantizar la idempotencia de la interfaz.

4. Basado en índice único

En términos generales, los bloqueos pesimistas, los bloqueos optimistas y los códigos de estado se utilizan para las operaciones de actualización para lograr la idempotencia, mientras que los índices únicos se utilizan para las operaciones de inserción para garantizar la idempotencia.

1) Al crear un pedido, el front-end primero obtiene el número de pedido a través de la interfaz y luego trae el número de pedido al solicitar el back-end. Se agrega un índice único al número de pedido en la tabla de pedidos. Si es el mismo número de pedido Se inserta, se informará un error directamente.

2) Cuando se consumen mensajes MQ, messageIdson únicos. Podemos agregar una nueva tabla de registro de consumo y usar messageId como clave principal. Si se consume repetidamente, existirá el mismo messageId y se informará un error directamente cuando se inserte.

5. Basado en cerraduras distribuidas

La lógica de los bloqueos distribuidos para lograr la idempotencia es que cuando llega una solicitud, primero intente obtener el bloqueo distribuido. Si tiene éxito, se ejecutará la lógica empresarial. De lo contrario, si la adquisición falla, la solicitud se descartará y la solicitud regresar directamente al éxito.

De hecho, el bloqueo pesimista presentado anteriormente utiliza el bloqueo distribuido de la base de datos, que empaqueta múltiples operaciones en una operación atómica para garantizar la idempotencia. Sin embargo, debido al bajo rendimiento de los bloqueos distribuidos de la base de datos,

En su lugar, podemos usar: redis o zookeeper para implementar bloqueos distribuidos.

Pasos específicos:

El usuario inicia una solicitud a través del navegador, el servidor recopila datos y genera el código del número de pedido como único campo comercial.

Utilice el comando redis set para configurar el código de pedido en redis y configurar el tiempo de espera al mismo tiempo.

Determine si la configuración es exitosa. Si la configuración es exitosa, significa que es la primera solicitud y se realiza la operación de datos.

Si la configuración falla, significa que la solicitud se repite y se devolverá el éxito directamente.

6. Basado en token

La idea central del mecanismo de token es generar un certificado único, es decir, un token, para cada operación. Un token tiene solo un derecho de ejecución en cada etapa de la operación y, una vez que la ejecución es exitosa, el resultado de la ejecución se guarda. Para solicitudes repetidas, se devuelve el mismo resultado. La aplicación del mecanismo de fichas es muy amplia.

Por ejemplo: Cada solicitud contiene un ticket. Este ticket es de un solo uso. Se destruye después de usarse una vez y no se puede reutilizar. Este token es equivalente al concepto de un ticket. El token se trae con cada solicitud de interfaz. El servidor verifica el token cuando lo procesa por primera vez. Y este token solo se puede usar una vez. Si el usuario usa el mismo token Si la solicitud se realiza dos veces, la segunda vez no será procesada y será devuelta directamente.

La característica de la solución token es que requiere dos solicitudes para completar una operación comercial.

Generalmente incluye dos etapas de solicitud:

1) El cliente solicita 申请获取tokeny el servidor genera un token y lo devuelve.

2) Para la segunda solicitud 带着这个token, el servidor verifica el token y completa la operación comercial.

Pasos específicos:

Cuando un usuario accede a una página, el navegador inicia automáticamente una solicitud de token.

El servidor genera el token, lo guarda en redis y luego lo devuelve al navegador.

Cuando el usuario inicia una solicitud a través del navegador, se transporta el token.

Consulta si el token existe en redis, si no existe significa que es la primera solicitud y se realizarán operaciones de datos posteriores.

Si existe, significa que es una solicitud repetida y se devolverá el éxito directamente.

En redis, el token se eliminará automáticamente después del tiempo de vencimiento.

El esquema principal de tokens tiene ciertos riesgos, analicémoslo:

1. Eliminar el token primero [eliminar el token primero, luego ejecutar el negocio] o eliminar el token más tarde [ejecutar el negocio primero, luego eliminar el token];

(a) Eliminar primero puede provocar que el negocio no se ejecute y volver a intentarlo con el token anterior. Debido al anti-rediseño, la solicitud aún no se puede ejecutar;

(b) Eliminar el token más tarde es un gran problema. Puede hacer que el negocio se procese con éxito, pero el servicio se interrumpe, se agota el tiempo de espera, el token no se elimina y otros continúan reintentando, lo que provoca que el negocio se ejecute. dos veces;

Por lo tanto, será mejor que diseñemos eliminar el token primero y, si la llamada comercial falla, volver a obtener el token y solicitarlo nuevamente.

2. Aquí la adquisición, comparación y eliminación de tokens deben garantizar la atomicidad.

Después de verificar si el token existe, no use redis.get (token), luego use redis.del (token). Esta no es una operación atómica y aún causará problemas idempotentes en situaciones de alta concurrencia.

La forma en que podemos usarlo directamente redis.del(token):

Supongo que te gusta

Origin blog.csdn.net/weixin_43500974/article/details/129316496
Recomendado
Clasificación