[RocketMQ Advanced 2] Análisis en profundidad del mecanismo de creación de temas de RocketMQ, ¿por qué se recomienda desactivar la creación automática de temas?

Introducción:

¿Por qué hay varios servidores Broker en el clúster y autoCreateTopicEnable se establece en verdadero, lo que significa que el tema se crea automáticamente, pero la información de enrutamiento del tema recién creado solo está contenida en uno de los servidores Broker?

Valor esperado : Para la alta disponibilidad del envío de mensajes, se espera que el Tema recién creado cree una cola correspondiente en cada Broker en el clúster para evitar una falla de un solo nodo del Broker.

texto:

Todavía recuerdo la primera vez que usé rocketmq , necesitaba ir a la consola para crear temas por adelantado . Me preguntaba por qué quería diseñar de esta manera, así que decidí tomar una ola de código fuente y llevar a todos a comprender el mecanismo de creación del tema rocketmq desde la raíz.

En la idea de diseño de Rocketmq, el tema se usa como la forma de organización del mismo mensaje de lógica empresarial. Es solo un concepto lógico , y un tema contiene varias colas lógicas, a saber, colas de mensajes . El contenido del mensaje se almacena realmente en En la cola, y la cola se almacena en el corredor , a continuación utilizo un diagrama para ilustrar el modelo de almacenamiento de temas:

640? Wx_fmt = png

De hecho, hay dos métodos diferentes de creación de temas en rocketmq, uno es la creación previa que acabo de mencionar y el otro es la creación automática . Permítanme llevarlos a explicar estos dos mecanismos de creación en detalle desde la perspectiva del código fuente.

1. Creación automática

1.1 El proceso de creación automática de un tema

De forma predeterminada, no es necesario crear el tema manualmente. Cuando el productor envía un mensaje, extraerá la información de enrutamiento del tema del servidor de nombres. Si la información de enrutamiento del tema no existe, se extraerá de forma predeterminada cuando se inicie el intermediario. El nombre predeterminado es " 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";

El conmutador creado automáticamente se configura en BrokerConfig, controlado por el campo autoCreateTopicEnable ,

org.apache.rocketmq.common.BrokerConfig :

@ImportantField
private boolean autoCreateTopicEnable = true;

Cuando el corredor se inicia , llamará al constructor TopicConfigManager , después de que se abra (verdadero) autoCreateTopicEnable, se guardará "TBW102" en  topicConfigTable  en :

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);
}

El corredor enviará la información del tema de topicConfigTable  al servidor de nombres enviando un paquete de latidos  , y el servidor de nombres registrará la información del tema en el  RouteInfoManager  .

Continúe para ver cómo se obtiene la información de enrutamiento de temas del servidor de nombres cuando se envía el mensaje :

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;
  }
}

En el método anterior, el tema envía un mensaje por primera vez , y la información de enrutamiento del tema no se puede obtener del namserver en este momento. Si topicPublishInfo = null ingresa el primer if, se realizará una segunda solicitud al namserver y luego isDefault = true . Habilite el tema "TBW102" predeterminado para obtener información de enrutamiento del servidor de nombres . En este momento, el corredor ha registrado el tema "TBW102" en el servidor de nombres de forma predeterminada (lo anterior se ha registrado cuando se inicia el corredor):

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);
    }
  }
}

Si isDefault = true y defaultMQProducer no está vacío , la información de enrutamiento predeterminada se obtiene del servidor de nombres . En este momento , se obtendrá la información de enrutamiento de temas "TBW102" predeterminada de todos los agentes con el interruptor de creación automática y el número de cola de mensajes de tema predeterminado (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 la información de enrutamiento del tema de la caché local. Dado que el tema está enviando un mensaje por primera vez, no hay información de enrutamiento para el tema en este momento. Por lo tanto, al comparar la información de enrutamiento del tema con "TBW102", cambió a verdadero, es decir, hay un cambio, ingrese La siguiente 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);
    }
  }
}

Utilice la información de enrutamiento del tema "TBW102" para crear  TopicPublishInfo , y use el tema como clave y TopicPublishInfo como el valor para actualizar la caché local del corredor . Aquí puede comprender que los corredores han trabajado tan duro para crear el tema "TBW102" y registrar su información de enrutamiento en El servidor de nombres, inmediatamente después de ser adquirido por el nuevo tema, usa la información de enrutamiento del tema "TBW102" para construir un nuevo TopicPublishInfo y usarlo como propio. Dado que la información de enrutamiento de TopicPublishInfo es la información de enrutamiento del tema "TBW102" predeterminado, realmente desea enviar el mensaje El tema también se cargará y enviará al corredor donde se encuentra el tema "TBW102" Aquí podemos llamarlo método de piratería .

Cuando el corredor recibe el mensaje, llamará al método createTopicInSendMessageMethod en el método msgCheck para rellenar la información del tema en la caché de topicConfigTable, y el corredor enviará periódicamente un latido para enviar el topicConfigTable al servidor de nombres para su registro.

Un diagrama de secuencia para crear y obtener automáticamente información sobre el tema al enviar mensajes:

640? Wx_fmt = png

1.2 Proceso del mensaje del productor

Echemos un vistazo al proceso de envío de mensajes, no es muy complicado, no es más que encontrar el tema para enviar el mensaje a qué Broker y luego enviar el mensaje. 【referencia】

Ahora sé para qué se usa TBW102, es decir, el inicio del broker que acepta la creación automática del tema registrará este tema predeterminado en el NameServer, de modo que cuando el Productor envíe un mensaje de nuevo tema, sabrá qué Broker puede crear automáticamente el tema y luego enviarlo a ese Broker. .

Cuando el Broker recibe este mensaje, encuentra que no encuentra el tema correspondiente, pero acepta crear un tema nuevo, que creará la información de enrutamiento del Tema correspondiente.

1.3 Desventajas de la creación automática de temas

Cree automáticamente un tema, entonces es posible que el mensaje del tema solo se envíe a un Broker y no jugará el papel de balanceo de carga .

Porque después de que la solicitud para crear un nuevo tema llega al corredor, el corredor crea la información de enrutamiento correspondiente, pero el latido se envía cada 30 segundos, por lo que el servidor de nombres tarda hasta 30 segundos en conocer la información de enrutamiento de este nuevo tema.

Suponiendo que el remitente sigue enviando mensajes de forma continua y rápida , en realidad no hay información de enrutamiento sobre este tema en el servidor de nombres, por lo que existe la posibilidad de que otros corredores que permitan la creación automática también puedan crear la información de enrutamiento del tema correspondiente, de modo que los corredores del clúster Puede aceptar la información de este tema y lograr el propósito del equilibrio de carga, pero hay algunos corredores que pueden no recibirla .

Si el remitente no envía uno dentro de los 30 segundos posteriores al envío de este tiempo , el Broker anterior actualiza la información de enrutamiento al NameServer con el latido, entonces el Productor que envía el mensaje del tema después solo puede conocer el mensaje del tema del NameServer El envío al Broker anterior está desequilibrado. Si hay muchas noticias sobre este nuevo tema, la carga sobre ese Broker será muy alta. Además, no se logra una alta disponibilidad, si este broker falla, no se puede cargar a otro, resultando en un solo punto de falla.

Por tanto, no se recomienda habilitar la creación automática de temas online, es decir, el parámetro autoCreateTopicEnable.

Segundo, crea con anticipación

De hecho, parece más apropiado llamar a la creación previa, es decir, crear información relacionada con el tema en el intermediario y registrarla en el servidor de nombres, y luego obtener directamente la información de enrutamiento del tema del servidor de nombres cuando el cliente envía un mensaje , pero la creación manual estará más orientada a la acción. La imagen es fácil de entender y le indica directamente que la información de su tema debe crearse manualmente en la consola .

La creación previa debe crearse a través de comandos relacionados con el tema proporcionados por mqadmin y ejecutar:

./mqadmin updateTopic

Los parámetros oficiales son los siguientes:

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 directamente el método de ejecución de comandos en su clase de implementación:

2.1 Creado por modo broker:

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

// -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;
}

Obtenga la dirección del intermediario con el parámetro -b en tiempo de ejecución desde la herramienta de línea de comandos commandLine . DefaultMQAdminExt es la API ejecutada por la consola rocketmq predeterminada . En este momento, llame al método de inicio, que crea un mqClientInstance, que encapsula los detalles de la comunicación netty, y luego Este es el paso más importante. Llame a createAndUpdateTopicConfig para enviar la información de configuración del tema al intermediario especificado para completar la creación del tema.

2.2 Crear por modo de clúster:

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

// -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;
}

La lógica de crear a través del modo de clúster es aproximadamente la misma que la de crear a través del modo de agente. Hay un paso para obtener la dirección maestra de todos los agentes del clúster desde el servidor de nombres según el clúster y luego enviar la información del tema a cada agente del clúster en un bucle . Esta lógica es la misma que la especificada Un solo corredor es consistente.

Esto también muestra que cuando se usa el modo de clúster para crear un tema, el número de colas de cada agente en el clúster es el mismo. Cuando se usa un modo de agente único para crear un tema, el número de colas de cada agente puede ser inconsistente.

Cree un diagrama de secuencia de antemano:

640? Wx_fmt = png

3. ¿Cuándo necesito crear un tema por adelantado?

Se recomienda encender sin conexión y apagar en línea , no lo que dije, sino una recomendación oficial:

640? Wx_fmt = png

¿Por qué RocketMQ está diseñado así? Después de una ola de análisis profundo del código fuente, obtuve la respuesta que quería:

De acuerdo con el análisis del código fuente anterior, llegamos a la conclusión de que rocketmq primero obtendrá información de enrutamiento del tema al enviar un mensaje . Si el tema es la primera vez que se envía un mensaje , ya que el servidor de nombres no tiene información de enrutamiento del tema, volverá a utilizar " TBW102 " . El tema predeterminado obtiene información de enrutamiento. Suponiendo que los corredores hayan activado el interruptor de creación automática , entonces se obtendrá la información de enrutamiento de todos los corredores en este momento . El envío del mensaje seleccionará uno de los corredores para enviar mensajes de acuerdo con el algoritmo de carga (solo se puede enviar uno de ellos, De lo contrario, se enviará repetidamente) . Después de que el mensaje llega al corredor, se encuentra que no existe tal tema localmente, y la información para crear el tema se almacenará en la caché local, y la información de enrutamiento del tema se registrará en el servidor de nombres, entonces esto causará una consecuencia: todos los futuros Todos los mensajes de este tema se enviarán a este agente. Si el volumen de mensajes del tema es muy grande, causará demasiada carga en un agente determinado. Dicho almacenamiento de mensajes no logrará el efecto de equilibrio de carga . De manera similar, si esto El intermediario falla y cuelga, el envío de mensajes fallará y el nodo único del intermediario falla.

 Referencia original

● La optimización de rendimiento de Tomcat8 más sólida de la historia

¿Por qué Alibaba puede resistir 10 mil millones en 90 segundos? - La evolución de la arquitectura distribuida de alta concurrencia del lado del servidor

Plataforma de comercio electrónico B2B: función de pago electrónico ChinaPay UnionPay

Aprenda el candado distribuido de Zookeeper, deje que los entrevistadores lo miren con admiración

Solución de bloqueo distribuido de Redisson con microservicio de pico de comercio electrónico de SpringCloud

Vea más artículos buenos, ingrese a la cuenta oficial, por favor, excelente en el pasado

Una cuenta pública profunda y conmovedora 0.0

 

Supongo que te gusta

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