Introdução
Algumas das propriedades de configuração encontradas no Apache Hadoop têm um efeito direto nos clientes, como o Apache HBase. Uma dessas propriedades é chamada de "dfs.datanode.max.xcievers" e pertence ao subprojeto HDFS. Ele define o número de threads do lado do servidor e – até certo ponto – soquetes usados para conexões de dados. Definir esse número muito baixo pode causar problemas à medida que você aumenta ou aumenta a utilização do cluster. Este post ajudará você a entender o que acontece entre o cliente e o servidor e como determinar um número razoável para essa propriedade.
O problema
Como o HBase está armazenando tudo o que precisa dentro do HDFS, o limite superior rígido imposto pela propriedade de configuração "dfs.datanode.max.xcievers" pode resultar em poucos recursos disponíveis para o HBase, manifestando-se como IOExceptions em ambos os lados da conexão. Aqui está um exemplo da lista de discussão do HBase [1], onde as seguintes mensagens foram inicialmente registradas no lado do RegionServer:
2008-11-11 19:55:52,451 INFO org.apache.hadoop.dfs.DFSClient: Exception in createBlockOutputStream java.io.IOException:Não foi possível ler do stream
2008-11-11 19:55:52,451 INFO org.apache.hadoop.dfs.DFSClient: Abandoning block blk_-5467014108758633036_595771
2008-11- 11 19:55:58,455 WARN org.apache.hadoop.dfs.DFSClient: DataStreamer Exception:java.io.IOException:Não é possível criar novo bloco.
2008-11-11 19:55:58,455 WARN org.apache .hadoop.dfs.DFSClient:Erro de recuperação do bloco blk_-5467014108758633036_595771 bad datanode[0]
2008-11-11 19:55:58,482 FATAL org.apache.hadoop.hbase.regionserver.Flusher:repetição do hlog necessária . Forçando o desligamento do servidor
Correlacionando isso com os logs do Hadoop DataNode revelou a seguinte entrada:
ERRO org.apache.hadoop.dfs.DataNode: DatanodeRegistration(10.10.10.53:50010,storageID=DS-1570581820-10.10.10.53-50010-1224117842339,infoPort=50075, ipcPort=50020):DataXceiver:java.io.IOException: xceiverCount 258 excede o limite de xcievers simultâneos 256
Neste exemplo, o valor baixo de “dfs.datanode.max.xcievers” para os DataNodes fez com que todo o RegionServer fosse encerrado. Esta é uma situação muito ruim. Infelizmente, não existe uma regra rígida que explique como calcular o limite necessário. Geralmente, é aconselhável aumentar o número do padrão de 256 para algo como 4096 (consulte [1], [2], [3], [4] e [5] para referência). Isso é feito adicionando esta propriedade ao arquivo hdfs-site.xml de todos os DataNodes (observe que está incorreto):
Observação:você precisará reiniciar seus DataNodes após fazer essa alteração no arquivo de configuração. public DFSInputStream open(String src) lança IOException A instância de fluxo retornada é o que precisa de um soquete e thread do lado do servidor, que são usados para ler e gravar blocos de dados. Eles fazem parte do contrato de troca de dados entre o cliente e o servidor. Observe que existem outros protocolos baseados em RPC em uso entre as várias máquinas, mas para fins desta discussão eles podem ser ignorados. this.threadGroup =new ThreadGroup(“dataXceiverServer”); Observe que o encadeamento DataXceiverServer já está ocupando um ponto do grupo de encadeamentos. O DataNode também possui esta classe interna para recuperar o número de threads atualmente ativos neste grupo: /** Número de xceivers simultâneos por nó. */ Os blocos de leitura e gravação, conforme iniciados pelo cliente, fazem com que uma conexão seja estabelecida, que é encapsulada pelo encadeamento DataXceiverServer em uma instância DataXceiver. Durante esta entrega, um thread é criado e registrado no grupo de threads acima. Portanto, para cada operação ativa de leitura e gravação, um novo thread é rastreado no lado do servidor. Se a contagem de threads no grupo exceder o máximo configurado, a referida exceção é lançada e registrada nos logs do DataNode: if (curXceiverCount> dataXceiverServer.maxXceiverCount) { LOG.debug(“O número de conexões ativas é:” + datanode.getXceiverCount()); e monitore durante a inicialização do HBase o que está registrado no DataNode. Para simplificar, isso é feito em uma configuração pseudodistribuída com uma única instância DataNode e RegionServer. O seguinte mostra a parte superior da página de status do RegionServer. $ bin/start-hbase.sh Log do DataNode: 2012-03-05 13:01:35,309 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:O número de conexões ativas é:1 2012-03-05 13:01:41,246 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:O número de conexões ativas é:6 Você pode ver como as regiões são abertas uma após a outra, mas o que você também pode notar é que o número de conexões ativas nunca chega a 22 – mal chega a 10. Por que é que? Para entender isso melhor, precisamos ver como os arquivos no HDFS são mapeados para a instância do DataXceiver do lado do servidor – e os threads reais que eles representam. “DataXceiver for client /127.0.0.1:64281 [enviando bloco blk_5532741233443227208_4201]” daemon prio=5 tid=7fb96481d000 nid=0x1178b4000 executável [1178b3000] “DataXceiver para cliente /127.0.0.1:64172 [bloco de recebimento blk_-2005512129579433420_4199 client=DFSClient_hb_rs_10.0.0.29 ,60020,1330984111693_1330984118810]” daemon prio=5 tid=7fb966109000 nid=0x1169cb000 executável [1169ca000] Essas são as únicas entradas do DataXceiver (neste exemplo), portanto, a contagem no grupo de threads é um pouco enganosa. Lembre-se de que o encadeamento do daemon DataXceiverServer já representa uma entrada extra, que combinada com as duas acima contabiliza as três conexões ativas – o que na verdade significa três encadeamentos ativos. A razão pela qual o log indica quatro, é que ele registra a contagem de um encadeamento ativo que está prestes a terminar. Então, logo após a contagem de quatro ser registrada, na verdade é um a menos, ou seja, três e, portanto, correspondendo à nossa contagem de threads ativos. “PacketResponder 0 for Block blk_-2005512129579433420_4199” daemon prio=5 tid=7fb96384d000 nid=0x116ace000 in Object.wait () [116acd000] Esta conversa está atualmente no estado TIMED_WAITING e não é considerada ativa. É por isso que a contagem emitida pelas instruções de log do DataXceiver não inclui esse tipo de thread. Se eles se tornarem ativos devido ao envio de dados do cliente, a contagem de threads ativos aumentará novamente. Outra coisa a notar é que este thread não precisa de uma conexão separada, ou soquete, entre o cliente e o servidor. O PacketResponder é apenas um encadeamento no lado do servidor para receber dados de bloco e transmiti-los para o próximo DataNode no pipeline de gravação. $ hadoop fsck /hbase -openforwrite $ hadoop fsck /hbase -files -blocks … … Isso oferece duas coisas. Primeiro, o resumo indica que há um bloco de arquivo aberto no momento em que o comando foi executado – correspondendo à contagem relatada pela opção “-openforwrite” acima. Em segundo lugar, a lista de blocos ao lado de cada arquivo permite que você corresponda o nome da thread ao arquivo que contém o bloco que está sendo acessado. Neste exemplo o bloco com o ID “blk_5532741233443227208_4201” é enviado do servidor para o cliente, aqui um RegionServer. Este bloco pertence ao HBase .META. tabela, conforme mostrado pela saída do comando Hadoop fsck. A combinação de JStack e fsck pode servir como um substituto pobre para lsof (uma ferramenta na linha de comando do Linux para “listar arquivos abertos”). hbase(main):003:0> scan 'usertable' Log do DataNode: 2012-03-05 14:42:20,580 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:O número de conexões ativas é:6 O número de conexões ativas atinge o indescritível 22 agora. Observe que essa contagem já inclui o encadeamento do servidor, portanto, ainda estamos um pouco aquém do que poderíamos considerar o máximo teórico – com base no número de arquivos que o HBase precisa manipular.
Isso deve ajudar com o problema acima, mas você ainda pode querer saber mais sobre como tudo isso funciona e o que o HBase está fazendo com esses recursos. Discutiremos isso no restante deste post. Mas antes disso, precisamos esclarecer por que você não pode simplesmente definir esse número muito alto, digamos 64K, e pronto.
Há uma razão para um limite superior, e é duplo:primeiro, as threads precisam de sua própria pilha, o que significa que elas ocupam memória. Para servidores atuais, isso significa 1 MB por thread[6] por padrão. Em outras palavras, se você usar todos os threads do DataXceiver 4096, precisará de cerca de 4 GB de heap para acomodá-los. Isso reduz o espaço que você designou para memstores e caches de bloco, bem como todas as outras partes móveis da JVM. Na pior das hipóteses, você pode se deparar com uma OutOfMemoryException e o processo RegionServer é torrado. Você deseja definir essa propriedade para um número razoavelmente alto, mas também não muito alto.
Em segundo lugar, tendo esses muitos threads ativos, você também verá sua CPU ficando cada vez mais carregada. Haverá muitas trocas de contexto acontecendo para lidar com todo o trabalho simultâneo, o que tira recursos para o trabalho real. Assim como as preocupações com a memória, você deseja que o número de threads não cresça sem limites, mas forneça um limite superior razoável – e é para isso que serve “dfs.datanode.max.xcievers”.
Detalhes do sistema de arquivos do Hadoop
Do lado do cliente, a biblioteca HDFS está fornecendo a abstração chamada Path. Essa classe representa um arquivo em um sistema de arquivos suportado pelo Hadoop, representado pela classe FileSystem. Existem algumas implementações concretas da classe abstrata FileSystem, uma das quais é o DistributedFileSytem, representando o HDFS. Essa classe, por sua vez, envolve a classe DFSClient real que lida com todas as interações com os servidores remotos, ou seja, o NameNode e os muitos DataNodes.
Quando um cliente, como o HBase, abre um arquivo, ele o faz, por exemplo, chamando os métodos open() ou create() da classe FileSystem, aqui as encarnações mais simplistas
public FSDataOutputStream create(Path f) lança IOException
A instância de stream retornada é uma classe especializada DFSOutputStream ou DFSInputStream, que lida com toda a interação com o NameNode para descobrir onde residem as cópias dos blocos e a comunicação de dados por bloco por DataNode.
No lado do servidor, o DataNode envolve uma instância de DataXceiverServer, que é a classe real que lê a chave de configuração acima e também gera a exceção acima quando o limite é excedido.
Quando o DataNode é iniciado, ele cria um grupo de encadeamentos e inicia a instância DataXceiverServer mencionada assim:
this.dataXceiverServer =new Daemon( threadGroup,
novo DataXceiverServer(ss, conf, this));
this.threadGroup.setDaemon(true); // destruição automática quando vazio
int getXceiverCount() {
return threadGroup ==null ? 0 :threadGroup.activeCount();
}
throw new IOException(“xceiverCount ” + curXceiverCount
+ ” excede o limite de xcievers simultâneos ”
+ dataXceiverServer.maxXceiverCount);
}
Implicações para clientes
Agora, a questão é:como a leitura e a escrita do cliente se relacionam com os threads do lado do servidor. Antes de entrarmos nos detalhes, vamos usar as informações de depuração que a classe DataXceiver registra quando é criada e fechada
…
LOG.debug(datanode.dnRegistration + “:O número de conexões ativas é:” + datanode.getXceiverCount());
A parte importante está na seção “Metrics”, onde diz “storefiles=22”. Portanto, supondo que o HBase tenha pelo menos tantos arquivos para manipular, além de alguns arquivos extras para o log de gravação antecipada, devemos ver a mensagem de logs acima informando que temos pelo menos 22 “conexões ativas”. Vamos iniciar o HBase e verificar os arquivos de log DataNode e RegionServer:
Linha de comando:
…
2012-03-05 13:01:35,315 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS- 1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:2
12/03/05 13:01:35 INFO regionserver.MemStoreFlusher:globalMemStoreLimit=396,7m, globalMemStoreLimitLowMark=347.1m, maxHeap=991.7m
12/03/05 13:01:39 INFO http.HttpServer:Porta retornada por webServer.getConnectors()[0].getLocalPort() antes de open() é -1 . Abrindo o listener em 60030
2012-03-05 13:01:40,003 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:1
12/03/05 13:01:40 INFO regionserver.HRegionServer:Solicitação recebida para abrir a região:-ROOT-,,0.70236052
2012-03-05 13:01:40,882 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode :O número de conexões ativas é:3
2012-03-05 13:01:40,884 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448 -10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:4
2012-03-05 13:01:40,888 DEBUG org.apache.hadoop.hdfs.server. datanode.DataNode:O número de conexões ativas é:3
…
12/03/05 13:01:40 INFO regionserver.HRegion:Onlined -ROOT-,,0.70236052; next sequenceid=63083
2012-03-05 13:01:40,982 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:3
2012-03-05 13 :01:40.983 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020 O número de conexões ativas é:4
…
12/03/05 13:01:41 INFO regionserver.HRegionServer:Pedido recebido para abrir região:.META.,,1.1028785192
2012-03 -05 13:01:41,026 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:3
2012-03-05 13:01:41,027 DEBUG org.apache.hadoop. hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:4
…
12/03/05 13:01:41 INFO regionserver.HRegião:Onlined .META.,,1.1028785192; next sequenceid=63082
2012-03-05 13:01:41,109 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:3
2012-03-05 13 :01:41,114 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:4
2012-03-05 13:01:41,117 DEBUG org.apache.hadoop.hdfs.server .datanode.DataNode:O número de conexões ativas é:5
12/03/05 13:01:41 INFO regionserver.HRegionServer:Recebida solicitação para abrir 16 regiões
12/03/05 13 :01:41 INFO regionserver.HRegionServer:Pedido recebido para abrir região:usertable,,1330944810191.62a312d67981c86c42b6bc02e6ec7e3f.
12/03/05 13:01:41 INFO regionserver.HRegionServer:Pedido recebido para abrir região:usertable,user1120311784, 1330944810191.90d287473fe223f0ddc137020efda25d.
…
2012-03-05 13:01:41,248 DEBUG org.apache.hadoop.hdfs.server.dataNode.DataNode:O número de conexões ativas é:7
…
2012-03-05 13:01:41,257 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772 , infoPort=50075, ipcPort=50020):O número de conexões ativas é:10
2012-03-05 13:01:41,257 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0. 0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:9
…
12/03/05 13:01:41 INFO regionserver.HRegion:tabela de usuários online, user1120311784,1330944810191.90d287473fe223f0ddc137020efda25d.; next sequenceid=62917
12/03/05 13:01:41 INFO regionserver.HRegion:on-line usertable,,1330944810191.62a312d67981c86c42b6bc02e6ec7e3f.; next sequenceid=62916
…
12/03/05 13:01:41 INFO regionserver.HRegion:on-line usertable,user1361265841,1330944811370.80663fcf291e3ce00080599964f406ba.; next sequenceid=62919
2012-03-05 13:01:41,474 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:6
2012-03-05 13 :01:41,491 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:7
2012-03-05 13:01:41,495 DEBUG org.apache.hadoop.hdfs.server .datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:8
2012-03 -05 13:01:41,508 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:7
…
12/03/05 13:01:41 INFO regionserver .HRegion:tabela de usuários online,user1964968041,1330944848231.dd89596e9129e1caa7e07f8a491c9734.; next sequenceid=62920
2012-03-05 13:01:41,618 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:6
2012-03-05 13 :01:41,621 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020 O número de conexões ativas é:7
…
2012-03-05 13:01:41,829 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID =DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:7
12/03/05 13:01:41 INFO regionserver.HRegion:tabela de usuários online ,user515290649,1330944849739.d23924dc9e9d5891f332c337977af83d.; next sequenceid=62926
2012-03-05 13:01:41,832 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:6
2012-03-05 13 :01:41,838 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020 O número de conexões ativas é:7
12/03/05 13:01:41 INFO regionserver.HRegion:tabela de usuários online,user757669512,1330944850808.cd0d6f16d8ae9cf0c9277f5d6c6c6b9f.; next sequenceid=62929
…
2012-03-05 14:01:39,711 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:4
2012 -03-05 22:48:41,945 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:4
12/03/05 22:48:41 INFO regionserver.HRegion:on-line usertable,user757669512,1330944850808.cd0d6f16d8ae9cf0c9277f5d6c6c6b9f.; next sequenceid=62929
2012-03-05 22:48:41,963 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64 -50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:4
Mergulho Profundo no Hadoop
Os mencionados DFSInputStream e DFSOutputStream são realmente fachadas em torno dos conceitos usuais de fluxo. Eles envolvem a comunicação cliente-servidor nessas interfaces Java padrão, enquanto roteiam internamente o tráfego para um DataNode selecionado – que é aquele que contém uma cópia do bloco atual. Ele tem a liberdade de abrir e fechar essas conexões conforme necessário. À medida que um cliente lê um arquivo no HDFS, as classes da biblioteca do cliente alternam de forma transparente de bloco para bloco e, portanto, de DataNode para DataNode, portanto, ele deve abrir e fechar conexões conforme necessário.
O DFSInputStream tem uma instância de uma classe DFSClient.BlockReader , que abre a conexão com o DataNode. A instância de stream chama blockSeekTo() para cada chamada para read() que cuida de abrir a conexão, se já não houver nenhuma. Uma vez que um bloco é completamente lido, a conexão é fechada. Fechar o fluxo tem o mesmo efeito, é claro.
O DFSOutputStream tem uma classe auxiliar semelhante, o DataStreamer. Ele rastreia a conexão com o servidor, que é iniciada pelo método nextBlockOutputStream(). Ele tem outras classes internas que ajudam a escrever os dados do bloco, que omitimos aqui por uma questão de brevidade.
Ambos os blocos de escrita e leitura requerem um thread para manter o soquete e os dados intermediários no lado do servidor, encapsulados na instância do DataXceiver. Dependendo do que seu cliente está fazendo, você verá o número de conexões flutuar em torno do número de arquivos atualmente acessados no HDFS.
De volta ao enigma do HBase acima:o motivo pelo qual você não vê até 22 (e mais) conexões durante o início é que, enquanto as regiões são abertas, os únicos dados necessários são o bloco de informações do HFile. Este bloco é lido para obter detalhes vitais sobre cada arquivo, mas depois fechado novamente. Isso significa que o recurso do lado do servidor é liberado em rápida sucessão. As quatro conexões restantes são mais difíceis de determinar. Você pode usar o JStack para despejar todos os threads no DataNode, que neste exemplo mostra esta entrada:
java.lang.Thread.State:RUNNABLE
…
java.lang.Thread.State:RUNNABLE
…
Observe também que as classes auxiliares internas, como PacketResponder, ocupam outro encadeamento no grupo enquanto estão ativas. A saída do JStack indica esse fato, listando o encadeamento como tal:
java.lang.Thread.State:TIMED_WAITING (no monitor de objetos)
em java.lang.Object.wait(Método Nativo)
em org.apache.hadoop. hdfs.server.datanode.BlockReceiver$PacketResponder \
.lastDataNodeRun(BlockReceiver.java:779)
– bloqueado (um org.apache.hadoop.hdfs.server.datanode.BlockReceiver$PacketResponder)
em org.apache.hadoop.hdfs.server.datanode.BlockReceiver$PacketResponder.run(BlockReceiver.java:870)
em java.lang.Thread.run(Thread.java:680)
O comando Hadoop fsck também tem uma opção para relatar quais arquivos estão atualmente abertos para gravação:
FSCK iniciado por larsgeorge de /10.0.0.29 para caminho / hbase em Mon Mar 05 22:59:47 CET 2012
……/hbase/.logs/10.0.0.29,60020,1330984111693/10.0.0.29%3A60020.1330984118842 0 bytes, 1 bloco(s), OPENFORWRITE:………………………………..Status:SAUDÁVEL
Tamanho total: 2088783626 B
Total de diretórios: 54
Total de arquivos: 45
…
Isso não se relaciona imediatamente a um encadeamento do lado do servidor ocupado, pois eles são alocados por ID de bloco. Mas você pode perceber que há um bloco aberto para escrever. O comando Hadoop tem opções adicionais para imprimir os arquivos reais e o ID do bloco que os compõem:
FSCK iniciado por larsgeorge de /10.0.0.29 para caminho /hbase em Ter 06 de março 10:39:50 CET 2012
/hbase/.META./1028785192/.tmp
/hbase/.META./1028785192/info
/hbase/.META./1028785192/info/4027596949915293355 36517 bytes, 1 bloco(s): OK
0. blk_5532741233443227208_4201 len=36517 repl=1
Status:SAUDÁVEL
Tamanho total: 2088788703 B
Total dirs : 54
Total de arquivos: 45 (Arquivos atualmente sendo gravados:1)
Total de blocos (validados): 64 (tamanho médio do bloco 32637323 B) (Total de blocos de arquivos abertos (não validados):1)
Blocos minimamente replicados: 64 (100,0 %)
…
O JStack também relata que há um encadeamento DataXceiver, com um PacketResponder que o acompanha, para o ID do bloco “blk_-2005512129579433420_4199”, mas esse ID está ausente na lista de blocos relatados pelo fsck. Isso ocorre porque o bloco ainda não está finalizado e, portanto, não está disponível para os leitores. Em outras palavras, o Hadoop fsck relata apenas blocos completos (ou sincronizados[7][8], para a versão do Hadoop que suporta esse recurso).
Voltar ao HBase
Abrir todas as regiões não precisa de tantos recursos no servidor quanto você esperaria. No entanto, se você varrer toda a tabela do HBase, forçará o HBase a ler todos os blocos em todos os HFiles:
HBase Shell:
…
1000000 linha(s) em 1460,3120 segundos
2012-03-05 14:43:23,293 DEBUG org.apache.hadoop.hdfs.server.dataNode.DataNode:O número de conexões ativas é:7
2012 -03-05 14:43:23,299 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:8
…
2012-03-05 14:49:24,332 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0. 0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:11
2012-03-05 14:49:24,332 DEBUG org .apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:10
2012-03-05 14:49:59,987 DEBUG org.apache.hadoop.hdfs.server.datanod e.DataNode:O número de conexões ativas é:11
2012-03-05 14:51:12,603 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:12
2012-03-05 14:51:12,605 DEBUG org.apache.hadoop.hdfs .server.datanode.DataNode:O número de conexões ativas é:11
2012-03-05 14:51:46,473 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:12
…
2012-03-05 14:56:59,420 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:15
2012-03-05 14:57:31,722 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:16
2012-03-05 14:58:24,909 DEBUG org.apache.hadoop.hdfs. server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Número de ato ive conexões é:17
2012-03-05 14:58:24,910 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:16
…
2012-03-05 15:04:17,688 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:21
2012-03-05 15:04:17,689 DEBUG org.apache .hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é:22
2012-03-05 15:04:54,545 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Número de conexões ativas é:21
2012-03-05 15:05:55,901 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):O número de conexões ativas é :22
2012-03-05 15:05:55,901 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:O número de conexões ativas é:21
O que tudo isso significa?
Então, quantos “xcievers (sic)” você precisa? Dado que você usa apenas o HBase, você pode simplesmente monitorar a métrica “storefiles” acima (que você também obtém através do Ganglia ou JMX) e adicionar alguns por cento para arquivos de log intermediários e write-ahead. Isso deve funcionar para sistemas em movimento. No entanto, se você determinar esse número em um sistema ocioso e totalmente compactado e assumir que é o máximo, poderá achar esse número muito baixo quando começar a adicionar mais arquivos de armazenamento durante as liberações regulares do memstore, ou seja, assim que você começar a adicionar dados às tabelas do HBase. Ou se você também usar MapReduce nesse mesmo cluster, agregação de log do Flume e assim por diante. Você precisará levar em conta esses arquivos extras e, mais importante, blocos abertos para leitura e gravação.
Observe novamente que os exemplos neste post estão usando um único DataNode, algo que você não terá em um cluster real. Para isso, você terá que dividir o número total de arquivos de armazenamento (conforme a métrica HBase) pelo número de DataNodes que você possui. Se você tiver, por exemplo, uma contagem de arquivos de armazenamento de 1.000 e seu cluster tiver 10 DataNodes, você deve estar bem com o padrão de 256 threads xceiver por DataNode.
O pior caso seria o número de todos os leitores e gravadores ativos, ou seja, aqueles que estão enviando ou recebendo dados no momento. Mas como isso é difícil de determinar com antecedência, você pode considerar construir em uma reserva decente. Além disso, uma vez que o processo de escrita precisa de um thread extra – embora de vida curta – (para o PacketResponder), você também deve considerar isso. Portanto, uma fórmula razoável, mas bastante simplista, poderia ser:
Essa fórmula leva em consideração que você precisa de cerca de dois threads para um escritor ativo e outro para um leitor ativo. Isso é então somado e dividido pelo número de DataNodes, pois você deve especificar o “dfs.datanode.max.xcievers” por DataNode.
Se você voltar para a captura de tela do HBase RegionServer acima, verá que havia 22 arquivos de armazenamento. Estes são imutáveis e só serão lidos, ou seja, ocupam apenas um thread. Para todos os memstores que são liberados para o disco, você precisa de dois threads – mas apenas até que sejam totalmente gravados. The files are finalized and closed for good, cleaning up any thread in the process. So these come and go based on your flush frequency. Same goes for compactions, they will read N files and write them into a single new one, then finalize the new file. As for the write-ahead logs, these will occupy a thread once you have started to add data to any table. There is a log file per server, meaning that you can only have twice as many active threads for these files as you have RegionServers.
For a pure HBase setup (HBase plus its own HDFS, with no other user), we can estimate the number of needed DataXceiver’s with the following formula:
Since you will be hard pressed to determine the active number of store files, flushes, and so on, it might be better to estimate the theoretical maximum instead. This maximum value takes into account that you can only have a single flush and compaction active per region at any time. The maximum number of logs you can have active matches the number of RegionServers, leading us to this formula:
Obviously, the number of store files will increase over time, and the number of regions typically as well. Same for the numbers of servers, so keep in mind to adjust this number over time. In practice, you can add a buffer of, for example, 20%, as shown in the formula below – in an attempt to not force you to change the value too often.
On the other hand, if you keep the number of regions fixed per server[9], and rather split them manually, while adding new servers as you grow, you should be able to keep this configuration property stable for each server.
Final Advice &TL;DR
Here is the final formula you want to use:
It computes the maximum number of threads needed, based on your current HBase vitals (no. of store files, regions, and region servers). It also adds a fudge factor of 20% to give you room for growth. Keep an eye on the numbers on a regular basis and adjust the value as needed. You might want to use Nagios with appropriate checks to warn you when any of the vitals goes over a certain percentage of change.
Note:Please make sure you also adjust the number of file handles your process is allowed to use accordingly[10]. This affects the number of sockets you can use, and if that number is too low (default is often 1024), you will get connection issues first.
Finally, the engineering devil on one of your shoulders should already have started to snicker about how horribly non-Erlang-y this is, and how you should use an event driven approach, possibly using Akka with Scala[11] – if you want to stay within the JVM world. Bear in mind though that the clever developers in the community share the same thoughts and have already started to discuss various approaches[12][13].
Links: