1. Introdução a bloqueios distribuídos
Os bloqueios distribuídos são usados principalmente para proteger os recursos compartilhados entre os processos, os hosts e as redes em um ambiente distribuído para obter acesso mutuamente exclusivo para garantir a consistência dos dados.
2. Introdução à arquitetura
Antes de apresentar o uso do Zookeeper para implementar bloqueios distribuídos, primeiro veja o diagrama de arquitetura do sistema atual
Explicação: Toda a área à esquerda representa um cluster de Zookeeper. O Locker é um nó persistente do Zookeeper e Node_1, Node_2 e Node_3 são nós seqüenciais temporários sob o nó persistente do Locker. Client_1, Client_2, Client_N significa vários clientes, e o serviço significa recursos compartilhados que exigem acesso mutuamente exclusivo.
Idéias para a aquisição de bloqueios distribuídos
1. Ideia geral de obter bloqueios distribuídos
Ao adquirir um bloqueio distribuído, crie um nó seqüencial temporário no nó do armário e exclua o nó temporário ao liberar o bloqueio. O cliente chama o método CreateNode para criar nós seqüenciais temporários no Locker e, em seguida, chama GetChildren ("Locker") para obter todos os nós filhos no Locker. Observe que nenhum observador é necessário no momento. Depois que o cliente obtém todos os caminhos de nós da criança, se descobrir que o número do nó filho que criou antes é o menor, considera -se que o cliente obteve o bloqueio. Se você achar que o nó que você criou não é o menor entre todos os filhos do armário, significa que você não obteve a fechadura. No momento, o cliente precisa encontrar o nó menor que ele e, em seguida, ligue para o método exist () nele e registrar o ouvinte do evento nele. Depois disso, se o nó que você estiver em questão for excluído, o observador do cliente receberá a notificação correspondente. Neste momento, você determinará novamente se o nó que você criou é o menor número de série entre os nós do Locker Child. Rugao obteve a fechadura. Caso contrário, repita as etapas acima para continuar a obter um nó menor que você e registre -se para ouvir. Ainda há muitos julgamentos lógicos necessários no processo atual.
2. O processo do algoritmo principal para adquirir bloqueios distribuídos
A seguir, é apresentado o mesmo fluxograma para analisar o algoritmo completo para a aquisição de bloqueios distribuídos, como segue:
Explicação: Quando o Cliente A quiser adquirir um bloqueio distribuído, primeiro crie um nó seqüencial temporário (Node_N) no Locker e, em seguida, obtenha imediatamente todos os nós filhos (de primeiro nível) no Locker.
Nesse momento, como vários clientes competirão por bloqueios ao mesmo tempo, o número de nós filhos no Locker será maior que 1. Para nós seqüenciais, a característica é que existe um número numérico após o nome do nó. O número do número do nó criado primeiro é menor que o criado posteriormente. Portanto, os nós filhos podem ser classificados de pequeno a grande na ordem do sufixo do nome do nó. Dessa forma, o primeiro é o nó seqüencial criado primeiro. Neste momento, representa o cliente que primeiro se esforça para a fechadura! No momento, determine se o menor nó é o node_n criado pelo cliente A antes. Nesse caso, significa que o cliente A adquiriu o bloqueio. Caso contrário, significa que o bloqueio foi adquirido por outros clientes. Portanto, o cliente A deve esperar que ele libere o bloqueio, ou seja, o cliente B que adquiriu o bloqueio exclui o nó que criou.
No momento, saberemos se o cliente B lançou o bloqueio ouvindo o evento de exclusão dos nós seqüenciais menores que o Node_N Times. Nesse caso, o cliente A adquire todas as crianças novamente no armário e as compara com os nós Node_N criados por si só até que o node_n criado por si só seja o menor número de sequência entre todos os filhos do armário, o que significa que o cliente A adquiriu a fechadura!
4. Implementação de código de bloqueios distribuídos com base em Zookeeper
1. Defina uma interface de bloqueio distribuída
A interface de bloqueio distribuída definida é a seguinte:
interface pública distributedlock { / ** adquira o bloqueio, se não for obtido, espera* / public void adquirir () lança exceção; / *** Adquira o bloqueio até o tempo limite* @param Tempo de tempo limite* @param unidade unidade unidade de tempo parâmetro unidade* @return se o bloqueio é obtido* @throws exceção*/ public boolean adquirir (longa data, unidade de unidade de tempo) lança exceção; / *** Libere a exceção do bloqueio* @THOWS*/ public void Release () lança exceção;}2. Defina um mutex simples
Definir uma classe de bloqueio mutex, implementar a interface de bloqueio definida acima e herdar uma classe baseada em classe baseada. Esta classe base é usada principalmente para interagir com o Zookeeper, incluindo um método para tentar adquirir o bloqueio e um bloqueio de liberação.
/** A implementação específica da interface de bloqueio é alcançada principalmente pelo herdado da classe pai baseada em base. A classe pai é implementada com base nos detalhes específicos do Zookeeper para implementar bloqueios distribuídos* /classe pública SimpledStribedLockMutex estende baseado em base que os implementos distribuídos de distribuição { /*usados para salvar o nó que implementa bloqueios distribuídos em zookeeper, como o nome do nomes: /Locker,*esse nó deve ser um NONT, que deve ser um nó. Crie nós seqüenciais temporários neste nó para implementar bloqueios distribuídos*/ Basepath de string final privada; /*Nome do bloqueio prefixo. Por exemplo, os nós seqüenciais criados no Locker iniciam com o bloqueio, que facilita a filtragem de nós irrelevantes*Os nós criados são semelhantes a: Lock-00000001, Lock-00000002*/ String private StaticFinal Lock_name = "Lock-"; /* Usado para salvar os nós seqüenciais bem -sucedidos criados por um cliente no Locker, para operações relacionadas subsequentes (como julgamento)*/ String privada OurLockPath; /*** Usado para adquirir recursos de bloqueio e obter bloqueios através do método de aquisição de bloqueio da classe pai* Horário de @param para adquirir o tempo de tempo limite do bloqueio* @param unidade de tempo unidade tempo* @return Se a bloqueio é obtida* @THURSCECTION*/PRIVATION NOTLOCK (Long Time Unit) é obtido a exceção que a exceção. Para detalhes, consulte a implementação do TIPLOCK. OurLockPath = Tentlock (tempo, unidade); Retornar OurLockPath! = NULL; } /** * Pass in the Zookeeper client connection object, and basePath * @param client Zookeeper client connection object* @param basePath basePath is a persistent node*/ public SimpleDistributedLockMutex(ZkClientExt client, String basePath){ /*Calling the constructor of the parent class creates a basePath node in Zookeeper, and sets a prefix for the basePath node child Nó *Salve a referência do BasePath ao atributo atual da classe */ super (cliente, base, bloqueio, lock_name); this.Basepath = Basepath; } /** adquirindo o bloqueio até o tempo limite, e uma exceção é lançada após o tempo limite* /public void adquirir () lança exceção {//-1 significa que o tempo limite não está definido e o tempo limite é determinado pela Zookeeper se (! }} / *** Adquira o bloqueio com o tempo limite* / public boolean adquirir (longa data, unidade de unidade de tempo) lança a exceção {return Internallock (tempo, unidade); } / ** Libere o bloqueio* / public void release () lança exceção {releaselock (OurLockPath); }}3. Detalhes da implementação de bloqueios distribuídos
A lógica da chave para a aquisição de bloqueios distribuídos é baseada em Lock, que implementa os detalhes da implementação de bloqueios distribuídos com base no Zookeeper.
public class BedidoDistributedLock {private final ZKClientExt Client; Caminho final de string privado; Basepath de String String privado; String final privado String Lockname; Inteiro final estático privado max_retry_count = 10; public baseado no cliente (ZKCLIENTEXT Client, String Path, String LockName) {this.client = client; this.Basepath = Path; this.path = path.concat ("/"). concat (lockname); this.lockname = LockName; } private void DeleteourPath (String OurPath) lança exceção {client.Delete (OurPath); } String privada CreateLockNode (cliente zkclient, caminho da string) lança a exceção {return client.createpheMeralSealquencial (path, null); } / ** * Método principal para obter bloqueio * @param startMillis * @param Millistowait * @param OurPath * @return * @throws Exceção * / private boolean waittolock (startmillis longo, longa millistowait, string durath) joga exceção {boolean Havethelock = false; boolean dodelete = false; tente {while (! HaveThelock) {// Este método implementa a aquisição de todos os nós seqüenciais no nó do armário e classifica de pequena a grande lista <String> Children = getSortEdChildren (); String sequencenodename = OurPath.substring (bashepath.length ()+1); // Calcule a posição de classificação dos nós do pedido criados pelo cliente agora em todos os nós filhos do armário. Se o tipo for 0, significa que o bloqueio foi obtido. int usendex = Children.IndexOF (sequencenodename); /*Se o nó de ordem [temporário] que eu criei antes não foi encontrado no getSortedChildren, isso significa que o nó que criamos pode ser excluído devido a uma quebra de flash de rede. A exceção precisa ser jogada. Deixe o nível anterior lidar com o *Nível anterior é capturar a exceção e executar o número especificado de repetições. Consulte o método de tentativa no método de tentativa subsequente*/ if (OurIndex <0) {tiro o novo zknonODeexception ("não encontrado:" + sequencenodename); } // Se o nó criado pelo cliente atual for maior que 0 na lista de nós do Locker Child, isso significa que outros clientes adquiriram o bloqueio // No momento, o cliente atual precisa esperar que outros clientes liberassem o bloqueio, boolean isGetThelock = OurIndex == 0; // Como determinar se outros clientes lançaram o bloqueio? Obtenha o nó menor que ele na lista de nós filhos e configure uma sessão de escuta para It String Pathtowatch = ISGETTHELOCK? nulo: filhos.get (OurIndex - 1); if (isGetThelock) {HaveThelock = true; } else {// Se o nó menor for excluído, significa que o nó do cliente atual deve ser o menor; portanto, use o CountDownLatch para realizar a string de espera anteriores da String anteriores = Basepath .CONCAT ("/") .CONCAT (Pathtowatch); Final CountdownLatch Latch = new Countdownlatch (1); final izkdatalistener anteriorlistener = new izkdatalistener () {// Quando ocorre um pequeno evento de exclusão de nó, deixe o CountdownLatch termina e aguarde // Neste momento, você precisa deixar o programa voltar e fazer um novo julgamento! public void HandledAtadeleted (string datapath) lança a exceção {latch.countdown (); } public void handledatachange (string datapath, objeto dados) lança a exceção {// ignore}}; tente {// exceção se o nó não existir client.subSccrincedATACHANGS (PRIESTENSECHENCE, PRINCIPAL); if (Millistowait! = NULL) {Millistowait - = (System.CurrentTimemillis () - StartMillis); startMillis = system.currenttimemillis (); if (Millistowait <= 0) {dodelete = true; // cronometrado - exclua nossa quebra de nós; } latch.await (Millistowait, timeUnit.microseconds); } else {latch.await (); }} catch (zknonODeexception e) {// ignore} finalmente {client.unsubSccrincedAtACHANGS (ANTESENSTEMENSECHENCE, AnteriorListener); }}}}} catch (Exceção e) {// A exceção precisa ser excluída dodelete = true; jogar e; } finalmente {// se você precisar excluir o nó se (dodelete) {deleteourPath (OurPath); }} retornar havethelock; } String privada getLockNodenumber (String str, String LockName) {int index = str.LastIndexOf (LockName); if (index> = 0) {index += LockName.Length (); Índice de retorno <= str.Length ()? str.substring (índice): ""; } retornar str; } Lista privada <String> getSortEdChildren () lança a exceção {try {list <string> children = client.getChildren (baseepath); Coleções.sort (crianças, novo comparador <string> () {public int compare (string lhs, string rhs) {return getlocknodenumber (lhs, lockname) .compareto (getlocknodenumber (rhs, nome de bloqueio);}}); retornar crianças; } catch (zknonODeexception e) {client.createPersistent (baseepath, true); retorno getSortedChildren (); }} void protegido Releaselock (String LockPath) lança exceção {DeleteourPath (LockPath); } String protegida TINTLOCK (LONGO TEMPO, UNIDADE DE TEMPORIDADE) lança exceção {final long startMillis = System.currenttimemillis (); Final Longo Millistowait = (unidade! = NULL)? unit.tomillis (tempo): nulo; String OurPath = null; booleano hashelock = false; boolean isDone = false; int retryCount = 0; // quebra de flash líquido requer tentar novamente enquanto (! Isdone) {isdone = true; tente {// createLockNode é usado para criar o nó de ordem [temporário] para o cliente adquirir o bloqueio em Locker (nó Basepath persistente). OurPath = CreateLockNode (cliente, caminho); / *** Esse método é usado para determinar se o bloqueio foi obtido, ou seja, se os nós do pedido criados por você são os menores entre todos os nós filhos do armário* se o bloqueio não for adquirido, aguarde a liberação do bloqueio a ser liberado e tente novamente, até que o bloqueio seja adquirido ou timizado*/ hashelock = Waittolock (StartMillis, } catch (zknonODeexception e) {if (retrycount ++ <max_retry_count) {isdone = false; } else {tiro e; }}}} if (hasthelock) {return OurPath; } retornar nulo; }O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.