[RocketMQ Advanced 2] Análise detalhada do mecanismo de criação de tema RocketMQ, por que é recomendado desligar a criação automática de tema?

introdução:

Por que há vários servidores Broker no cluster e autoCreateTopicEnable está definido como true, o que significa que o tópico é criado automaticamente, mas as informações de roteamento do tópico recém-criado estão contidas apenas em um dos servidores Broker.

Valor esperado : para a alta disponibilidade de envio de mensagens, espera-se que o Tópico recém-criado crie uma fila correspondente em cada Broker no cluster para evitar uma única falha de nó do Broker.

texto:

Ainda me lembro da primeira vez que usei rocketmq , precisei ir para o console para criar tópicos com antecedência . Eu me perguntei por que queria projetar dessa forma, então decidi pegar uma onda de código-fonte e levar todos para entender o mecanismo de criação do rocketmq topic a partir da raiz.

Na ideia de design do Rocketmq, o tópico é usado como a forma de organização da mesma mensagem de lógica de negócios. É apenas um conceito lógico e um tópico contém várias filas lógicas, nomeadamente filas de mensagens . O conteúdo da mensagem é realmente armazenado em Na fila, e a fila é armazenada no broker , abaixo eu uso um diagrama para ilustrar o modelo de armazenamento de tópico:

640? Wx_fmt = png

Na verdade, existem dois métodos de criação de tópicos diferentes no rocketmq, um é a pré-criação que acabei de mencionar e o outro é a criação automática . Deixe-me guiá-lo para explicar esses dois mecanismos de criação em detalhes da perspectiva do código-fonte.

1. Criação automática

1.1 O processo de criação automática de um tema

Por padrão, o tópico não precisa ser criado manualmente. Quando o produtor envia uma mensagem, ele puxa as informações de roteamento do tópico do servidor de nomes. Se as informações de roteamento do tópico não existirem, elas serão puxadas por padrão quando o corretor for iniciado. O nome padrão é " TBW102 " Tema:

org.apache.rocketmq.common.MixAll :

// Will be created at broker when isAutoCreateTopicEnablepublic static final String AUTO_CREATE_TOPIC_KEY_TOPIC = "TBW102";
public static final String AUTO_CREATE_TOPIC_KEY_TOPIC = "TBW102";

O switch criado automaticamente é configurado em BrokerConfig e controlado pelo campo autoCreateTopicEnable ,

org.apache.rocketmq.common.BrokerConfig :

@ImportantField
private boolean autoCreateTopicEnable = true;

Quando o corretor for iniciado , chamará o construtor TopicConfigManager , após a (true) autoCreateTopicEnable abrir, "TBW102" Salvar em  topicConfigTable  em :

org.apache.rocketmq.broker.topic.TopicConfigManager # TopicConfigManager :

// MixAll.AUTO_CREATE_TOPIC_KEY_TOPICif (this.brokerController.getBrokerConfig().isAutoCreateTopicEnable()) {    String topic = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC;    TopicConfig topicConfig = new TopicConfig(topic);    this.systemTopicList.add(topic);    topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig()                                 .getDefaultTopicQueueNums());    topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig()                                  .getDefaultTopicQueueNums());    int perm = PermName.PERM_INHERIT | PermName.PERM_READ | PermName.PERM_WRITE;    topicConfig.setPerm(perm);    this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);}
if (this.brokerController.getBrokerConfig().isAutoCreateTopicEnable()) {
    String topic = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC;
    TopicConfig topicConfig = new TopicConfig(topic);
    this.systemTopicList.add(topic);
    topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig()
                                 .getDefaultTopicQueueNums());
    topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig()
                                  .getDefaultTopicQueueNums());
    int perm = PermName.PERM_INHERIT | PermName.PERM_READ | PermName.PERM_WRITE;
    topicConfig.setPerm(perm);
    this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
}

O broker enviará as informações do tópico de topicConfigTable  ao servidor de nomes enviando um pacote de pulsação  e o servidor de nomes registrará as informações do tópico no  RouteInfoManager  .

Continue a ver como as informações de roteamento do tópico são obtidas do servidor de nomes quando a mensagem é enviada :

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl # tryToFindTopicPublishInfo :

private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {  TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);  if (null == topicPublishInfo || !topicPublishInfo.ok()) {    this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());    // 生产者第一次发送消息,topic在nameserver中并不存在    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);    topicPublishInfo = this.topicPublishInfoTable.get(topic);  }  if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {    return topicPublishInfo;  } else {    // 第二次请求会将isDefault=true,开启默认“TBW102”从namerserver获取路由信息    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);    topicPublishInfo = this.topicPublishInfoTable.get(topic);    return topicPublishInfo;  }}
  TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
  if (null == topicPublishInfo || !topicPublishInfo.ok()) {
    this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
    // 生产者第一次发送消息,topic在nameserver中并不存在
    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
    topicPublishInfo = this.topicPublishInfoTable.get(topic);
  }

  if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
    return topicPublishInfo;
  } else {
    // 第二次请求会将isDefault=true,开启默认“TBW102”从namerserver获取路由信息
    this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
    topicPublishInfo = this.topicPublishInfoTable.get(topic);
    return topicPublishInfo;
  }
}

No método acima, o tópico envia uma mensagem pela primeira vez e as informações de roteamento do tópico não podem ser obtidas do namserver neste momento. Se topicPublishInfo = null inserir o primeiro if, então uma segunda solicitação ao namserver será feita e, em seguida, isDefault = true . Habilite o tópico "TBW102" padrão para obter informações de roteamento do servidor de nomes . Neste momento, o tópico "TBW102" foi registrado no servidor de nomes pelo broker por padrão (o acima foi registrado quando o broker é iniciado):

org.apache.rocketmq.client.impl.factory.MQClientInstance # updateTopicRouteInfoFromNameServer :

if (isDefault && defaultMQProducer != null) {
  // 使用默认的“TBW102”topic获取路由信息
  topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),1000 * 3);
  if (topicRouteData != null) {
    for (QueueData data : topicRouteData.getQueueDatas()) {
      int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
      data.setReadQueueNums(queueNums);
      data.setWriteQueueNums(queueNums);
    }
  }
}

Se isDefault = true e defaultMQProducer não estiver vazio , as informações de roteamento padrão são obtidas do servidor de nomes . Neste momento , as informações de roteamento de tópico "TBW102" padrão de todos os brokers com a opção de criação automática serão obtidas e o número da fila de mensagens de tópico padrão (valor mínimo) ) .

org.apache.rocketmq.client.impl.factory.MQClientInstance # updateTopicRouteInfoFromNameServer :

TopicRouteData old = this.topicRouteTable.get(topic);
boolean changed = topicRouteDataIsChange(old, topicRouteData);
if (!changed) {
  changed = this.isNeedUpdateTopicRouteInfo(topic);
} else {
  log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);
}

Recupere as informações de roteamento do tópico do cache local. Como o tópico está enviando uma mensagem pela primeira vez, não há informações de roteamento para o tópico no momento. Portanto, ao comparar as informações de roteamento do tópico para "TBW102", alterado para verdadeiro, ou seja, há uma alteração, digite A seguinte lógica:

org.apache.rocketmq.client.impl.factory.MQClientInstance # updateTopicRouteInfoFromNameServer :

// Update sub info{
{
  Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
  Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
  while (it.hasNext()) {
    Entry<String, MQConsumerInner> entry = it.next();
    MQConsumerInner impl = entry.getValue();
    if (impl != null) {
      impl.updateTopicSubscribeInfo(topic, subscribeInfo);
    }
  }
}

Use as informações de roteamento do tópico "TBW102" para construir  TopicPublishInfo e use topic como a chave e TopicPublishInfo como o valor para atualizar o cache local do corretor . Aqui você pode entender que os corretores trabalharam muito para criar o tópico "TBW102" e registrar suas informações de roteamento para O servidor de nomes, imediatamente após ser adquirido pelo novo tópico, usa as informações de roteamento do tópico "TBW102" para construir um novo TopicPublishInfo e usá-lo como seu. Como as informações de roteamento de TopicPublishInfo são as informações de roteamento do tópico "TBW102" padrão, você realmente deseja enviar a mensagem O tópico também será carregado e enviado para o broker onde o tópico "TBW102" está localizado, aqui podemos chamá-lo de método de hacking .

Quando o broker receber a mensagem, ele chamará o método createTopicInSendMessageMethod no método msgCheck para colocar as informações do tópico no cache topicConfigTable e o broker enviará periodicamente uma pulsação para enviar o topicConfigTable ao servidor de nomes para registro.

Um diagrama de sequência para criar e obter informações de tópicos automaticamente ao enviar mensagens:

640? Wx_fmt = png

1.2 Processo de mensagem do produtor

Vamos dar uma olhada no processo de envio de mensagens. Quase não é muito complicado. Nada mais é do que encontrar o Tópico para enviar a mensagem para qual Broker, e então enviar a mensagem. 【referência】

Agora eu sei para que é utilizado o TBW102, ou seja, a startup do broker que aceita a criação automática do tópico irá cadastrar este tópico padrão no NameServer, para que quando o Produtor enviar uma nova mensagem de tópico, ele saberá qual Broker pode criar automaticamente o tópico, e então enviar para aquele Broker .

Ao receber esta mensagem, o Broker descobre que não encontra o tópico correspondente, mas aceita criar um novo tópico, que criará as informações de roteamento do tópico correspondente.

1.3 Desvantagens da criação automática de temas

Crie um tópico automaticamente então é possível que a mensagem do tópico seja enviada apenas para um Broker , e ele não terá a função de balanceamento de carga .

Porque depois que a solicitação de criação de um novo Tópico chega ao Broker, o Broker cria as informações de roteamento correspondentes, mas o heartbeat é enviado a cada 30s , portanto, leva até 30s para o NameServer saber as informações de roteamento desse novo Tópico.

Supondo que o remetente ainda está enviando mensagens de forma contínua e rápida , na verdade não há informações de roteamento sobre este tópico no NameServer, portanto, há uma chance de que outros corretores que permitem a criação automática também possam criar informações de roteamento de tópico correspondentes, de modo que os Corretores no cluster Você pode aceitar as informações deste tópico e atingir o objetivo de balanceamento de carga, mas existem alguns corretores que podem não recebê-las .

Se o remetente não enviar um dentro de 30 segundos após o envio desta vez , o Broker anterior atualiza as informações de roteamento para o NameServer com a pulsação, e então o Produtor que envia a mensagem do Tópico depois pode saber apenas a mensagem do Tópico do NameServer. Envie para a Corretora anterior, que não está balanceada, se houver muitas novidades sobre esse novo tópico, a carga nessa Corretora será muito alta. Além disso, a alta disponibilidade não é alcançada.Se este broker falhar, ele não pode ser carregado para outro, resultando em um único ponto de falha.

Portanto, não é recomendável habilitar a criação automática de tópicos online, ou seja, o parâmetro autoCreateTopicEnable.

Em segundo lugar, crie com antecedência

Na verdade, parece mais apropriado chamar a pré-criação, ou seja, criar informações relacionadas ao tópico no corretor e registrá-lo no servidor de nomes, e então obter diretamente as informações de roteamento do tópico do servidor de nomes quando o cliente enviar uma mensagem , mas a criação manual será mais orientada para a ação A imagem é fácil de entender, informando diretamente que as informações do seu tópico precisam ser criadas manualmente no console .

A pré-criação deve ser criada por meio de comandos relacionados a tópicos fornecidos por mqadmin e executar:

./mqadmin updateTopic

Os parâmetros oficiais são os seguintes:

usage: mqadmin updateTopic
-b,--brokerAddr <arg>       create topic to which broker
-c,--clusterName <arg>      create topic to which cluster
-h,--help                   Print help
-n,--namesrvAddr <arg>      Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876
-o,--order <arg>            set topic's order(true|false
-p,--perm <arg>             set topic's permission(2|4|6), intro[2:W 4:R; 6:RW]
-r,--readQueueNums <arg>    set read queue nums
-s,--hasUnitSub <arg>       has unit sub (true|false
-t,--topic <arg>            topic name
-u,--unit <arg>             is unit topic (true|false
-w,--writeQueueNums <arg>   set write queue nums

Localizamos diretamente o método de execução de comandos em sua classe de implementação:

2.1 Criado pelo modo corretor:

org.apache.rocketmq.tools.command.topic.UpdateTopicSubCommand # execute :

// -b,--brokerAddr <arg>   create topic to which broker
if (commandLine.hasOption('b')) {
  String addr = commandLine.getOptionValue('b').trim();
  defaultMQAdminExt.start();
  defaultMQAdminExt.createAndUpdateTopicConfig(addr, topicConfig);
  return;
}

Obtenha o endereço do corretor com o parâmetro -b no tempo de execução da ferramenta de linha de comando commandLine. DefaultMQAdminExt é a API executada pelo console rocketmq padrão . Neste momento, chame o método start, que cria um mqClientInstance, que encapsula os detalhes da comunicação netty e, em seguida, Esta é a etapa mais importante. Chame createAndUpdateTopicConfig para enviar as informações de configuração do tópico ao broker especificado para concluir a criação do tópico.

2.2 Criar pelo modo de cluster:

org.apache.rocketmq.tools.command.topic.UpdateTopicSubCommand # execute :

// -c,--clusterName <arg>   create topic to which cluster
else if (commandLine.hasOption('c')) {
  String clusterName = commandLine.getOptionValue('c').trim();
  defaultMQAdminExt.start();
  Set<String> masterSet =
    CommandUtil.fetchMasterAddrByClusterName(defaultMQAdminExt, clusterName);
  for (String addr : masterSet) {
    defaultMQAdminExt.createAndUpdateTopicConfig(addr, topicConfig);
    System.out.printf("create topic to %s success.%n", addr);
  }
  return;
}

A lógica da criação por meio do modo de cluster é praticamente a mesma que a da criação por meio do modo de corretor. Há uma etapa de obtenção do endereço mestre de todos os corretores no cluster do servidor de nomes de acordo com o cluster e, em seguida, enviar informações de tópico para cada corretor no cluster em um loop . Esta lógica é a mesma que a especificada Um único corretor é consistente.

Isso também mostra que quando o modo de cluster é usado para criar um tópico, o número de filas de cada broker no cluster é o mesmo. Quando um único modo de broker é usado para criar um tópico, o número de filas de cada broker pode ser inconsistente.

Crie um diagrama de sequência com antecedência:

640? Wx_fmt = png

3. Quando preciso criar um tópico com antecedência?

É recomendado ligar offline e desligar online , não o que eu disse, mas uma recomendação oficial:

640? Wx_fmt = png

Por que o RocketMQ foi projetado assim? Após uma onda de análises profundas do código-fonte, obtive a resposta que queria:

De acordo com a análise do código-fonte acima, concluímos que o rocketmq primeiro obterá informações de roteamento de tópico ao enviar uma mensagem . Se o tópico for a primeira vez que enviar uma mensagem , uma vez que o servidor de nomes não possui informações de roteamento de tópico, ele usará " TBW102 " novamente O tópico padrão obtém informações de roteamento. Supondo que os corretores ativaram a chave de criação automática , as informações de roteamento de todos os corretores serão obtidas neste momento . O envio da mensagem selecionará um dos corretores para enviar mensagens de acordo com o algoritmo de carregamento (apenas um deles pode ser enviado, Caso contrário, ele será enviado repetidamente) . Após a mensagem chegar ao corretor, verifica-se que não existe tal tópico localmente, e as informações para a criação do tópico serão armazenadas no cache local e as informações de roteamento do tópico serão registradas no servidor de nomes, então isso causará uma consequência: todos os futuros As mensagens deste tópico serão todas enviadas para este broker. Se o volume de mensagens do tópico for muito grande, isso causará muita carga em um determinado broker. Esse armazenamento de mensagens não terá o efeito de balanceamento de carga . Da mesma forma, se isso O Broker falha e desliga, o envio de mensagens falha e o nó único do Broker falha.

 Referência original

● A otimização de desempenho do Tomcat8 mais forte da história

Por que o Alibaba pode resistir a 10 bilhões em 90 segundos? - A evolução da arquitetura distribuída de alta simultaneidade do lado do servidor

Plataforma de comércio eletrônico B2B - função de pagamento eletrônico ChinaPay UnionPay

Aprenda o bloqueio distribuído do Zookeeper, deixe os entrevistadores olharem para você com admiração

Solução de bloqueio distribuído de microsserviço do SpringCloud e-commerce de pico de Redisson

Confira mais bons artigos, entre na conta oficial - por favor - excelente no passado

Uma conta pública profunda e comovente 0.0

 

Acho que você gosta

Origin blog.csdn.net/a1036645146/article/details/109581499
Recomendado
Clasificación