Introdução e uso do RabbitMQ

Índice

1. Visão geral do MQ

2. Introdução ao RabbitMQ

3. Cinco modos de trabalho do RabbitMQ

1. Modo simples

2. filas de trabalho modo fila de trabalho

3. Modelo de assinatura Pub/Sub

4. Modo de roteamento de roteamento

5. Modo curinga de tópicos


1. Visão geral do MQ

O nome completo do MQ é Message Queue (Message Queue), que é um contêiner que salva mensagens durante o processo de transmissão de mensagens. Usado principalmente em sistemas distribuídos

comunicar entre eles.

Resumir:

MQ, fila de mensagens, middleware para armazenar mensagens

Existem duas formas de comunicação em sistemas distribuídos: chamadas remotas diretas e comunicação indireta através de terceiros.

O remetente é chamado de produtor e o destinatário é chamado de consumidor

Vantagens do MQ:

Desacoplamento de aplicativos: melhore a tolerância a falhas e a capacidade de manutenção do sistema

Aceleração assíncrona: melhore a experiência do usuário e o rendimento do sistema

Redução de picos e preenchimento de vales: melhorando a estabilidade do sistema

Desvantagens do MQ:

1. Disponibilidade reduzida do sistema

Quanto mais dependências externas forem introduzidas no sistema, pior será a estabilidade do sistema. Quando o MQ cair, isso terá um impacto nos negócios.

2. A complexidade do sistema aumenta

A adição do MQ aumentou muito a complexidade do sistema.No passado, as chamadas remotas síncronas eram feitas entre sistemas, mas agora as chamadas assíncronas são feitas através do MQ.

usar. Como garantir que as mensagens não sejam consumidas repetidamente? Como lidar com a perda de mensagens? Em seguida, garantir a ordem de entrega das mensagens?

3. Questão de consistência

Depois que o sistema A processa o negócio, ele envia dados de mensagens para os sistemas B, C e D por meio do MQ. Se o sistema B e o sistema C o processam com êxito, o sistema D falha ao processá-lo. Como garantir a consistência do processamento de dados de mensagens?

Condições para usar MQ

1. Os produtores não precisam obter feedback dos consumidores. Para chamadas diretas antes de introduzir a fila de mensagens, o valor de retorno de sua interface deve ser

Vazio, permite que a camada superior continue avançando como se a ação da camada inferior ainda não tivesse sido concluída, ou seja, o chamado assíncrono.

2. Permita inconsistências temporárias.

3. É realmente eficaz. Ou seja, os benefícios de desacoplamento, aceleração e corte de pico excedem os custos de adesão e gerenciamento do MQ.

QM comum

2. Introdução ao RabbitMQ

AMQP, Advanced Message Queuing Protocol, é um protocolo de rede e um aplicativo

Um padrão aberto para protocolos de camada projetados para middleware orientado a mensagens. Clientes e middleware de mensagens baseados neste protocolo podem entregar mensagens

As informações não são limitadas por diferentes produtos de cliente/middleware, diferentes linguagens de desenvolvimento, etc. Em 2006, a especificação AMQP foi lançada.

Analogia ao HTTP.

Em 2007, foi lançado o RabbitMQ1.0, desenvolvido pela Rabbit Technology Company com base no padrão AMQP. RabbitMQ é desenvolvido usando a linguagem Erlang

cabelo. A linguagem Erlang foi projetada por Ericson. É uma linguagem projetada especificamente para o desenvolvimento de sistemas distribuídos e de alta concorrência, sendo amplamente utilizada na área de telecomunicações.

frigideira.

Conceitos relacionados no RabbitMQ:

Broker: Um aplicativo que recebe e distribui mensagens. RabbitMQ Server é o Message Broker.

Host virtual: Projetado por motivos de multilocação e segurança, os componentes básicos do AMQP são divididos em um grupo virtual, semelhante ao conceito de namespace na rede. Quando vários usuários diferentes usam os serviços fornecidos pelo mesmo servidor RabbitMQ, vários vhosts podem ser divididos e cada usuário cria troca/fila, etc.

Conexão: conexão TCP entre editor/consumidor e corretor

Canal: Se uma conexão for estabelecida toda vez que o RabbitMQ for acessado, a sobrecarga do TCPConnection será enorme e a eficiência será baixa quando o volume de mensagens for grande. Canal é uma conexão lógica estabelecida dentro da conexão. Se o aplicativo suportar multi-threading, cada thread geralmente cria um canal separado para comunicação. O método AMQP inclui o ID do canal para ajudar o cliente e o corretor de mensagens a identificar o canal, para que os canais sejam completamente isolado. de. O Canal, como uma conexão leve, reduz bastante a sobrecarga do sistema operacional no estabelecimento de conexões TCP.

Troca: A mensagem chega à primeira parada do corretor, de acordo com as regras de distribuição, corresponde à chave de roteamento da tabela de consulta e distribui a mensagem para a fila. Os tipos comumente usados ​​são: direto (ponto a ponto), tópico (publicar-assinar) e fanout (multicast).

Fila: A mensagem é finalmente enviada aqui para ser retirada pelo consumidor.

Vinculação: A conexão virtual entre troca e fila. A ligação pode conter chave de roteamento. As informações de ligação são salvas na tabela de consulta em troca e usadas como base para distribuição de mensagens.

RabbitMQ oferece 6 modos de trabalho:

Modo simples, filas de trabalho, modo de publicação e assinatura de publicação/assinatura,

Modo de roteamento de roteamento, modo de tema de tópicos, modo de chamada remota RPC (chamada remota, não exatamente MQ; não iremos apresentá-los ainda).

Introdução ao modo correspondente no site oficial: https://www.rabbitmq.com


JMS

JMS é a interface do programa aplicativo Java Message Service.É um middleware orientado a mensagens na plataforma Java.

API de software

JMS é uma das especificações JavaEE, análoga ao JDBC

Muitos middlewares de mensagens implementam a especificação JMS, como ActiveMQ. RabbitMQ não fornece oficialmente um pacote de implementação JMS, mas

A comunidade de código aberto tem

3. Cinco modos de trabalho do RabbitMQ

Primeiro crie um projeto maven e crie dois módulos nele (um produtor e um consumidor)

2. Adicione dependências (produtores e consumidores dependem disso)

<dependencies>
        <!--rabbitmq java 客户端-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.6.0</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

1. Modo simples

P: Produtor, ou seja, o programa que deseja enviar a mensagem
C: Consumidor: O receptor da mensagem, que aguardará a chegada da mensagem
Fila: Fila de mensagens, a parte vermelha da imagem. Semelhante a uma caixa de correio, as mensagens podem ser armazenadas em cache; os produtores entregam mensagens a ela e os consumidores recuperam mensagens dela.

1. Crie um produtor

public class HelloRabbitmq {
    public static void main(String[] args) throws IOException, TimeoutException {

        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");//ip  默认值 localhost
        factory.setPort(5672); //端口  默认值 5672
        //factory.setVirtualHost("/itcast");//虚拟机 默认值/
        factory.setUsername("admin");//用户名 默认 guest
        factory.setPassword("admin");//密码 默认值 guest
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //5. 创建队列Queue
        /*
        queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        参数:
            1. queue:队列名称
            2. durable:是否持久化,当mq重启之后,还在
            3. exclusive:
                * 是否独占。只能有一个消费者监听这队列
                * 当Connection关闭时,是否删除队列
                *
            4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
            5. arguments:参数。

         */
        //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
        channel.queueDeclare("hello_world",true,false,false,null);

        String body = "Hello rabbitmq....";
        /*
        * basicPublish(String exchange,String routingKey, BasicProperties props, byte[ ] body)参数:
        1. exchange:交换机名称。简单模式下交换机会使用黑认的“"
        *2. routingKey:路由名称,默认交换机和队列名一样就可以绑定
        3. props :配置信息
        4. body:发送消息数据
        * */
        //发送消息
        channel.basicPublish("","hello_world",null,body.getBytes());

        channel.close();
        connection.close();

    }
}

2. Crie consumidores

public class ConsumerHelloWorld {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //5. 创建队列Queue
        /*
        queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        参数:
            1. queue:队列名称
            2. durable:是否持久化,当mq重启之后,还在
            3. exclusive:
                * 是否独占。只能有一个消费者监听这队列
                * 当Connection关闭时,是否删除队列
                *
            4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
            5. arguments:参数。

         */
        //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
        channel.queueDeclare("hello_world",true,false,false,null);
        /*
        basicConsume(String queue, boolean autoAck, Consumer callback)
        参数:
            1. queue:队列名称
            2. autoAck:是否自动确认
            3. callback:回调对象
         */
        // 接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /*
                回调方法,当收到消息后,会自动执行该方法
                1. consumerTag:标识
                2. envelope:获取一些信息,交换机,路由key...
                3. properties:配置信息
                4. body:数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumerTag:" + consumerTag);
                System.out.println("Exchange:" + envelope.getExchange());
                System.out.println("RoutingKey:" + envelope.getRoutingKey());
                System.out.println("properties:" + properties);
                System.out.println("body:" + new String(body));
            }
        };
        channel.basicConsume("hello_world",true,consumer);
        //关闭资源?不要
    }
}

2. filas de trabalho modo fila de trabalho

Filas de Trabalho: Comparado com o modo simples do programa de entrada, há um ou mais consumidores, e vários consumidores consomem o mesmo

Mensagens na fila. Cenário de aplicação: Quando a tarefa é muito pesada ou há muitas tarefas, o uso da fila de trabalho pode melhorar a velocidade do processamento da tarefa.

1. Crie um produtor

public class ProducerWorkQueues {
    public static void main(String[] args) throws IOException, TimeoutException {

        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");//ip  默认值 localhost
        factory.setPort(5672); //端口  默认值 5672
        //factory.setVirtualHost("/itcast");//虚拟机 默认值/
        factory.setUsername("admin");//用户名 默认 guest
        factory.setPassword("admin");//密码 默认值 guest
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //5. 创建队列Queue
        /*
        queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        参数:
            1. queue:队列名称
            2. durable:是否持久化,当mq重启之后,还在
            3. exclusive:
                * 是否独占。只能有一个消费者监听这队列
                * 当Connection关闭时,是否删除队列
                *
            4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
            5. arguments:参数。

         */
        //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
        channel.queueDeclare("hello_workQueues",true,false,false,null);
        //创建消息
        for (int i = 1; i <= 10; i++) {
            String body = i+"hello rabbitmq~~~";

            //6. 发送消息
            channel.basicPublish("","hello_workQueues",null,body.getBytes());
        }
        /*
        * basicPublish(String exchange,String routingKey, BasicProperties props, byte[ ] body)参数:
        1. exchange:交换机名称。简单模式下交换机会使用黑认的“"
        2. routingKey:路由名称
        3. props :配置信息
        4. body:发送消息数据
        * */
        //7.释放资源
        channel.close();
        connection.close();

    }
}

2. Crie consumidores: você precisa criar dois consumidores aqui para ver o efeito.

public class ConsumerWorkQueues1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //5. 创建队列Queue
        /*
        queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        参数:
            1. queue:队列名称
            2. durable:是否持久化,当mq重启之后,还在
            3. exclusive:
                * 是否独占。只能有一个消费者监听这队列
                * 当Connection关闭时,是否删除队列
                *
            4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
            5. arguments:参数。

         */
        //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
        channel.queueDeclare("hello_workQueues",true,false,false,null);
        /*
        basicConsume(String queue, boolean autoAck, Consumer callback)
        参数:
            1. queue:队列名称
            2. autoAck:是否自动确认
            3. callback:回调对象
         */
        // 接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /*
                回调方法,当收到消息后,会自动执行该方法
                1. consumerTag:标识
                2. envelope:获取一些信息,交换机,路由key...
                3. properties:配置信息
                4. body:数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("body:" + new String(body));
            }
        };
        channel.basicConsume("hello_workQueues",true,consumer);
        //关闭资源?不要
    }
}

3. Modelo de assinatura Pub/Sub

No modelo de assinatura, há uma função adicional do Exchange e o processo muda ligeiramente:

P: Produtor, ou seja, o programa que quer enviar uma mensagem, mas não a envia mais para a fila, mas para X (switch) C: O consumidor, receptor da mensagem, sempre aguardará a chegada da mensagem Fila : Fila de mensagens,
receber mensagens, mensagens em cache
Exchange: Switch (X). Por um lado, são recebidas mensagens enviadas pelos produtores. Por outro lado, saber tratar a mensagem, como submetê-la para uma fila especial, submetê-la a todas as filas ou descartar a mensagem. Como fazer isso depende do tipo de Exchange. O Exchange tem os três tipos comuns a seguir:
>Fanout: Broadcast, entrega a mensagem a todas as filas vinculadas à exchange
>Direto: Direcionado, entrega a mensagem à fila que corresponde à chave de roteamento especificada
>Tópico: Curinga, entrega a mensagem ao fila que corresponde à chave de roteamento A fila
Exchange ) é responsável apenas por encaminhar mensagens e não tem a capacidade de armazenar mensagens. Portanto, se não houver fila vinculada ao Exchange, ou não houver fila que esteja em conformidade com as regras de roteamento, a mensagem será perdida!

1. Crie um produtor

public class ProducerPubSub {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");//ip  默认值 localhost
        factory.setPort(5672); //端口  默认值 5672
        //factory.setVirtualHost("/itcast");//虚拟机 默认值/
        factory.setUsername("admin");//用户名 默认 guest
        factory.setPassword("admin");//密码 默认值 guest
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
       /*

       exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
       参数:
        1. exchange:交换机名称
        2. type:交换机类型
            DIRECT("direct"),:定向
            FANOUT("fanout"),:扇形(广播),发送消息到每一个与之绑定队列。
            TOPIC("topic"),通配符的方式
            HEADERS("headers");参数匹配

        3. durable:是否持久化
        4. autoDelete:自动删除
        5. internal:内部使用。 一般false
        6. arguments:参数
        */

        String exchangeName = "test_fanout";
        //5. 创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);

        //6、创建队列
        String queueName1 = "test_fanout_queue1";
        String queueName2 = "test_fanout_queue2";
        channel.queueDeclare(queueName1,true,false,false,null);
        channel.queueDeclare(queueName2,true,false,false,null);
        //7. 绑定队列和交换机
        /*
        queueBind(String queue, String exchange, String routingKey)
        参数:
            1. queue:队列名称
            2. exchange:交换机名称
            3. routingKey:路由键,绑定规则
                如果交换机的类型为fanout ,routingKey设置为""
         */
        channel.queueBind(queueName1,exchangeName,"");
        channel.queueBind(queueName2,exchangeName,"");

        String body = "发送消息。。。。";
        //8. 发送消息
        channel.basicPublish(exchangeName,"",null,body.getBytes());

        //9. 释放资源
        channel.close();
        connection.close();

    }
}

2. Crie consumidores: você precisa criar dois consumidores aqui para ver o efeito.

public class ConsumerPubSub1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();

        //队列名称
        String queueName1 = "test_fanout_queue1";
        String queueName2 = "test_fanout_queue2";
        /*
        basicConsume(String queue, boolean autoAck, Consumer callback)
        参数:
            1. queue:队列名称
            2. autoAck:是否自动确认
            3. callback:回调对象
         */
        // 接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /*
                回调方法,当收到消息后,会自动执行该方法
                1. consumerTag:标识
                2. envelope:获取一些信息,交换机,路由key...
                3. properties:配置信息
                4. body:数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息打印到控制台.....");
            }
        };
        channel.basicConsume(queueName1,true,consumer);
    }
}

 Execute resultados do consumidor

4. Modo de roteamento de roteamento

Descrição do modo:

A ligação entre a fila e o switch não pode ser feita arbitrariamente, mas um RoutingKey deve ser especificado.

Ao enviar uma mensagem para o Exchange, o remetente da mensagem também deve especificar o RoutingKey da mensagem. O
Exchange não entrega mais a mensagem para cada fila vinculada, mas julga com base na Routing Key da mensagem. Somente o Routingkey da fila e a chave de roteamento da mensagem é Somente se forem completamente consistentes a mensagem será recebida.

Ilustração:

P: Produtor, envia uma mensagem para o Exchange. Ao enviar uma mensagem, uma chave de roteamento será especificada.
X: Exchange (switch), recebe a mensagem do produtor e, em seguida, entrega a mensagem para a fila que corresponde exatamente à chave de roteamento .

C1: Consumidor, cuja fila especifica mensagens que exigem que a chave de roteamento seja erro.C2
: Consumidor, cuja fila especifica mensagens que requerem chaves de roteamento de informação, erro e aviso.
 

1. Crie um produtor

public class ProducerRouting {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");//ip  默认值 localhost
        factory.setPort(5672); //端口  默认值 5672
        //factory.setVirtualHost("/itcast");//虚拟机 默认值/
        factory.setUsername("admin");//用户名 默认 guest
        factory.setPassword("admin");//密码 默认值 guest
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
       /*

       exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
       参数:
        1. exchange:交换机名称
        2. type:交换机类型
            DIRECT("direct"),:定向
            FANOUT("fanout"),:扇形(广播),发送消息到每一个与之绑定队列。
            TOPIC("topic"),通配符的方式
            HEADERS("headers");参数匹配

        3. durable:是否持久化
        4. autoDelete:自动删除
        5. internal:内部使用。 一般false
        6. arguments:参数
        */

        String exchangeName = "test_direct";
        //5. 创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,false,null);
        //6. 创建队列
        String queueName1 = "test_direct_queue1";
        String queueName2 = "test_direct_queue2";

        channel.queueDeclare(queueName1,true,false,false,null);
        channel.queueDeclare(queueName2,true,false,false,null);
        //7. 绑定队列和交换机
        /*
        queueBind(String queue, String exchange, String routingKey)
        参数:
            1. queue:队列名称
            2. exchange:交换机名称
            3. routingKey:路由键,绑定规则
                如果交换机的类型为fanout ,routingKey设置为""
         */
        //队列1绑定 error
        channel.queueBind(queueName1,exchangeName,"error");
        //队列2绑定 info  error  warning
        channel.queueBind(queueName2,exchangeName,"info");
        channel.queueBind(queueName2,exchangeName,"error");
        channel.queueBind(queueName2,exchangeName,"warning");

        String body = "日志信息:张三调用了delete方法...出错误了。。。日志级别:error...";
        //8. 发送消息
        channel.basicPublish(exchangeName,"info",null,body.getBytes());

        //9. 释放资源
        channel.close();
        connection.close();

    }
}

 

2. Crie consumidores: você precisa criar dois consumidores aqui para ver o efeito.

public class ConsumerRouting1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //队列名称
        String queueName1 = "test_direct_queue1";
        String queueName2 = "test_direct_queue2";
        
        /*
        basicConsume(String queue, boolean autoAck, Consumer callback)
        参数:
            1. queue:队列名称
            2. autoAck:是否自动确认
            3. callback:回调对象
         */
        // 接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /*
                回调方法,当收到消息后,会自动执行该方法
                1. consumerTag:标识
                2. envelope:获取一些信息,交换机,路由key...
                3. properties:配置信息
                4. body:数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息打印到控制台.....");
            }
        };
        channel.basicConsume(queueName1,true,consumer);
        
        //关闭资源?不要
    }
}

 

5. Modo curinga de tópicos

O modo de tópico de tópico pode realizar as funções do modo de publicação e assinatura Pub/Sub e do modo de roteamento de roteamento, mas o tópico pode usar curingas ao configurar a chave de roteamento, que é mais flexível.

1. Crie um produtor

public class ProducerTopics {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");//ip  默认值 localhost
        factory.setPort(5672); //端口  默认值 5672
        //factory.setVirtualHost("/itcast");//虚拟机 默认值/
        factory.setUsername("admin");//用户名 默认 guest
        factory.setPassword("admin");//密码 默认值 guest
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
       /*
       exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
       参数:
        1. exchange:交换机名称
        2. type:交换机类型
            DIRECT("direct"),:定向
            FANOUT("fanout"),:扇形(广播),发送消息到每一个与之绑定队列。
            TOPIC("topic"),通配符的方式
            HEADERS("headers");参数匹配
        3. durable:是否持久化
        4. autoDelete:自动删除
        5. internal:内部使用。 一般false
        6. arguments:参数
        */
        String exchangeName = "test_topic";
        //5. 创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,false,null);
        //6. 创建队列
        String queueName1 = "test_topic_queue1";
        String queueName2 = "test_topic_queue2";
        channel.queueDeclare(queueName1,true,false,false,null);
        channel.queueDeclare(queueName2,true,false,false,null);
        //7. 绑定队列和交换机
        /*
        queueBind(String queue, String exchange, String routingKey)
        参数:
            1. queue:队列名称
            2. exchange:交换机名称
            3. routingKey:路由键,绑定规则
                如果交换机的类型为fanout ,routingKey设置为""
         */
        // routing key  系统的名称.日志的级别。
        //=需求: 所有error级别的日志存入数据库,所有order系统的日志存入数据库
        channel.queueBind(queueName1,exchangeName,"#.error");
        channel.queueBind(queueName1,exchangeName,"order.*");
        channel.queueBind(queueName2,exchangeName,"*.*");

        String body = "日志信息:张三调用了findAll方法...日志级别:info...";
        //8. 发送消息
        channel.basicPublish(exchangeName,"order.error",null,body.getBytes());

        //9. 释放资源
        channel.close();
        connection.close();
    }
}

2. Crie consumidores: você precisa criar dois consumidores aqui para ver o efeito.


public class ConsumerTopic1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2. 设置参数
        factory.setHost("192.168.138.129");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        //3. 创建连接 Connection
        Connection connection = factory.newConnection();
        //4. 创建Channel
        Channel channel = connection.createChannel();
        //队列名称
        String queueName1 = "test_topic_queue1";
        String queueName2 = "test_topic_queue2";
        /*
        basicConsume(String queue, boolean autoAck, Consumer callback)
        参数:
            1. queue:队列名称
            2. autoAck:是否自动确认
            3. callback:回调对象
         */
        // 接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /*
                回调方法,当收到消息后,会自动执行该方法
                1. consumerTag:标识
                2. envelope:获取一些信息,交换机,路由key...
                3. properties:配置信息
                4. body:数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息存入数据库.......");
            }
        };
        channel.basicConsume(queueName1,true,consumer);
        //关闭资源?不要
    }
}

Acho que você gosta

Origin blog.csdn.net/qi341500/article/details/129700257
Recomendado
Clasificación