El uso indebido de las cerraduras distribuidas de Redis provocó un gran accidente, ¡se vendieron en exceso 100 botellas de Feitian Moutai! ! !

El uso de bloqueos distribuidos basados ​​en Redis no es nada nuevo en la actualidad.

Este artículo se basa principalmente en el análisis y solución de accidentes provocados por cerraduras distribuidas redis en nuestros proyectos actuales. Los pedidos de snap-up en nuestro proyecto se resuelven mediante candados distribuidos.Una vez, la operación realizó una campaña de snap-up para Feitian Moutai.100 botellas estaban en stock, ¡pero 100 botellas estaban sobrevendidas! ¡Ya sabes, la escasez de Maotai voladores en esta tierra! ! !

El accidente está clasificado como accidente grave P0 ... solo puede aceptarse con franqueza. Se dedujo el desempeño de todo el equipo del proyecto ~~ Después del accidente, el CTO me nombró por mi nombre y me pidió que tomara la iniciativa para solucionarlo.

De acuerdo, prisa ~

escena del accidente

Después de un poco de comprensión, aprendí que esta interfaz de actividad de compra de pánico nunca había sucedido antes, pero ¿por qué está sobrevendido esta vez?
La razón es que los productos de compra urgente anteriores no eran productos escasos, pero este evento fue en realidad Feitian Maotai. A través del análisis de datos de puntos enterrados, los datos básicamente se han duplicado, ¡y se puede imaginar el entusiasmo del evento! No hay mucho que decir, vaya directamente al código central y la parte confidencial se trata con pseudocódigo. . .

public SeckillActivityRequestVO seckillHandle(SeckillActivityRequestVO request) {
    
    
SeckillActivityRequestVO response;
    String key = "key:" + request.getSeckillId;
    try {
    
    
        Boolean lockFlag = redisTemplate.opsForValue().setIfAbsent(key, "val", 10, TimeUnit.SECONDS);
        if (lockFlag) {
    
    
            // HTTP请求用户服务进行用户相关的校验
            // 用户活动校验
            
            // 库存校验
            Object stock = redisTemplate.opsForHash().get(key+":info", "stock");
            assert stock != null;
            if (Integer.parseInt(stock.toString()) <= 0) {
    
    
                // 业务异常
            } else {
    
    
                redisTemplate.opsForHash().increment(key+":info", "stock", -1);
                // 生成订单
                // 发布订单创建成功事件
                // 构建响应VO
            }
        }
    } finally {
    
    
        // 释放锁
        stringRedisTemplate.delete("key");
        // 构建响应VO
    }
    return response;
}

El código anterior garantiza que la lógica empresarial tenga suficiente tiempo de ejecución durante el tiempo de vencimiento del bloqueo distribuido con un período de validez de 10 segundos; el bloque de declaración try-finalmente se utiliza para garantizar que el bloqueo se libere a tiempo. El inventario también se verifica dentro del código comercial. Parece muy seguro ~ No se preocupe, continúe analizando. . .

Si necesita más material para entrevistas de las principales empresas, también puede hacer clic para ingresar directamente y obtenerlo gratis Contraseña: CSDN

Causa del accidente

La actividad snap-up de Feitian Maotai atrajo a un gran número de nuevos usuarios para descargar y registrar nuestra APLICACIÓN. Entre ellos, hay muchas fiestas de la lana que utilizan métodos profesionales para registrar nuevos usuarios para recolectar pedidos de lana y cepillos. Por supuesto, nuestro sistema de usuario está preparado de antemano, y el acceso a la verificación humano-máquina de Alibaba Cloud, la autenticación de tres factores y el sistema de control de riesgo de desarrollo propio y otras 18 artes marciales ha bloqueado una gran cantidad de usuarios ilegales. No puedo evitar que me guste aquí ~ Pero debido a esto, el servicio al usuario ha estado bajo una gran carga operativa.

En el momento en que comenzó la actividad de compra de pánico, una gran cantidad de solicitudes de verificación de usuarios golpearon el servicio del usuario. La puerta de enlace de servicio del usuario tiene un breve retraso de respuesta. El tiempo de respuesta de algunas solicitudes supera los 10 s, pero debido a que el tiempo de espera de respuesta de las solicitudes HTTP se establece en 30 s, esto hace que la interfaz se bloquee para la verificación del usuario. Después de 10 s, distribuido El bloqueo ha En este momento, una nueva solicitud puede obtener el bloqueo, lo que significa que el bloqueo se sobrescribe. Después de que se ejecutan estas interfaces bloqueadas, se ejecutará la lógica de liberar el bloqueo, lo que libera los bloqueos de otros subprocesos, lo que provoca que nuevas solicitudes compitan por el bloqueo. En este momento, solo podemos confiar en la verificación del inventario, pero la verificación del inventario no es no atómica. Se utiliza el método de obtener y comparar. La tragedia de la sobreventa sucedió así ~~~

Análisis de accidentes

Después de un análisis cuidadoso, se puede encontrar que esta interfaz snap-up tiene serios riesgos de seguridad en escenarios de alta concurrencia, que se concentran principalmente en tres lugares:

  • No hay ningún otro sistema de manejo tolerante a fallas de riesgo.
    Debido al estricto servicio al usuario, la respuesta de la puerta de enlace se retrasa, pero no hay forma de solucionarlo. Este es el fusible de la sobreventa.
  • El bloqueo distribuido aparentemente seguro no lo es en absoluto.
    Aunque se adopta el método de establecer el valor de clave [EX segundos] [PX milisegundos] [NX | XX], si el subproceso A se ejecuta durante mucho tiempo antes de que se pueda liberar, el el bloqueo expirará. En este momento, el hilo B puede adquirir el bloqueo. Cuando el hilo A termina de ejecutarse, al liberar el bloqueo se libera el bloqueo del hilo B. En este momento, el subproceso C puede adquirir el bloqueo nuevamente, y en este momento, si el subproceso B termina de ejecutar el desbloqueo, en realidad es el bloqueo establecido por el subproceso liberado C. Esta es la causa directa de la sobreventa.
  • Verificación de inventario no atómico La verificación de inventario
    no atómico conduce a resultados de verificación de inventario inexactos en escenarios concurrentes. Esta es la causa fundamental de la sobreventa.

A través del análisis anterior, la causa raíz del problema es que la verificación del inventario se basa en gran medida en bloqueos distribuidos. Porque en el caso del armado normal y del borrado de bloqueos distribuidos, no hay problema con la verificación del inventario. Sin embargo, cuando las cerraduras distribuidas no son seguras y confiables, la verificación del inventario es inútil.

solución

Después de conocer el motivo, podemos recetar el medicamento adecuado.

Obtenga cerraduras distribuidas relativamente seguras

Definición relativamente segura: set y del se mapean uno por uno, y no habrá otro bloqueo del. Desde la perspectiva de la situación real, incluso si se puede lograr el mapeo conjunto y del uno a uno, no se puede garantizar la seguridad absoluta del negocio. Debido a que el tiempo de vencimiento del bloqueo siempre está limitado, a menos que el tiempo de vencimiento no esté establecido o el tiempo de vencimiento sea muy largo, esto también traerá otros problemas. Entonces no tiene sentido. Para lograr un candado distribuido relativamente seguro, debe confiar en el valor de la clave. Cuando se libera el bloqueo, la unicidad del valor se utiliza para garantizar que no se elimine. Implementamos la obtención y comparación atómica en función del script LUA, de la siguiente manera:

public void safedUnLock(String key, String val) {
    
    
    String luaScript = "local in = ARGV[1] local curr=redis.call('get', KEYS[1]) if in==curr then redis.call('del', KEYS[1]) end return 'OK'"";
    RedisScript<String> redisScript = RedisScript.of(luaScript);
    redisTemplate.execute(redisScript, Collections.singletonList(key), Collections.singleton(val));
}

Usamos scripts LUA para desbloquear de forma segura.

Logre una verificación de inventario segura

Si tenemos una comprensión más profunda de la concurrencia, encontraremos que operaciones como obtener, comparar, leer y guardar no son atómicas. Si queremos lograr la atomicidad, también podemos usar scripts LUA para lograrlo. Pero en nuestro ejemplo, dado que solo se puede colocar una botella en una actividad de compra de pánico, puede basarse en la atomicidad de redis en lugar de la implementación del script LUA. la razón es:

// redis会返回操作之后的结果,这个过程是原子性的
Long currStock = redisTemplate.opsForHash().increment("key", "stock", -1);

No se encontró, la verificación de inventario en el código es completamente "superflua".

Código mejorado

Después del análisis anterior, decidimos crear una nueva clase DistributedLocker específicamente para manejar bloqueos distribuidos.

public SeckillActivityRequestVO seckillHandle(SeckillActivityRequestVO request) {
    
    
SeckillActivityRequestVO response;
    String key = "key:" + request.getSeckillId();
    String val = UUID.randomUUID().toString();
    try {
    
    
        Boolean lockFlag = distributedLocker.lock(key, val, 10, TimeUnit.SECONDS);
        if (!lockFlag) {
    
    
            // 业务异常
        }

        // 用户活动校验
        // 库存校验,基于redis本身的原子性来保证
        Long currStock = stringRedisTemplate.opsForHash().increment(key + ":info", "stock", -1);
        if (currStock < 0) {
    
     // 说明库存已经扣减完了。
            // 业务异常。
            log.error("[抢购下单] 无库存");
        } else {
    
    
            // 生成订单
            // 发布订单创建成功事件
            // 构建响应
        }
    } finally {
    
    
        distributedLocker.safedUnLock(key, val);
        // 构建响应
    }
    return response;
}

Pensamiento profundo

¿Es necesario el bloqueo distribuido?

Después de la mejora, podemos encontrar que podemos garantizar que no seremos sobrevendidos con la ayuda de la deducción atómica de redis. correcto. Pero si no existe tal bloqueo, entonces todas las solicitudes pasarán por la lógica de negocio. Debido a la dependencia de otros sistemas, la presión sobre otros sistemas aumentará en este momento. Esto aumentará la pérdida de rendimiento y la inestabilidad del servicio, y la ganancia no merece la pena. Según los bloqueos distribuidos, parte del tráfico se puede interceptar hasta cierto punto.

Si necesita más material para entrevistas de las principales empresas, también puede hacer clic para ingresar directamente y obtenerlo gratis Contraseña: CSDN

Selección de cerraduras distribuidas

Alguien propuso usar RedLock para implementar bloqueos distribuidos. RedLock es más confiable, pero a costa de sacrificar cierto rendimiento. En este escenario, esta mejora en la confiabilidad es muy inferior a la rentabilidad generada por la mejora en el desempeño. Para escenarios con requisitos de confiabilidad extremadamente altos, RedLock se puede usar para lograrlo.

¿Es necesario volver a pensar en los bloqueos distribuidos?

Debido a que el error debe repararse con urgencia, lo optimizamos, realizamos una prueba de esfuerzo en el entorno de prueba y lo implementamos en línea de inmediato. Resulta que esta optimización es exitosa y el rendimiento se mejora ligeramente En el caso de falla de cerradura distribuida, no hay situación de sobreventa. Sin embargo, ¿hay margen de optimización? ¡algunos! Dado que el servicio se implementa en un clúster, podemos distribuir el inventario por igual a cada servidor del clúster y notificar a cada servidor del clúster mediante difusión. La capa de puerta de enlace utiliza un algoritmo hash basado en el ID de usuario para determinar qué servidor solicitar. De esta manera, la deducción y el juicio de inventario se pueden realizar en función de la caché de la aplicación. ¡El rendimiento se ha mejorado aún más!

// 通过消息提前初始化好,借助ConcurrentHashMap实现高效线程安全
private static ConcurrentHashMap<Long, Boolean> SECKILL_FLAG_MAP = new ConcurrentHashMap<>();
// 通过消息提前设置好。由于AtomicInteger本身具备原子性,因此这里可以直接使用HashMap
private static Map<Long, AtomicInteger> SECKILL_STOCK_MAP = new HashMap<>();

...

public SeckillActivityRequestVO seckillHandle(SeckillActivityRequestVO request) {
    
    
SeckillActivityRequestVO response;

    Long seckillId = request.getSeckillId();
    if(!SECKILL_FLAG_MAP.get(requestseckillId)) {
    
    
        // 业务异常
    }
     // 用户活动校验
     // 库存校验
    if(SECKILL_STOCK_MAP.get(seckillId).decrementAndGet() < 0) {
    
    
        SECKILL_FLAG_MAP.put(seckillId, false);
        // 业务异常
    }
    // 生成订单
    // 发布订单创建成功事件
    // 构建响应
    return response;
}

A través de la transformación anterior, no necesitamos depender en absoluto de redis. ¡Tanto el rendimiento como la seguridad pueden mejorarse aún más! Por supuesto, esta solución no considera escenarios complejos como la expansión dinámica y la contracción de la máquina, si aún quedan por considerar, es mejor considerar directamente la solución de bloqueos distribuidos.

para resumir

La sobreventa de materias primas escasas es definitivamente un accidente importante. Si la cantidad de sobreventa es grande, incluso tendrá un impacto operativo y social muy serio en la plataforma. Después de este accidente, me di cuenta de que ninguna línea de código en el proyecto debe tomarse a la ligera, de lo contrario, en algunos escenarios, ¡estos códigos que normalmente funcionan se convertirán en asesinos mortales! Para un desarrollador, al diseñar un plan de desarrollo, el plan debe considerarse a fondo. ¿Cómo se puede considerar el plan de manera integral? ¡Solo aprendizaje continuo!

Beneficios del lector

¡Gracias por ver aquí!
He compilado muchas de las últimas preguntas de la entrevista de Java de 2020 (incluidas las respuestas) y notas de estudio de Java aquí, como se muestra a continuación
Inserte la descripción de la imagen aquí

Las respuestas a las preguntas de la entrevista anteriores están organizadas en notas de documentos. Además de las entrevistas, también recopiló información sobre algunos de los fabricantes y entrevistó la última colección 2020 de Zhenti (ambas documentan una pequeña parte de la captura de pantalla) gratis para que todos la compartan, los que lo necesiten pueden hacer clic para ingresar a la señal: ¡CSDN! Libre para compartir ~

Si te gusta este artículo, reenvíalo y dale me gusta.

¡Recuerda seguirme!
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_49527334/article/details/111858176
Recomendado
Clasificación