RabbitMQ 工作模式二

之前写了WORKQUEUES 跟   Publish/Subscribe 俩种模式 ,RabbitMQ 工作模式一

Routing 工作模式

特点

      每个消费者监听自己的队列,并且设置routingkey

      生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列

说白了,就是在publish/subscribe 工作模式的基础上加一层筛选,判断

 if(队列.routingkey == 生产者.routingkey)    
        发送消息给duilie

下面是我写的生产者

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer03 {

    //队列名称
    private static final String QUEUE_INFORM_TEST1 = "queue_inform_test1";
    private static final String QUEUE_INFORM_TEST2 = "queue_inform_test2";
    public final static String  EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";

    public static void main(String[] args) throws Exception{


        ConnectionFactory factory = new ConnectionFactory();

        //设置ip
        factory.setHost("localhost");

        //设置端口
        factory.setPort(5672);

        //设置账号密码
        factory.setUsername("guest");  //默认账号密码都是guest
        factory.setPassword("guest");

        //设置虚拟空间
        factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();//创建一个通道


        /**
         * 定义交换机
         *  param1 :  交换机名称
         *  param2 :   交换机类型
         *
         */
        channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);

        /**
         *    定义消息队列
         *
         *    String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
         *
         */


        channel.queueDeclare(QUEUE_INFORM_TEST1,true ,false ,false ,null);
        channel.queueDeclare(QUEUE_INFORM_TEST2,true ,false ,false ,null);

        //绑定交换机跟队列
        /*
            String queue, String exchange, String routingKey
         */
        channel.queueBind(QUEUE_INFORM_TEST1,EXCHANGE_ROUTING_INFORM ,QUEUE_INFORM_TEST1);
        channel.queueBind(QUEUE_INFORM_TEST2,EXCHANGE_ROUTING_INFORM ,QUEUE_INFORM_TEST2);


        String message = "";

        //给队列发送消息

        for (int i = 0; i < 9; i++) {

//
//            String exchange, String routingKey, BasicProperties props, byte[] body


            message = "人间有百媚千红,唯你是我情之所钟。第"+i+"条消息";

            //给test1 发送9条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_TEST1 ,null ,message.getBytes() );
        }


        for (int i = 0; i < 5; i++) {

//
//            String exchange, String routingKey, BasicProperties props, byte[] body


            message = "你别回头看我了,走吧,山高水长,可别再碰到我这么喜欢你的人了。第"+i+"条消息";

            //给test2 发送5条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_TEST2 ,null ,message.getBytes() );
        }
        
        
        channel.close();
        
        connection.close();
    }


}

设置了交换机名称,及路由的交换机类型

channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);

--------------------------------------------------------------------------------------------------------------------------------------

 声明了俩个队列...分别为 test1, test2     

**
 * String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
 *
 * param1: 队列名称
 * param2: 是否持久化
 * param3 : 是否独占此队列
 * param4 : 队列不用是否自动删除
 *  param5 : 参数
 */
channel.queueDeclare(QUEUE_INFORM_Test1,true ,false ,false ,null );
 channel.queueDeclare(QUEUE_INFORM_Test2,true ,false ,false ,null );

------------------------------------------------------------------------------------------------------------

队列和交换机绑定  

//交换机和队列绑定
/**
 * String queue, String exchange, String routingKey
 * param1 :  队列名称
 * exchange :  交换机
 * routingKey : 给队列添加一个 路由key,交换机发送消息时根据填写的路由key 来判断,如果填写的key 跟 队列的路由key 相同,那么就会发送消息给此队列 

 */
  channel.queueBind(QUEUE_INFORM_TEST1,EXCHANGE_ROUTING_INFORM ,QUEUE_INFORM_TEST1);
  channel.queueBind(QUEUE_INFORM_TEST2,EXCHANGE_ROUTING_INFORM ,QUEUE_INFORM_TEST2);

-------------------------------------------------------------------------

发送消息

/**
 * String exchange, String routingKey, BasicProperties props, byte[] body
 *
 *   param1  交换机名称
 *   param2  根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列
 *   param3  参数
 *   param4 传递的字符串
 *
 *
 */
 channel.basicPublish(EXCHANGE_FANOUT_INFORM,"" , null, message.getBytes());

``````````````````````````````````````````````````````````````````````````````````````````````````````````````

以上是生产者的代码 ,下面提供消费者的代码

import com.rabbitmq.client.*;

import java.io.IOException;

public class ConsumerTest1 {

    //队列名称
    private static final String QUEUE_INFORM_TEST1 = "queue_inform_test1";


    public static void main(String[] args){

        Connection connection = null;
        Channel channel = null;

        try {

            
            ConnectionFactory factory = new ConnectionFactory();

            //设置ip
            factory.setHost("localhost");

            //设置端口
            factory.setPort(5672);

            //设置账号密码
            factory.setUsername("guest");  //默认账号密码都是guest
            factory.setPassword("guest");

            //设置虚拟空间
            factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

            //创建与RabbitMQ服务的TCP连接
            connection = factory.newConnection();

            //创建连接通道 ,每个连接可以创建多个通道,每个通道只有一个会话
            channel = connection.createChannel();

          

//            创建默认消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {

//                重写监听方法


                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {


                    System.out.println("receive message.."+new String(body,"utf-8"));
                }
            };


//            监听队列
            /**
             * String queue, boolean autoAck, Consumer callback
             *
             * param1 :  队列名称
             * param2 :   是否自动回复,接收到消息会自动恢复mq收到了,mq会删除消息,如果拒绝的话需要手动回复,不回复的话会导致mq不删除被消费过的消息,一直存在
             * param3 : 消费对象,,包含消费方法
             *
             */
            channel.basicConsume(QUEUE_INFORM_TEST1,true,consumer);
        }catch (Exception e){

        }
    }
}

消费者二 

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;

import com.rabbitmq.client.*;

import java.io.IOException;

public class ConsumerTest2 {

    private static final String QUEUE_INFORM_TEST2 = "queue_inform_test2";

    public static void main(String[] args){

        Connection connection = null;
        Channel channel = null;

        try {



            ConnectionFactory factory = new ConnectionFactory();

            //设置ip
            factory.setHost("localhost");

            //设置端口
            factory.setPort(5672);

            //设置账号密码
            factory.setUsername("guest");  //默认账号密码都是guest
            factory.setPassword("guest");

            //设置虚拟空间
            factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

            //创建与RabbitMQ服务的TCP连接
            connection = factory.newConnection();

            //创建连接通道 ,每个连接可以创建多个通道,每个通道只有一个会话
            channel = connection.createChannel();

           


//            创建默认消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {

//                重写监听方法


                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {


                    System.out.println("receive message.."+new String(body,"utf-8"));
                }
            };


//            监听队列
            /**
             * String queue, boolean autoAck, Consumer callback
             *
             * param1 :  队列名称
             * param2 :   是否自动回复,接收到消息会自动恢复mq收到了,mq会删除消息,如果拒绝的话需要手动回复,不回复的话会导致mq不删除被消费过的消息,一直存在
             * param3 : 消费对象,,包含消费方法
             *
             */
            channel.basicConsume(QUEUE_INFORM_TEST2,true , consumer);
        }catch (Exception e){

        }
    }
}

消费者的代码其实很简单,就是监听而已.只需要指定一下监听的队列就行并提供一个 执行的方法 ,就是下面这句

channel.basicConsume(QUEUE_INFORM_TEST2,true , consumer);

测试

   先启动producer,否则会报错,因为consumer 中没有声明队列,并且没有中rabbitmq中发现队列,就会抛出异常 

   生产者 会给 test1 发送  9条消息   给test 发送 5条消息

  然后我们依次启动俩个消费者 

  然后看处理结果

Topics 工作模式 

通配符工作模式

      每个消费者监听自己的队列,并且设置带统配符的routingkey

      生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。

统配符规则:

                     符号#可以匹配多个词,

                     符号*可以匹配一个词语。

因为通配符感觉比较难讲,所以我在网上找了一个充值的案例

 场景 

        用户充值完成, email 的用户 接收email的提示, sms的用户接收sms的提示.. 设置两种通知类型都接收的则两种通知都有效。

大致思路 

设置 路由匹配规则     

  邮件的匹配规则   "inform.#.email.#"          sms的匹配规则   "inform.#.sms.#"

给只接收email的用户 发送消息  路由设置     inform.email 

给只接收 sm的用户  发送消息 路由设置   inform.sms 

给 都接收的用户 发送消息                 inform.sms.email         不懂的话 看一下匹配规则,,有点取巧来着 

下面是我写的生产者 

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer03 {

    //队列名称
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    public final static String  EXCHANGE_ROUTING_INFORM = "exchange_topic_inform";

    public static void main(String[] args) throws Exception{


        ConnectionFactory factory = new ConnectionFactory();

        //设置ip
        factory.setHost("localhost");

        //设置端口
        factory.setPort(5672);

        //设置账号密码
        factory.setUsername("guest");  //默认账号密码都是guest
        factory.setPassword("guest");

        //设置虚拟空间
        factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();//创建一个通道


        /**
         * 定义交换机
         *  param1 :  交换机名称
         *  param2 :   交换机类型
         *
         */
        channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.TOPIC);

        /**
         *    定义消息队列
         *
         *    String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
         *
         */


        channel.queueDeclare(QUEUE_INFORM_EMAIL,true ,false ,false ,null);
        channel.queueDeclare(QUEUE_INFORM_SMS,true ,false ,false ,null);

        //绑定交换机跟队列
        /*
            String queue, String exchange, String routingKey
         */
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM ,"inform.#.email.#");
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM ,"inform.#.sms.#");


        String message = "";

        //给接收邮件的人发邮件
        for (int i = 0; i < 9; i++) {

//
//            String exchange, String routingKey, BasicProperties props, byte[] body


            message = "记忆最让人崩溃的地方,也许就在于它的猝不及防在某个祥和的午后,你正吃着火锅唱着歌,那些尖利的记忆碎片就像潮水突然涌进你到脑海里,让你闪躲不及。";

            //给test1 发送9条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.email" ,null ,message.getBytes() );
        }

        //给接收短信的人发短信
        for (int i = 0; i < 5; i++) {

//
//            String exchange, String routingKey, BasicProperties props, byte[] body


            message = "发送了一百条短信,九十九条都是你";

            //给test2 发送5条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.sms" ,null ,message.getBytes() );
        }

        
        
//        给都接收的人发送 

        for (int i = 0; i < 3; i++) {
            message = "我回你是秒回,你回我是轮回";

            //给test2 发送5条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.sms.email" ,null ,message.getBytes() );
        }
        

        channel.close();

        connection.close();
    }


}

-----------------------

 定义交换机,并设置交换机的类型为通配符模式

/**
 * 定义交换机
 *  param1 :  交换机名称
 *  param2 :   交换机类型
 *
 */
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.TOPIC);

---------------------------------------------------

绑定交换机跟队列,并配置队列的路由通配符规则

  channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM ,"inform.#.email.#");
  channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM ,"inform.#.sms.#");

--------------------------------------------------------------------------------------------

发送消息给broker 并发送路由key

/*** 参数明细

* 1、交换机名称,不指令使用默认交换机名称 Default Exchang

* 2、routingKey(路由key),根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列

* 3、消息属性

* 4、消息内容*/

 channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.email" ,null ,message.getBytes() );
 channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.sms" ,null ,message.getBytes() );
channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform.sms.email" ,null ,message.getBytes());

消息监听者1

import com.rabbitmq.client.*;

import java.io.IOException;

public class ConsumerTest1 {

    //队列名称
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";

    public static void main(String[] args){

        Connection connection = null;
        Channel channel = null;

        try {


            ConnectionFactory factory = new ConnectionFactory();

            //设置ip
            factory.setHost("localhost");

            //设置端口
            factory.setPort(5672);

            //设置账号密码
            factory.setUsername("guest");  //默认账号密码都是guest
            factory.setPassword("guest");

            //设置虚拟空间
            factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

            //创建与RabbitMQ服务的TCP连接
            connection = factory.newConnection();

            //创建连接通道 ,每个连接可以创建多个通道,每个通道只有一个会话
            channel = connection.createChannel();



//            创建默认消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {

//                重写监听方法


                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {


                    System.out.println("receive message.."+new String(body,"utf-8"));
                }
            };


//            监听队列
            /**
             * String queue, boolean autoAck, Consumer callback
             *
             * param1 :  队列名称
             * param2 :   是否自动回复,接收到消息会自动恢复mq收到了,mq会删除消息,如果拒绝的话需要手动回复,不回复的话会导致mq不删除被消费过的消息,一直存在
             * param3 : 消费对象,,包含消费方法
             *
             */
            channel.basicConsume(QUEUE_INFORM_EMAIL,true,consumer);
        }catch (Exception e){

        }
    }
}

消息监听二

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;

import com.rabbitmq.client.*;

import java.io.IOException;

public class ConsumerTest2 {

    private static final String QUEUE_INFORM_SMS= "queue_inform_sms";

    public static void main(String[] args){

        Connection connection = null;
        Channel channel = null;

        try {



            ConnectionFactory factory = new ConnectionFactory();

            //设置ip
            factory.setHost("localhost");

            //设置端口
            factory.setPort(5672);

            //设置账号密码
            factory.setUsername("guest");  //默认账号密码都是guest
            factory.setPassword("guest");

            //设置虚拟空间
            factory.setVirtualHost("/");//虚拟机默认的虚拟名称为/ , 虚拟机相当于一个独立的服务器

            //创建与RabbitMQ服务的TCP连接
            connection = factory.newConnection();

            //创建连接通道 ,每个连接可以创建多个通道,每个通道只有一个会话
            channel = connection.createChannel();




//            创建默认消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {

//                重写监听方法


                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {


                    System.out.println("receive message.."+new String(body,"utf-8"));
                }
            };


//            监听队列
            /**
             * String queue, boolean autoAck, Consumer callback
             *
             * param1 :  队列名称
             * param2 :   是否自动回复,接收到消息会自动恢复mq收到了,mq会删除消息,如果拒绝的话需要手动回复,不回复的话会导致mq不删除被消费过的消息,一直存在
             * param3 : 消费对象,,包含消费方法
             *
             */
            channel.basicConsume(QUEUE_INFORM_SMS,true , consumer);
        }catch (Exception e){

        }
    }
}

其实监听者代码都不要变来着,都是通用的

测试 

    先启动生产者,后启动监听者.....理由是  如果先启动监听者,队列没创建,就会报错

测试结果

sms的打印台打印结果

email 打印台打印结果

其实搞懂了路由模式后这个就很容易理解这个....

Header模式

header 模式其实跟路由模式很像,他们不同的是header模式取消routingkey,使用header中的 key/value(键值对)匹配队列

了解了很多不同的模式,其实你会发现,代码很多都是相同的....这里我就不粘贴全部代码,就只给不同的代码了 

绑定交换机跟队列的代码

  HashMap<String, Object> header_email = new HashMap<>();
        header_email.put("inform_type", "cms");

        HashMap<String, Object> header_sms = new HashMap<>();
        header_sms.put("inform_type", "sms");

        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM ,"",header_email);
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM ,"",header_sms);

发送消息的代码


            message = "记忆最让人崩溃的地方,也许就在于它的猝不及防在某个祥和的午后,你正吃着火锅唱着歌,那些尖利的记忆碎片就像潮水突然涌进你到脑海里,让你闪躲不及。";


            HashMap<String, Object> header = new HashMap<>();

            header.put("inform_type", "sms");  //匹配sms通知消费者绑定的header


            AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties.Builder();

            properties.headers(header)
            //给test1 发送9条消息
            channel.basicPublish(EXCHANGE_ROUTING_INFORM,"" ,properties.build() ,message.getBytes() );

消费者都是一样的,改一下队列名称就行

猜你喜欢

转载自blog.csdn.net/qq_40794266/article/details/84613571