Tabla de contenido
2. ¿Qué situaciones hay que prevenir?
3. ¿Bajo qué circunstancias se requiere la idempotencia?
2. Varios mecanismos de bloqueo.
(1) Bloqueo pesimista de la base de datos
(2) Bloqueo optimista de la base de datos
(3) Bloqueo distribuido de capa empresarial
(1) Restricciones únicas de la base de datos
(2) redis configurado para evitar la redundancia
5. Identificación única de solicitud global
1. ¿Qué es la idempotencia?
2. ¿Qué situaciones hay que prevenir?
- El usuario hace clic en el botón varias veces
- La página del usuario regresa y se envía nuevamente.
- Los microservicios se llaman entre sí y la solicitud falla debido a problemas de red. fingir desencadena el mecanismo de reintento
- Otras condiciones comerciales
3. ¿Bajo qué circunstancias se requiere la idempotencia?
4. Soluciones idempotentes
1. Mecanismo de fichas _
- El servidor proporciona una interfaz para enviar tokens . Cuando analizamos el negocio, qué negocios tienen problemas idempotentes, primero debemos obtener el token antes de ejecutar el negocio, y el servidor guardará el token para redis .
- Luego, cuando llame a la solicitud de la interfaz comercial, lleve el token , generalmente en el encabezado de la solicitud.
- El servidor determina si el token existe en redis , si existe , indica la primera solicitud , luego elimina el token y continúa ejecutando el negocio.
- Si se determina que el token no existe en redis , significa que la operación se repite y la marca repetida se devuelve directamente al cliente , lo que garantiza que el código comercial no se ejecutará repetidamente.
A continuación, considere si eliminar el token primero o ejecutar el negocio primero después de enviar el pedido.
analizar:
Eliminar el token más tarde
① La eliminación posterior puede hacer que el negocio se procese con éxito, pero el servicio se interrumpe, se agota el tiempo de espera y el token no se elimina . Otros continúan reintentando, lo que hace que el negocio se ejecute dos veces.
② Durante la ejecución del negocio, debido a que el token no se ha eliminado, si el cliente presiona dos veces para enviar la orden, esta se ejecutará dos veces.
Eliminar el token primero
① El formato del código es aproximadamente el siguiente
if(serverToken == redisToken) {
del(serverToken)
执行业务
}
② Si se considera el extremo, aún es posible que los clientes hagan clic continuamente para enviar órdenes y, al mismo tiempo, realicen múltiples juicios y ejecuten múltiples transacciones.
Pero es obvio que la segunda opción es menos probable y menos peligrosa, por lo que elegimos eliminar el token primero y luego considerar la optimización.
Podemos agrupar los tres pasos para determinar si el token existe en redis y eliminar el token en redis, es decir, la adquisición, comparación y eliminación del token deben ser atómicas.
if
redis.call('get', KEYS[1]) == ARGV[1]
then
return redis.call('del', KEYS[1])
else
return 0
end
2. Varios mecanismos de bloqueo.
(1) Bloqueo pesimista de la base de datos
(2) Bloqueo optimista de la base de datos
(3) Bloqueo distribuido de capa empresarial
3. Varias limitaciones únicas
(1) Restricciones únicas de la base de datos
(2) redis configurar anti-reparación
4. Reloj antipesado
5. Identificación única de solicitud global
proxy_set_header X-Request-Id $request_id;
Caso: envío de pedido
<form action="http://order.gulimall.com/submitOrder" method="post">
<input type="hidden" name="addrId" id="addrIdInput"/>
<input type="hidden" name="payPrice" id="payPriceInput"/>
<input type="hidden" name="orderToken" th:value="${orderConfirmData.Token}"/>
<button class="tijiao" type="submit">提交订单</button>
</form>
② Valor de configuración
function getFare(addrId) {
$("#addrIdInput").val(addrId)
$.get("http://gulimall.com/api/ware/wareinfo/fare?addrId=" + addrId, function (data) {
console.log(data);
$("#fare").text(data.data.fare)
var total = [[${orderConfirmData.total}]]
// 设置应付价格
var payPrice = data.data.fare * 1 + total * 1
$("#payPriceEle").text(payPrice)
$("#payPriceInput").val(payPrice)
$("#recieveAddressEle").text(data.data.address.provice + "" + data.data.address.detailAddress);
$("#recieverEle").text(data.data.address.name)
})
}
③ Primero, garantice la unicidad del pedido en el nivel de la base de datos y agregue restricciones de unicidad al número de pedido.
④ Interfaz de pedidos de fondo
@PostMapping("/submitOrder")
public String submitOrder(OrderSubmitVo vo) {
// 下单,创建订单,校验令牌,检验价格,锁库存
SubmitOrderResponseVo responseVo = orderService.submitOrder(vo);
System.out.println("订单提价的数据。。。" + vo);
if(responseVo.getCode() == 0) {
// 成功显示支付页面
return "pay";
} else {
// 失败返回订单确认页面
return "redirect:http://order.gulimall.com/toTrade";
}
}
⑤ Asegúrese de que la obtención de tokens de redis, la comparación y la eliminación de tokens sean atómicas.
@Override
public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
submitVoThreadLocal.set(vo);
MemberResVo memberResVo = LoginUserInterceptor.loginUser.get();
SubmitOrderResponseVo response = new SubmitOrderResponseVo();
String redisToken = redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResVo.getId());
String orderToken = vo.getOrderToken();
// 成功返回1 失败返回0
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
// 保证原子性
Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResVo.getId()), orderToken);
if(result == 0L) {
// 验证失败
response.setCode(1);
return response;
} else {
// 下单,创建订单,校验令牌,检验价格,锁库存
// 1、创建订单,订单项等信息
OrderCreateTo order = createOrder();
// 2、验价
BigDecimal payAmount = order.getOrder().getPayAmount();
if(Math.abs(payAmount.subtract(vo.getPayPrice()).doubleValue()) < 0.01) {
// 金额对比成功后保存订单
saveOrder(order);
} else {
response.setCode(2); // 金额对比失败
return response;
}
}
// 逻辑如下,但是其实我们需要保证对比和删除是原子性的,因此使用redisTemplate的脚本
// if(orderToken != null && orderToken.equals(redisToken) ) {
// // 检验成功
// redisTemplate.delete(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResVo.getId());
// } else {
// // 失败
// }
response.setCode(1);
return response;
}