De tempos em tempos, recebemos perguntas sobre tópicos que não se encaixam diretamente na documentação, pois se aprofundam nos aspectos internos das bibliotecas de clientes. Nesta série, abordaremos diferentes temas de interesse - neste caso, como o SDK se inicializa sozinho.
Observe que esta postagem foi escrita tendo em mente as versões 2.5.9 / 2.6.0 do Java SDK. As coisas podem mudar com o tempo, mas a abordagem geral deve permanecer praticamente a mesma.
Primeiros passos
Para saber quais hosts remotos devem solicitar uma configuração em geral, contamos com a lista de nomes de host que o usuário fornece por meio do comando CouchbaseCluster API. Assim, por exemplo, se este código for fornecido:
1 |
CouchbaseCluster agrupamento = CouchbaseCluster.criar("node1", "node2", "node3"); |
Em seguida, usaremos nó1 , nó2 e nó3 como parte do processo inicial de bootstrap. É importante entender, neste ponto, que nenhuma conexão real foi estabelecida ainda. O código apenas armazena esses três hosts na tabela Provedor de configuração como hosts semente. No entanto, eles não são armazenados como cadeias de caracteres, mas sim como um Endereço de rede cada um dos quais fornece um invólucro conveniente e mais rico em recursos sobre o JDK simples InetAddress . Por exemplo, ele pode ser modificado por meio das propriedades do sistema para não permitir pesquisas de DNS reverso ou forçar IPv4 sobre IPv6, mesmo que o JDK esteja configurado de forma diferente.
Se nenhum host for fornecido, 127.0.0.1 será definido como um host semente.
Para os leitores que também estão usando um SDK que depende do string de conexão para configuração: o Java SDK analisará essa string quando fornecida, mas não usará nenhuma das definições de configuração além da lista de nós de inicialização. Toda configuração deve ser aplicada por meio da função CouchbaseEnvironment e seu construtor ou por meio das propriedades do sistema, quando disponíveis.
Há um último aspecto que precisamos abordar antes de abrirmos um balde e começarmos a trabalhar com as ervas daninhas. Se o CouchbaseEnvironment está configurado com dnsSrvEnabled Durante a fase de construção do cluster, o código executará uma pesquisa no DNS SRV para recuperar os hosts reais em vez do host de referência do DNS SRV. O código utiliza classes JVM para fazer isso; o código pode ser encontrado em aqui.
Obtendo uma configuração
Agora que temos uma lista de nós-semente com os quais trabalhar, assim que um balde é aberto, o verdadeiro trabalho começa. Vamos considerar o seguinte trecho de código como ponto de partida:
1 |
Balde balde = agrupamento.openBucket("amostra de viagem"); |
O ato de abrir um bucket tem muitas etapas pequenas que precisam ser concluídas, mas a primeira e mais importante etapa é buscar uma configuração inicial boa de um dos nós do cluster.
O Provedor de configuração constrói uma cadeia de carregadores de configuração, em que cada carregador tem um mecanismo diferente para tentar obter uma configuração. Atualmente, há dois carregadores diferentes em funcionamento:
– Carregador de transportadoraKV: tenta obter a configuração por meio do protocolo binário memcache do serviço KV (porta
11210 por padrão).
– Carregador httpTenta obter a configuração do gerenciador de cluster (porta
8091 por padrão).
O Carregador de transportadora tem precedência, pois é a opção mais escalável (o serviço KV é capaz de lidar com solicitações muito mais altas por segundo do que o gerenciador de cluster) e, se não estiver disponível, o SDK voltará para o Carregador http . Um dos motivos pelos quais ele pode falhar é que determinadas versões (mais antigas) do Couchbase Server não oferecem suporte a esse mecanismo. Outras falhas em geral incluem a porta 11210 sendo bloqueados por firewalls (o que, de qualquer forma, leva a outros problemas no futuro...) ou soquetes com tempo limite.
O Carregador http também tem dois pontos de extremidade HTTP para buscar (dependendo da versão do Couchbase Server novamente para compatibilidade com versões anteriores), um que fornece uma configuração mais concisa e outro que fornece uma configuração detalhada. A configuração detalhada é a que existe desde a época do Couchbase Server 1.8/2.0 e tem garantia de funcionamento, mas os recursos mais recentes provavelmente não estão expostos nela.
Você entendeu o tema aqui: as abordagens mais novas/mais escalonáveis são tentadas primeiro com opções de fallback para clientes mais antigos e configurações personalizadas. Em geral, o gerenciador de cluster é sempre responsável por gerenciar e fornecer a configuração atualizada, mas ela também é enviada ao serviço KV para que os SDKs possam recuperá-la com um comando especial.
Como um caso especial para servidores mais antigos, se a configuração foi carregada por meio do Carregador http Se o SDK estiver conectado a um dos endpoints HTTP, o SDK anexará uma conexão de streaming para receber atualizações de configuração. Se a conexão Carregador de transportadora for bem-sucedido, continuaremos a sondar com um intervalo de sondagem configurável para uma configuração por meio do protocolo binário, pois é mais eficiente em termos de recursos no lado do servidor (o serviço KV é muito mais adequado para lidar com muitas solicitações em potencial do que o gerenciador de cluster, razão pela qual o carregamento de portadora foi implementado em primeiro lugar).
Abordaremos os diferentes casos de erro mais adiante. Por enquanto, suponhamos que conseguimos obter uma configuração válida de um dos carregadores. Nesse ponto, ela é enviada de volta para o provedor de configuração, que é verificado e enviado para o Solicitador.
É possível que o provedor de configuração não esteja encaminhando a configuração, sendo que o principal motivo é que ela está desatualizada ou não é nova o suficiente. Toda configuração tem uma revisão que a acompanha e o SDK rastreia internamente esse número de revisão para propagar apenas as configurações mais recentes do que as já processadas. Durante uma operação de rebalanceamento, isso pode acontecer com bastante frequência e, se você tiver o registro de depuração ativado, poderá detectar isso procurando uma linha de registro com a mensagem: Não aplicação novo configuração, mais antigos ou mesmo rev ID .
Assim que o Solicitador recebe uma nova configuração e inicia um reconfiguração. A reconfiguração tem apenas uma função: pegar a configuração fornecida pelo servidor e alterar o estado interno do SDK para espelhá-la. Isso pode significar adicionar ou remover referências de nós, pontos de extremidade de serviços ou soquetes. O SDK espelha a linguagem comum do servidor, portanto, gerencia Nós que fornecem Serviços que pode ter um ou mais Pontos finais.
Se você ativar o registro de depuração, poderá ver durante a inicialização como os diferentes nós, serviços e pontos de extremidade são ativados com base na configuração fornecida:
1 2 3 4 5 6 7 |
[cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Solicitador:435 - Início reconfiguração. [cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Solicitador:527 - Início reconfiguração para balde viagens-amostra [cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Solicitador:292 - Nó Endereço de rede{localhost/127.0.0.1, fromHostname=falso, reverseDns=verdadeiro} já registrado, pular. [cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Solicitador:352 - Peguei instruído para adicionar Serviço BINÁRIO, para Nó Endereço de rede{localhost/127.0.0.1, fromHostname=falso, reverseDns=verdadeiro} [cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Nó:273 - [127.0.0.1/localhost]: Adição Serviço BINÁRIO [cb-cálculos-8] 2018-06-20 17:43:55 DEBUG Nó:276 - [127.0.0.1/localhost]: Serviço BINÁRIO já adicionado, pular. ... |
Você também pode ver que ele simplesmente ignora determinadas operações se o resultado desejado já tiver sido alcançado. Você verá a maioria dessas mensagens quando novas configurações chegarem, ou seja, durante o bootstrap, o rebalanceamento ou o failover.
A reconfiguração, assim como o rebalanceamento no servidor, é uma operação "on-line". Isso significa que as operações em andamento não são afetadas na maior parte do tempo e, se forem, são reprogramadas e colocadas de volta no ringbuffer até que sejam bem-sucedidas ou atinjam o tempo limite.
Assim que a primeira reconfiguração for bem-sucedida, nossa solicitação de abertura do bucket será concluída e o Balde é retornado na API síncrona. Aqui reside uma troca não óbvia: poderíamos retornar a solicitação de balde aberto imediatamente, mas não detectaríamos nenhum erro durante o bootstrap. Esperar pela conclusão da primeira reconfiguração nos permite detectar mais erros imediatamente, mas também significa que quanto mais nós forem adicionados ao cluster, mais tempo levará - certifique-se de medir o desempenho do bootstrap do seu cluster. O tempo limite padrão de 5 segundos deve ser suficiente para a maioria dos casos, mas, em implantações de nuvem grandes e mais lentas, pode fazer sentido aumentar esse tempo limite.
Você pode fazer isso modificando o ambiente:
1 2 3 |
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.construtor() .connectTimeout(TimeUnit.SEGUNDOS.paraMillis(10)) // definido para 10s de 5 .construir(); |
ou diretamente no openBucket ligar:
1 |
Balde balde = agrupamento.openBucket("amostra de viagem", 10, TimeUnit.SEGUNDOS); |
Falhas e configuração de bootstrap
O que acontece se um ou todos os hosts semente fornecidos não estiverem disponíveis? Para responder a essa pergunta, precisamos dar uma olhada rápida em como o carregador tenta obter as configurações.
Para cada host na lista de nós semente, um carregador primário e um de reserva são montados e todos eles disparam em paralelo tentando obter uma configuração adequada. Quando a primeira é encontrada, a sequência observável é cancelada e apenas uma configuração é usada daqui para frente. Usar essa abordagem, em vez de sequenciar, proporciona tempos de inicialização mais rápidos, mas também pode levar a mais erros no registro se um ou mais nós não forem acessíveis. No entanto, como estamos otimizando para o caso positivo (no caso de falha, algo está errado de qualquer forma), essa parece ser uma troca aceitável.
Essencialmente, desde que haja um bom nó para inicialização na lista de nós semente, o SDK poderá pegar uma configuração. Vamos considerar um caso especial em que em um nó MDS configuração, você instrui o SDK a fazer o bootstrap somente de um nó que não contenha o serviço KV (digamos, somente a consulta e, é claro, o gerenciador de cluster).
Se o registro de depuração estiver ativado, você verá o SDK tentando obter uma configuração do serviço KV, mas ela está sendo rejeitada com algum erro:
1 |
[cb-cálculos-7] 2018-06-21 10:53:48 DEBUG Carregador:178 - Poderia não adicionar serviço em Endereço de rede{10.142.175.102/10.142.175.102, fromHostname=falso, reverseDns=verdadeiro} porque de com.couchbase.cliente.núcleo.ponto final.kv.AuthenticationException: Balde não encontrado em Selecione Balde comando, remoção ele novamente. |
O erro específico não importa, mas mais adiante no registro você pode ver como o carregador muda para o fallback HTTP que está disponível:
1 2 3 4 5 |
[cb-cálculos-2] 2018-06-21 10:53:48 DEBUG Carregador:196 - Com sucesso habilitado Serviço CONFIG em Nó Endereço de rede{10.142.175.102/10.142.175.102, fromHostname=falso, reverseDns=verdadeiro} [cb-cálculos-2] 2018-06-21 10:53:48 DEBUG Carregador http:69 - Início para descobrir configuração através de HTTP Bootstrap ... [cb-cálculos-4] 2018-06-21 10:53:48 DEBUG Carregador http:93 - Com sucesso carregado configuração através de HTTP. [cb-cálculos-4] 2018-06-21 10:53:48 DEBUG Carregador:203 - Peguei configuração de Serviço, tentativa para analisar. |
Uma implicação disso é que, mesmo que em outro nó o serviço KV possa estar disponível, nesse caso o carregador HTTP ainda instruirá a conexão de streaming a ser aberta para buscar as configurações subsequentes.
É possível personalizar o comportamento do bootstrap por meio de configurações de ambiente:
- bootstrapHttpEnabled : Se o HttpLoader estiver ativado (true por padrão).
- bootstrapCarrierEnabled : Se o CarrierLoader estiver ativado (verdadeiro por padrão).
- bootstrapHttpDirectPort : Qual porta o HttpLoader deve contatar inicialmente se o SSL não estiver ativado (padrão 8091).
- bootstrapHttpSslPort : Qual porta o HttpLoader deve contatar inicialmente se o SSL estiver ativado (padrão 18091).
- bootstrapCarrierDirectPort : Qual porta o CarrierLoader deve contatar inicialmente se o SSL não estiver ativado (padrão 11210).
- bootstrapCarrierSslPort : Qual porta o CarrierLoader deve contatar inicialmente se o SSL estiver ativado (padrão 11207).
Essas configurações têm padrões sãos e, em geral, não precisam ser modificadas. Elas podem se tornar úteis em cenários de depuração ou se forem testadas compilações personalizadas dentro do Couchbase.
Considerações finais
Normalmente, a inicialização é tranquila e resiliente a falhas de nós individuais, mas se algo der errado e o bucket não abrir corretamente, a primeira ordem de ação é sempre examinar os logs no nível INFO/WARN e, se possível, ativar o registro DEBUG. Isso geralmente fornece informações sobre quais soquetes não se conectaram corretamente ou onde, na fase de inicialização, o cliente teve que parar (devido a erros ou tempos limite).
Lembre-se também de que, se você executar clusters grandes, especificar um tempo de abertura de bucket maior do que os 5 segundos habituais pode fazer sentido se a rede for lenta ou tiver uma variação de latência maior do que o normal.
Belo artigo! Obrigado.
Só estou curioso para saber quais registros no lado do servidor contêm as informações de inicialização. É o "memcached.log"? Agradeceria se você pudesse esclarecer melhor essa questão. Obrigado.
Sim, o memcached.log no lado do servidor tem algumas das informações. Embora ele não esteja registrando muito (além do comando "hello" mais recente), pois pode ser muito spam.
Há algo específico que esteja procurando?