Desde el principio hasta el combate real, te enseñaré cómo usar RabbitMQ en tu proyecto.

Hola a todos, soy Lou Tsai.

El artículo de RabbitMQ se escribió antes, pero el ejemplo dado en ese momento era la versión Demo. Este artículo integra principalmente RabbitMQ en el proyecto técnico de acuerdo con el conocimiento teórico escrito anteriormente.

No BB, en el directorio de artículos:

imagen

A continuación, repasemos primero los conocimientos teóricos. Si ya tiene claros estos conocimientos, puede pasar directamente a la parte de combate real.

1. Cola de mensajes

1.1 Modo de cola de mensajes

Actualmente existen dos modos principales de colas de mensajes, a saber, el "modo punto a punto" y el "modo de publicación/suscripción".

modo de igual a igual

Un mensaje específico solo puede ser consumido por un consumidor, y varios productores pueden enviar mensajes a la misma cola de mensajes, pero cuando un mensaje es procesado por un mensajero, el mensaje se bloqueará o eliminará de la cola y otros consumidores no pueden procesar el mensaje. .

Cabe señalar que si un consumidor no procesa un mensaje, el sistema de mensajes generalmente volverá a colocar el mensaje en la cola para que otros consumidores puedan continuar procesándolo.

imagen

 Si desea aprender sobre pruebas automatizadas, aquí le recomiendo un conjunto de videos. Se puede decir que este video es el tutorial de pruebas automatizadas número uno en toda la red en la estación B. Al mismo tiempo, la cantidad de personas en línea ha aumentado. Llegó a 1000 y hay notas para recopilar y compartir con usted. Dashen Technical Exchange: 798478386   

[Actualizado] La colección más detallada de tutoriales prácticos para pruebas automatizadas de interfaces Python impartidas por la estación B (la última versión de combate real)_哔哩哔哩_bilibili [Actualizado] La colección más detallada de tutoriales prácticos para pruebas automatizadas de Python interfaces enseñadas por la estación B (combate real) La última versión) tiene un total de 200 videos, que incluyen: 1. ¿Por qué debería realizarse la automatización de la interfaz en la automatización de la interfaz?, 2. La vista general de la solicitud en la automatización de la interfaz, 3. Combate de la interfaz en automatización de interfaz, etc. UP alberga videos más interesantes, preste atención a la cuenta UP. https://www.bilibili.com/video/BV17p4y1B77x/?spm_id_from=333.337.search-card.all.click 

modelo de publicación/suscripción

Varios suscriptores pueden recuperar y procesar un único mensaje al mismo tiempo. En general, existen dos tipos de suscripciones:

  • Suscripciones efímeras : estas suscripciones solo existen mientras el consumidor está en funcionamiento. Una vez que el consumidor sale, la suscripción correspondiente y los mensajes no procesados ​​se pierden.

  • Suscripción duradera (duradera) : esta suscripción siempre existirá a menos que se elimine activamente. Una vez que el consumidor sale, el sistema de mensajería continuará manteniendo la suscripción y los mensajes posteriores podrán continuar procesándose.

imagen

1.2 Funciones de RabbitMQ

  • Enrutamiento de mensajes (soporte) : RabbitMQ puede admitir diferentes tipos de enrutamiento de mensajes a través de diferentes conmutadores;

  • Mensajes ordenados (no admitidos) : al consumir mensajes, si el consumo falla, los mensajes se volverán a colocar en la cola y luego se volverán a consumir, lo que provocará que los mensajes estén desordenados;

  • Tiempo de mensaje (muy bueno) : a través de la cola de retraso, puede especificar el tiempo de retraso del mensaje, el tiempo de vencimiento TTL, etc.;

  • Manejo tolerante a fallas (muy agradable) : maneja fallas en el procesamiento de mensajes con reintentos de entrega y un intercambio de mensajes no entregados (DLX);

  • Escalado (general) : El escalado no es muy inteligente, porque incluso si se escala, solo hay una cola maestra, y solo esta cola maestra puede resistir la carga, por lo que entiendo que el escalado de RabbitMQ es muy débil (comprensión personal) .

  • Persistencia (no muy buena) : Los mensajes sin consumo pueden admitir la persistencia. Esto es para garantizar que los mensajes se puedan restaurar cuando la máquina esté inactiva, pero los mensajes consumidos se eliminarán inmediatamente, porque RabbitMQ no está diseñado para almacenar datos históricos.

  • Retroceso de mensajes (soporte) : debido a que los mensajes no admiten el almacenamiento permanente, naturalmente no se admite el seguimiento de mensajes.

  • Alto rendimiento (medio) : debido a que todas las solicitudes se ejecutan en la cola maestra al final, su diseño hace que el rendimiento de una sola máquina no alcance el estándar de 100.000 niveles.

2. Un estudio preliminar sobre el principio de RabbitMQ.

Lanzado en 2007, RabbitMQ es un sistema de cola de mensajes de código abierto desarrollado en lenguaje Erlang e implementado en base al protocolo AMQP.

2.1 Conceptos básicos

Cuando se trata de RabbitMQ, hay que mencionar el protocolo AMQP. El protocolo AMQP es un protocolo binario con características modernas. Es un protocolo de cola de mensajes avanzado estándar de capa de aplicación que proporciona servicios de mensajería unificada. Es un estándar abierto para protocolos de capa de aplicación y está diseñado para middleware orientado a mensajes.

Primero comprenda varios conceptos importantes del protocolo AMQP:

  • Servidor: Recibe la conexión del cliente e implementa el servicio de entidad AMQP.

  • Conexión: conexión, conexión de red entre la aplicación y el Servidor, conexión TCP.

  • Canal: las operaciones de canal, lectura y escritura de mensajes se realizan en el canal. Un cliente puede establecer múltiples canales, cada canal representa una tarea de sesión.

  • Mensaje: Mensaje, los datos transmitidos entre la aplicación y el servidor, el mensaje puede ser muy simple o complejo. Se compone de Propiedades y Cuerpo. Propiedades es el embalaje exterior, que puede modificar el mensaje, como prioridad del mensaje, retraso y otras características avanzadas; Cuerpo es el contenido del cuerpo del mensaje.

  • Virtual Host: host virtual para aislamiento lógico. Puede haber varios Intercambios y Colas en un host virtual y no puede haber Intercambios o Colas con el mismo nombre en el mismo host virtual.

  • Intercambio: el intercambio recibe mensajes y los enruta a una o más colas de acuerdo con las reglas de enrutamiento. Si la ruta no está disponible, devuélvala al productor o deséchela directamente. Hay cuatro tipos de conmutadores comúnmente utilizados por RabbitMQ: directo, tema, distribución en abanico y encabezados, que se describirán en detalle más adelante.

  • Enlace: enlace, conexión virtual entre el intercambio y la cola de mensajes, el enlace puede contener una o más RoutingKey.

  • RoutingKey: clave de enrutamiento. Cuando el productor envía un mensaje al conmutador, enviará una clave de enrutamiento para especificar las reglas de enrutamiento, de modo que el conmutador sepa a qué cola enviar el mensaje. La clave de enrutamiento suele ser una cadena separada por ".", como "com.rabbitmq".

  • Cola: cola de mensajes, utilizada para almacenar mensajes para el consumo de los consumidores.

2.2 Principio de funcionamiento

El modelo de protocolo AMQP consta de tres partes: productor, consumidor y servidor, el proceso de ejecución es el siguiente:

  1. El productor se conecta al servidor, establece una conexión y abre un canal.

  2. Los productores declaran intercambios y colas, establecen propiedades relacionadas y vinculan intercambios y colas mediante claves de enrutamiento.

  3. Los consumidores también deben realizar operaciones como establecer una conexión y abrir un canal para recibir mensajes.

  4. El productor envía un mensaje al host virtual en el servidor.

  5. El conmutador en el host virtual selecciona reglas de enrutamiento de acuerdo con la clave de enrutamiento y las envía a diferentes colas de mensajes.

  6. Los consumidores que se suscriben a la cola de mensajes pueden recibir el mensaje y consumirlo.

imagen

2.3 Interruptores de uso común

Hay cuatro tipos de conmutadores que RabbitMQ utiliza habitualmente: directo, tema, distribución en abanico y encabezados:

  • Intercambio directo: como puede ver en el texto, el intercambio directo significa que el intercambio debe estar vinculado a una cola, lo que requiere que el mensaje coincida exactamente con una clave de enrutamiento específica. En pocas palabras, es un envío uno a uno, punto a punto.

imagen

  • Fanout Exchange: este tipo de intercambio requiere colas vinculantes al intercambio. Un mensaje enviado a un intercambio se reenvía a todas las colas vinculadas a ese intercambio. Al igual que la transmisión por subred, cada host de la subred recibe una copia del mensaje. En pocas palabras, es publicar y suscribirse.

imagen

  • Intercambio de temas: si se traduce directamente, se llama intercambio de temas. Si se traduce del uso anterior, se puede llamar intercambio de comodines, lo cual sería más apropiado. Este modificador utiliza comodines para hacer coincidir y enrutar a la cola correspondiente. Hay dos tipos de comodines: "*" y "#". Cabe señalar que se debe agregar el símbolo "." antes del comodín.

    • * símbolo: Existe y solo coincide una palabra. Por ejemplo, a.* puede coincidir con "ab" y "ac", pero no con "abc".

    • Símbolo #: Coincide con una o más palabras. Por ejemplo, "conejo.#" puede coincidir no sólo con "conejo.ab", "conejo.a", sino también con "conejo.abc".

imagen

  • Intercambio de encabezados: este tipo de conmutador relativamente no se usa tanto. Es un poco diferente de los tres tipos anteriores: su enrutamiento no utiliza la clave de enrutamiento para la coincidencia de enrutamiento, sino el enrutamiento haciendo coincidir el valor de la clave que se incluye en el encabezado de la solicitud. Para crear una cola, debe configurar la información del encabezado vinculante. Hay dos modos: coincidencia completa y coincidencia parcial. Como se muestra en la figura anterior, el conmutador hará coincidir el valor clave vinculado a la cola de acuerdo con el valor clave incluido en la información del encabezado enviada por el productor y lo enrutará a la cola correspondiente.

imagen

3. Construcción del entorno RabbitMQ

Como uso una Mac, puedo consultar directamente el sitio web oficial:

https://www.rabbitmq.com/install-homebrew.html

Cabe señalar que primero se debe ejecutar:

brew update

Luego ejecuta:

brew install rabbitmq

Antes de que no se ejecutara Brew Update, cuando se ejecutaba Brew Install Rabbitmq directamente, se informaban varios errores extraños, la mayoría de los cuales eran "403 Forbidde".

Pero cuando ejecuta "brew install Rabbitmq", otros programas se instalarán automáticamente. Si instala Rabbitmq usando el código fuente, debido a que el inicio del servicio depende del entorno de erlang, debe instalar erlang manualmente, pero el funcionario ya lo ha hecho. usted con un clic.¿No es genial instalar automáticamente todos los programas de los que depende Rabbitmq?

imagen

El resultado de la ejecución final exitosa es el siguiente:

imagen

Iniciar el servicio:

# 启动方式1:后台启动
brew services start rabbitmq
# 启动方式2:当前窗口启动
cd /usr/local/Cellar/rabbitmq/3.8.19
rabbitmq-server

Ingrese en el navegador:

http://localhost:15672/

Aparecerá la interfaz de administración en segundo plano de RabbitMQ (tanto el nombre de usuario como la contraseña son invitados):

imagen

Instalado a través de Brew, una línea de comando para hacerlo, ¡realmente fragante!

4. Integración de RabbitMQ

4.1 Trabajo preliminar

Añadir cuenta:

## 添加账号
./rabbitmqctl add_user admin admin
## 添加访问权限
./rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
## 设置超级权限
./rabbitmqctl set_user_tags admin administrator

Pom introduce dependencias:

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.5.1</version>
</dependency>

 

4.2 Implementación del código

código central

Primero complete un singleton de ConnectionFactory, cada máquina tiene su propio ConnectionFactory para evitar la inicialización cada vez (en iteraciones posteriores, lo eliminaré y lo convertiré en un grupo de conexiones).

/**
 * @author Louzai
 * @date 2023/5/10
 */
public class RabbitmqUtil {

    /**
     * 每个key都有自己的工厂
     */
    private static Map<String, ConnectionFactory> executors = new ConcurrentHashMap<>();

    /**
     * 初始化一个工厂
     *
     * @param host
     * @param port
     * @param username
     * @param passport
     * @param virtualhost
     * @return
     */
    public static ConnectionFactory init(String host,
                                  Integer port,
                                  String username,
                                  String passport,
                                  String virtualhost) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(host);
        factory.setPort(port);
        factory.setUsername(username);
        factory.setPassword(passport);
        factory.setVirtualHost(virtualhost);
        return factory;
    }

    /**
     * 工厂单例,每个key都有属于自己的工厂
     *
     * @param key
     * @param host
     * @param port
     * @param username
     * @param passport
     * @param virtualhost
     * @return
     */
    public static ConnectionFactory getOrInitConnectionFactory(String key,
                                                               String host,
                                                               Integer port,
                                                               String username,
                                                               String passport,
                                                               String virtualhost) {
        ConnectionFactory connectionFactory = executors.get(key);
        if (null == connectionFactory) {
            synchronized (RabbitmqUtil.class) {
                connectionFactory = executors.get(key);
                if (null == connectionFactory) {
                    connectionFactory = init(host, port, username, passport, virtualhost);
                    executors.put(key, connectionFactory);
                }
            }
        }
        return connectionFactory;
    }
}

Obtenga RabbitmqClient:

/**
 * @author Louzai
 * @date 2023/5/10
 */
@Component
public class RabbitmqClient {

    @Autowired
    private RabbitmqProperties rabbitmqProperties;

    /**
     * 创建一个工厂
     * @param key
     * @return
     */
    public ConnectionFactory getConnectionFactory(String key) {
        String host = rabbitmqProperties.getHost();
        Integer port = rabbitmqProperties.getPort();
        String userName = rabbitmqProperties.getUsername();
        String password = rabbitmqProperties.getPassport();
        String virtualhost = rabbitmqProperties.getVirtualhost();
        return RabbitmqUtil.getOrInitConnectionFactory(key, host, port, userName,password, virtualhost);
    }
}

¡enfocar! ¡Toca la pizarra! ! ! Aquí está la lógica central de RabbmitMQ.

El tipo de conmutador que utilizamos es Direct Exchange. Este conmutador debe estar vinculado a una cola y requiere que el mensaje coincida exactamente con una clave de enrutamiento específica. En pocas palabras, es un envío uno a uno, punto a punto.

En cuanto a por qué no utilizamos el modo de transmisión y cambio de tema, porque el escenario de uso de la escuela técnica es enviar un solo mensaje, y el modo de envío y consumo punto a punto puede satisfacer completamente nuestras necesidades.

Los siguientes 3 métodos son muy simples:

  • Enviar mensaje: obtener fábrica -> crear enlace -> crear canal -> declarar cambio -> enviar mensaje -> cerrar enlace;

  • Consumir mensajes: obtener fábrica -> crear enlace -> crear canal -> determinar cola de mensajes -> vincular cola para cambiar -> aceptar y consumir mensajes;

  • Modo perpetuo de mensajes de consumo: consume mensajes RabbitMQ en modo sin bloqueo.

@Component
public class RabbitmqServiceImpl implements RabbitmqService {

    @Autowired
    private RabbitmqClient rabbitmqClient;

    @Autowired
    private NotifyService notifyService;

    @Override
    public void publishMsg(String exchange,
                           BuiltinExchangeType exchangeType,
                           String toutingKey,
                           String message) throws IOException, TimeoutException {
        ConnectionFactory factory = rabbitmqClient.getConnectionFactory(toutingKey);

        // TODO: 这种并发量起不来,需要改造成连接池

        //创建连接
        Connection connection = factory.newConnection();
        //创建消息通道
        Channel channel = connection.createChannel();

        // 声明exchange中的消息为可持久化,不自动删除
        channel.exchangeDeclare(exchange, exchangeType, true, false, null);

        // 发布消息
        channel.basicPublish(exchange, toutingKey, null, message.getBytes());

        System.out.println("Publish msg:" + message);
        channel.close();
        connection.close();
    }

    @Override
    public void consumerMsg(String exchange,
                            String queue,
                            String routingKey) throws IOException, TimeoutException {
        ConnectionFactory factory = rabbitmqClient.getConnectionFactory(routingKey);

        // TODO: 这种并发量起不来,需要改造成连接池

        //创建连接
        Connection connection = factory.newConnection();
        //创建消息信道
        final Channel channel = connection.createChannel();
        //消息队列
        channel.queueDeclare(queue, true, false, false, null);
        //绑定队列到交换机
        channel.queueBind(queue, exchange, routingKey);

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("Consumer msg:" + message);

                // 获取Rabbitmq消息,并保存到DB
                // 说明:这里仅作为示例,如果有多种类型的消息,可以根据消息判定,简单的用 if...else 处理,复杂的用工厂 + 策略模式
                notifyService.saveArticleNotify(JsonUtil.toObj(message, UserFootDO.class), NotifyTypeEnum.PRAISE);

                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 取消自动ack
        channel.basicConsume(queue, false, consumer);
    }

    @Override
    public void processConsumerMsg() {
        System.out.println("Begin to processConsumerMsg.");

        Integer stepTotal = 1;
        Integer step = 0;

        // TODO: 这种方式非常 Low,后续会改造成阻塞 I/O 模式
        while (true) {
            step ++;
            try {
                System.out.println("processConsumerMsg cycle.");
                consumerMsg(CommonConstants.EXCHANGE_NAME_DIRECT, CommonConstants.QUERE_NAME_PRAISE,
                        CommonConstants.QUERE_KEY_PRAISE);
                if (step.equals(stepTotal)) {
                    Thread.sleep(10000);
                    step = 0;
                }
            } catch (Exception e) {

            }
        }
    }
}

Aquí hay solo un ejemplo: si quieres usarlo en un entorno de producción, ¿qué problemas crees que hay?  Piénsalo tú primero y te lo contaré al final del artículo.

entrada de llamada

De hecho, antes utilizamos el método de llamada asincrónica incorporado de Java. Para facilitar la verificación, migré la función de me gusta del artículo a RabbitMQ. Mientras sean me gusta, seguiremos el modo RabbitMQ.

// 点赞消息走 RabbitMQ,其它走 Java 内置消息机制
if (notifyType.equals(NotifyTypeEnum.PRAISE) && rabbitmqProperties.getSwitchFlag()) {
    rabbitmqService.publishMsg(
            CommonConstants.EXCHANGE_NAME_DIRECT,
            BuiltinExchangeType.DIRECT,
            CommonConstants.QUERE_KEY_PRAISE,
            JsonUtil.toStr(foot));
} else {
    Optional.ofNullable(notifyType).ifPresent(notify -> SpringUtil.publishEvent(new NotifyMsgEvent<>(this, notify, foot)));
}

¿Dónde está la entrada de consumo? De hecho, cuando se inicia el programa, iniciamos RabbitMQ para su consumo y luego todo el proceso continúa ejecutándose en el programa.

@Override
public void run(ApplicationArguments args) {
    // 设置类型转换, 主要用于mybatis读取varchar/json类型数据据,并写入到json格式的实体Entity中
    JacksonTypeHandler.setObjectMapper(new ObjectMapper());
    // 应用启动之后执行
    GlobalViewConfig config = SpringUtil.getBean(GlobalViewConfig.class);
    if (webPort != null) {
        config.setHost("http://127.0.0.1:" + webPort);
    }
    // 启动 RabbitMQ 进行消费
    if (rabbitmqProperties.getSwitchFlag()) {
        taskExecutor.execute(() -> rabbitmqService.processConsumerMsg());
    }
    log.info("启动成功,点击进入首页: {}", config.getHost());
}

4.3 Demostración

Hicimos clic en el botón "Me gusta" varias veces para activar el envío del mensaje RammitMQ.

imagen

También puede ver los mensajes enviados y consumidos a través del registro.

imagen

¡Confío en! Muchos enlaces no cerrados. . .

imagen

Todavía hay muchos canales que no están cerrados. . .

imagen

Se estima que después de ejecutarse por un tiempo, se consumirá toda la memoria y la máquina fallará, ¿cómo se puede romper? ¡La respuesta es el grupo de conexiones!

4.4 Ramas de código

Para facilitar que todos aprendan el proceso de evolución de funciones, cada módulo abrirá una rama separada, incluida la versión actualizada posterior:

  • Almacén de códigos: https://github.com/itwanger/paicoding

  • Rama de código: feature/add_rabbitmq_20230506

Si necesita ejecutar RabbitMQ, la siguiente configuración debe cambiarse a verdadera, porque el código predeterminado es falso.

imagen

5 Epílogo

Este artículo les permite a todos conocer los principios básicos de RabbitMQ y cómo integrar RabbitMQ, pero no se puede usar en el entorno de producción real, pero esta es de hecho la primera versión que escribí y es puramente por diversión, porque hay Hay todavía muchos problemas.

Enumeraré brevemente:

  1. Debe agregar un grupo de conexiones a Connection; de lo contrario, la memoria seguirá consumiéndose y la máquina definitivamente no podrá manejarla;

  2. El método de consumo de RabbitMQ debe transformarse, porque el método while + sleep es demasiado simple y grosero;

  3. Si la tarea de consumo cuelga, necesita tener un mecanismo de consumo para reiniciar RabbitMQ;

  4. Si la máquina cuelga, los mensajes dentro de RabbitMQ no se pueden perder después de reiniciar.

Si también está muy interesado en las preguntas anteriores, puede basarse directamente en la función de rama / add_rabbitmq_20230506 y luego darme un PR, tecnología, me gusta aprender mientras juego.

Supongo que te gusta

Origin blog.csdn.net/m0_73409141/article/details/132540698
Recomendado
Clasificación