Entrevistador de Ali: ¿Es mejor usar Redis para bloqueos distribuidos? ¿O es Zookeeper mejor?

 

En primer lugar, el bloqueo distribuido es básicamente el mismo que el principio de bloqueo del que solemos hablar El propósito es garantizar que cuando varios subprocesos son concurrentes, solo un subproceso opera el negocio o el método o la variable al mismo tiempo.

En un proceso, es decir, un jvm o una aplicación, es fácil para nosotros manejar el control.El paquete concurrente jdk java.util nos ha proporcionado estos métodos para bloquear, como la palabra clave sincronizada o Lock lock, que pueden ser tratar con.

Sin embargo, si nuestra aplicación actual solo despliega un servidor, la concurrencia es muy pobre, si hay decenas de miles de solicitudes al mismo tiempo, es probable que cause una presión excesiva en el servidor y parálisis.

Piense en los escenarios comerciales, como los sobres rojos de Alipay a las 10:00 p. m. en Double Eleven y 30, naturalmente necesita usar varios servidores para procesar estos servicios al mismo tiempo, entonces puede haber cientos de estos servicios procesándose al mismo tiempo.

Pero piénselo, si hay 100 servidores para manejar el negocio de los sobres rojos, y ahora supongamos que hay 100 millones de sobres rojos, 10 millones de personas y la cantidad es aleatoria, entonces en este escenario comercial, ¿es necesario asegurarse los 10 millones de sobres rojos? La cantidad total de sobres rojos distribuidos por individuos al final es igual a 100 millones.

Si no se maneja bienSi a cada persona se le asigna 1 millón, entonces el padre de Jack Ma estima que el primer día del nuevo año tendrá que declararse en bancarrota.

1. ¿Qué sucede con las cerraduras convencionales?

En primer lugar, hablemos de por qué queremos construir un clúster. El entendimiento simple es que la demanda (concurrencia de solicitudes) se ha vuelto más grande y un trabajador tiene una capacidad de procesamiento limitada, por lo que se reclutarán más trabajadores para manejarla juntos.

Suponga que 10 millones de solicitudes se distribuyen uniformemente a 100 servidores, y cada servidor recibe 10 solicitudes (estas solicitudes de 10 solicitudes no llegan en el mismo segundo, tal vez dentro de 1,2 horas, puede imaginar nuestros tres sobres rojos abiertos en la décima noche, espere hasta las 10:20, algunas personas lo abren de inmediato y otras no lo recuerdan hasta las 12:00 ~)

En este caso, el número medio de solicitudes por segundo es inferior a 1.000, lo que aún puede ser tolerado por un servidor con presión general.

Después de que llega la primera solicitud, ¿necesita darle una parte de los 100 millones? La cantidad es aleatoria, suponiendo que la primera persona obtenga 100, ¿es necesario restar 100 de los 100 millones, dejando 99999900 ~

El segundo usuario dividirá nuevamente, y la cantidad es aleatoria. Esta vez, se dividirá en 200 yuanes. ¿Eso significa que se deben restar 200 yuanes de los 99999900 yuanes restantes, lo que deja 99999700 yuanes?

等到第10w个用户来,一看还有1000w,那这1000w全成他的了。

等于是在每个服务器中去分1亿,也就是10w个用户分了一个亿,最后总计有100个服务器,要分100亿。

如果真这样了,虽说马云爸爸不会破产(据最新统计马云有2300亿人民币),那分红包的开发项目组,以及产品经理,可以GG了~

简化结构图如下:

2,分布式锁怎么去处理?

 那么为了解决这个问题,让1000万用户只分1亿,而不是100亿,这个时候分布式锁就派上用处了。

分布式锁可以把整个集群就当作是一个应用一样去处理,那么也就需要这个锁,要独立于每一个服务之外,而不是在服务里面。

假设第一个服务器接收到用户1的请求后,那么这个时候,他就不能只在自己的应用中去判断还有多少钱可以分了,而需要去外部请求专门负责管理这1亿红包的人(服务),问他:哎,我这里要分100块,给我100。

管理红包的妹子(服务)一看,还有1个亿,那好,给你100块,然后剩下99999900块。

第二个请求到来后,被服务器2获取,继续去询问,管理红包的妹子,我这边要分10块,管理红包的妹子先查了下还有99999900,那就说:好,给你10块。那就剩下99999890块 

等到第1000w个请求到来后,服务器100拿到请求,继续去询问,管理红包的妹子,你要100,妹子翻了翻白眼,对你说,就剩1块了,爱要不要,那这个时候就只能给你1块了(1块也是钱啊,买根辣条还是可以的)。

这些请求编号1,2不代表执行的先后顺序,正式的场景下,应该是 100台服务器每个服务器持有一个请求去访问负责管理红包的妹子(服务),那在管红包的妹子那里同时会接收到100个请求,这个时候就需要在负责红包的妹子那里加个锁就可以了(抛绣球),你们100个服务器谁拿到锁(抢到绣球),谁就进来和我谈,我给你分,其他人就等着去吧

经过上面的分布式锁的处理后,马云爸爸终于放心了,决定给红包团队每人加一个鸡腿。

简化的结构图如下:

3,分布式锁的实现有哪些?

 说到分布式锁的实现,还是有很多的,有数据库方式的,有redis分布式锁,有zookeeper分布式锁等等

我们如果采用redis作为分布式锁,那么上图中负“责红包的妹子(服务)”,就可以替换成redis,请自行脑补。

3.1,为什么redis可以实现分布式锁?

首先redis是单线程的,这里的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

在实际的操作中过程大致是这样子的:

服务器1要去访问发红包的妹子,也就是redis,那么他会在redis中通过"setnx key value" 操作设置一个key 进去,value是啥不重要,重要的是要有一个key,也就是一个标记,而且这个key你爱叫啥叫啥,只要所有的服务器设置的key相同就可以。

假设我们设置一个,如下图

那么我们可以看到会返回一个1,那就代表了成功。

如果再来一个请求去设置同样的key,如下图:

这个时候会返回0,那就代表失败了。

那么我们就可以通过这个操作去判断是不是当前可以拿到锁,或者说可以去访问“负责发红包的妹子”,如果返回1,那我就开始去执行后面的逻辑,如果返回0,那就说明已经被人占用了,我就要继续等待。

当服务器1拿到锁之后,进行了业务处理,完成后,还需要释放锁,如下图所示:

删除成功返回1,那么其他的服务器就可以继续重复上面的步骤去设置这个key,以达到获取锁的目的。

当然以上的操作是在redis客户端直接进行的,通过程序调用的话,肯定就不能这么写,比如java 就需要通过jedis 去调用,但是整个处理逻辑基本都是一样的

通过上面的方式,我们好像是解决了分布式锁的问题,但是想想还有没有什么问题呢??

 对,问题还是有的,可能会有死锁的问题发生,比如服务器1设置完之后,获取了锁之后,忽然发生了宕机。

那后续的删除key操作就没法执行,这个key会一直在redis中存在,其他服务器每次去检查,都会返回0,他们都会认为有人在使用锁,我需要等。

为了解决这个死锁的问题,我们就就需要给key 设置有效期了。

 设置的方式有2种

1,第一种就是在set完key之后,直接设置key的有效期 "expire key timeout" ,为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

这种方式相当于,把锁持有的有效期,交给了redis去控制。如果时间到了,你还没有给我删除key,那redis就直接给你删了,其他服务器就可以继续去setnx获取锁。

2,第二种方式,就是把删除key权利交给其他的服务器,那这个时候就需要用到value值了,

比如服务器1,设置了value 也就是 timeout 为 当前时间+1 秒 ,这个时候服务器2 通过get 发现时间已经超过系统当前时间了,那就说明服务器1没有释放锁,服务器1可能出问题了,

服务器2就开始执行删除key操作,并且继续执行setnx 操作。

但是这块有一个问题,也就是,不光你服务器2可能会发现服务器1超时了,服务器3也可能会发现,如果刚好,服务器2,setnx操作完成,服务器3就接着删除,是不是服务器3也可以setnx成功了?

那就等于是服务器2和服务器3都拿到锁了,那就问题大了。这个时候怎么办呢?

这个时候需要用到  “GETSET  key value” 命令了。这个命令的意思就是获取当前key的值,并且设置新的值。

假设服务器2发现key过期了,开始调用 getset 命令,然后用获取的时间判断是否过期,如果获取的时间仍然是过期的,那就说明拿到锁了。

如果没有,则说明在服务2执行getset之前,服务器3可能也发现锁过期了,并且在服务器2之前执行了getset操作,重新设置了过期时间。

那么服务器2就需要放弃后续的操作,继续等待服务器3释放锁或者去监测key的有效期是否过期。

这块其实有一个小问题是,服务器3已经修改了有效期,拿到锁之后,服务器2,也修改了有效期,但是没能拿到锁,但是这个有效期的时间已经被在服务器3的基础上有增加一些,但是这种影响其实还是很小的,几乎可以忽略不计。

3.2,为什么zookeeper可以实现分布式锁?

百度百科是这么介绍的:ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。

那对于我们初次认识的人,可以理解成ZooKeeper就像是我们的电脑文件系统,我们可以在d盘中创建文件夹a,并且可以继续在文件夹a中创建 文件夹a1,a2。

那我们的文件系统有什么特点??那就是同一个目录下文件名称不能重复,同样ZooKeeper也是这样的。

 在ZooKeeper所有的节点,也就是文件夹称作 Znode,而且这个Znode节点是可以存储数据的。

我们可以通过“ create /zkjjj nice” 来创建一个节点,这个命令就表示,在跟目录下创建一个zkjjj的节点,值是nice。同样这里的值,和我在前面说的redis中的一样,没什么意义,你随便给。

另外ZooKeeper可以创建4种类型的节点,分别是:

1,持久性节点

2,持久性顺序节点

3,临时性节点

4,临时性顺序节点

首先说下持久性节点和临时性节点的区别,持久性节点表示只要你创建了这个节点,那不管你ZooKeeper的客户端是否断开连接,ZooKeeper的服务端都会记录这个节点。

临时性节点刚好相反,一旦你ZooKeeper客户端断开了连接,那ZooKeeper服务端就不再保存这个节点。

再说下顺序性节点,顺序性节点是指,在创建节点的时候,ZooKeeper会自动给节点编号比如0000001 ,0000002 这种的。

最后说下,zookeeper有一个监听机制,客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)等,zookeeper会通知客户端。

A continuación, continuaremos describiendo cómo bloquear a Zookeeper en combinación con nuestro escenario de distribución de paquetes rojos anterior.

Suponiendo que el servidor 1 crea un nodo /zkjjj, si tiene éxito, entonces el servidor 1 adquiere el bloqueo y el servidor 2 crea el mismo bloqueo, luego fallará y, en este momento, solo puede monitorear los cambios de este nodo.

Después de que el servidor 1 termine de procesar el negocio y elimine el nodo, se le notificará y luego creará el mismo nodo, obtendrá el bloqueo para procesar el negocio y luego eliminará el nodo. Los 100 servidores siguientes son similares.

Tenga en cuenta que los 100 servidores aquí no realizan las operaciones anteriores de crear nodos uno por uno, sino al mismo tiempo. Cuando el servidor 1 se crea con éxito, los 99 restantes se registrarán para escuchar este nodo, esperar notificaciones, etc.

Pero, ¿ha notado que todavía hay un problema aquí y que todavía habrá puntos muertos, verdad?

Cuando el servidor 1 se bloquea después de crear un nodo y no puede eliminarlo, los otros 99 servidores seguirán esperando la notificación y habrá terminado. . .

En este momento necesitas usar nodos temporales, como dijimos antes, la característica de los nodos temporales es que una vez que se desconecta el cliente, se pierde, es decir, cuando el servidor 1 crea un nodo, si se cuelga.

Luego, este nodo se eliminará automáticamente, de modo que otros servidores posteriores puedan continuar creando nodos y adquiriendo bloqueos.

Pero es posible que también debamos prestar atención a una cosa, ese es el impactante efecto de manada: para dar un ejemplo muy simple, cuando arrojas un trozo de comida a un grupo de palomas, aunque solo una paloma finalmente agarra la comida, todos los las palomas serán alertadas para competir, no agarrar..

Es decir, cuando el nodo del servidor 1 cambie, se notificará a los 99 servidores restantes, pero al final solo se creará con éxito 1 servidor, por lo que el 98 aún debe esperar para ser monitoreado, por lo que para lidiar con esta situación, un servidor temporal se necesita un nodo secuencial.

El significado general es que antes los 99 servidores escuchaban un nodo, ahora cada servidor escucha un nodo frente a sí mismo.

Suponiendo que 100 servidores envían solicitudes al mismo tiempo, se crearán 100 nodos secuenciales temporales /zkjjj/000000001, /zkjjj/000000002 bajo el nodo /zkjjj en este momento, hasta /zkjjj/000000100, este número es igual al que tienen sido establecido El orden en que se adquieren los bloqueos.

Cuando se procesa el nodo 001 y se elimina el nodo, 002 recibe la notificación para adquirir el bloqueo y comenzar la ejecución. Una vez completada la ejecución, el nodo se elimina y se notifica a 003~ y así sucesivamente.

Supongo que te gusta

Origin juejin.im/post/7086803252221575182
Recomendado
Clasificación