Cerradura distribuida de Zookeeper manuscrita

Antes de decir que Zookeeper es un bloqueo distribuido, sabemos que Redis también puede ser un bloqueo distribuido. Entonces, ¿por qué debería usar Zookeeper como un bloqueo distribuido?

La imagen de arriba muestra la comparación técnica de la base de datos, Redis y Zookeeper para implementar bloqueos distribuidos. No hace falta decir que el rendimiento de la base de datos para implementar bloqueos distribuidos es definitivamente muy bajo, aunque Redis tiene un alto rendimiento, pierde frente a Zookeeper en términos de consistencia final. Zookeeper tiene ventajas naturales en clústeres distribuidos. En un entorno de producción, el middleware generalmente se implementa en forma de un clúster, por lo que aquí hay un problema de sincronización maestro-esclavo. En un clúster maestro-esclavo de Redis, si el nodo maestro envía una sincronización de archivo RDB al nodo esclavo, el nodo maestro puede colgarse y el archivo es Los datos pueden perderse durante un segundo. Sin embargo, Redis tiene un conjunto de estrategias de confirmación de sincronización. El bloqueo con Redis es en realidad un conjunto de datos en Redis. Generalmente, es necesario que el nodo maestro sincronice los datos con el nodo esclavo antes de que pueda considerarse exitoso. Por supuesto, para mejorar la eficiencia, existe un modelo RedLock, que cree que mientras el número de nodos de sincronización exitosos exceda la mitad, el bloqueo se considera exitoso. Sin embargo, este algoritmo del modelo RedLock es bastante complicado de mantener. Pero Zookeeper, naturalmente, apoya este modelo.

Otra razón para usar Zookeeper es que tiene un mecanismo de vigilancia que se bloquea cuando el hilo no puede agarrar el candado. Cuando el hilo que sostiene el candado lo libera, el mecanismo de observador notificará activamente a otros subprocesos que se despierten para agarrar el candado. Con el bloqueo de Redis, el proceso de captura de hilo es un proceso KV spin-setnx. Esta captura de bloqueo no es tan elegante como el método de captura de bloqueo de Zookeeper. Entonces me gusta usar Zookeeper para implementar bloqueos distribuidos.

 

Características de Zookeeper

 

 

1. Estructura de datos del árbol de nodos, znode es un nodo similar a la ruta del sistema de archivos Unix, que puede almacenar u obtener datos de este nodo;
2. A través del cliente, el znode se puede agregar, eliminar, modificar y verificar, y el observador también se puede registrar para monitorear znode Variedad.

Las cerraduras distribuidas se pueden realizar a través de las dos características anteriores.

 

La figura anterior es el diagrama de flujo de la implementación de bloqueos distribuidos de Zookeeper. La siguiente es la implementación del código.

Código

 

Crea una ZkDistributeLockclase que implemente la interfaz Lock

@Slf4j
public class ZkDistributeLock implements Lock {

    @Autowired
    private ZookeeperConfig config;

    private String lockPath;

    private ZkClient client;

    private String currentPath;

    private String beforePath;
    ...
}

 

El nodo raíz del bloqueo se crea en el constructor.

public ZkDistributeLock(String lockPath) {
    super();
    this.lockPath = lockPath;
    ZookeeperConfig zookeeperConfig = new ZookeeperConfig();
    this.client = zookeeperConfig.getConnectionWithoutSpring();
    this.client.setZkSerializer(new MyZkSerializer());

    if (!this.client.exists(lockPath)) {
        try {
            this.client.createPersistent(lockPath);
        } catch (ZkNodeExistsException e) {
            e.getStackTrace();
        }
    }
}

 

lock()Método de escritura

@Override
public void lock() {

    if (!tryLock()) {
        //没获得锁,阻塞自己
        waitForLock();
        //再次尝试
        lock();
    }

}

tryLock()Método de escritura

public boolean tryLock() { 
//不会阻塞 //创建节点 
if (this.currentPath == null) { 
    currentPath = this.client.createEphemeralSequential(lockPath + "/", "lock"); 
} 
List<String> children = this.client.getChildren(lockPath); 
Collections.sort(children); 
if (currentPath.equals(lockPath + "/" + children.get(0))) { 
    return true; 
} else {
     int currentIndex = children.indexOf(currentPath.substring(lockPath.length() + 1));             
     beforePath = lockPath + "/" + children.get(currentIndex - 1); 
} 
log.info("锁节点创建成功:{}", lockPath); 
return false;
}

unlock()Método de escritura

@Override
public void unlock() {
    client.delete(this.currentPath);
}

waitForLock()Método de escritura

private void waitForLock() {

    CountDownLatch count = new CountDownLatch(1);
    IZkDataListener listener = new IZkDataListener() {
        @Override
        public void handleDataChange(String s, Object o) throws Exception {

        }

        @Override
        public void handleDataDeleted(String s) throws Exception {
            System.out.println(String.format("收到节点[%s]被删除了",s));
            count.countDown();
        }
    };

    client.subscribeDataChanges(this.beforePath, listener);

    //自己阻塞自己
    if (this.client.exists(this.beforePath)) {
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //取消注册
    client.unsubscribeDataChanges(this.beforePath, listener);
}

 

prueba

//创建订单时加锁
@Override
public void createOrder() {

    String lockPath = "/distribute_lock";
    String orderCode = null;
    //zk 分布式锁
    Lock lock = new ZkDistributeLock(lockPath);
    // zkDistributeLock.setLockPath(lockPath);
    lock.lock();
    try {
        orderCode = ocg.getOrderCode();
    } finally {
        lock.unlock();
    }

    log.info("当前线程:{},生成订单编号:{}",Thread.currentThread().getName() , orderCode);
    //其他逻辑

}

@Test
public void testDisLock() {
    //并发数
    int currency = 20;

    //循环屏障
    CyclicBarrier cyclicBarrier = new CyclicBarrier(currency);

    for (int i = 0; i < currency; i++) {
        new Thread(() -> {
            // OrderServiceImplWithDisLock orderService = new OrderServiceImplWithDisLock();
            System.out.println(Thread.currentThread().getName() + "====start====");
            //等待一起出发
            try {
                //CyclicBarrier共享锁模拟并发
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            orderService.createOrder();
        }).start();

    }
}

 

Ejecute el método de prueba y el nodo de bloqueo se generará en zookeeper.

 

Si el hilo agarra el bloqueo, mostrará que el nodo de bloqueo se creó correctamente

 

Y ejecuta el código dentro de la cerradura

 

No se puede agarrar la cerradura, se bloqueará, esperando que se libere la cerradura

 

Cuando se libera el bloqueo, el nodo se elimina y se notifica al mecanismo wacther

El hilo que agarra el candado ejecuta las tareas en el hilo actual

 

código github: [leer original] visible

Recomendado en el pasado

Escanee el código QR para ser más emocionante. O busque Lvshen_9 en WeChat , puede responder para obtener información en segundo plano

1.回复"java" 获取java电子书;

2.回复"python"获取python电子书;

3.回复"算法"获取算法电子书;

4.回复"大数据"获取大数据电子书;

5.回复"spring"获取SpringBoot的学习视频。

6.回复"面试"获取一线大厂面试资料

7.回复"进阶之路"获取Java进阶之路的思维导图

8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

9.回复"总结"获取Java后端面试经验总结PDF版

10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

Otro: haga clic en [ Mis beneficios ] para tener más sorpresas.

Supongo que te gusta

Origin blog.csdn.net/wujialv/article/details/108437963
Recomendado
Clasificación