• RabbitMQ------四种模式


    一. 工作队列模式

    1. 消息生产能力大于消费能力,增加多几个消费节点

    2. 默认策略:轮训(round robin)

    缺点:存在部分节点消费过快,部分节点消费过慢,导致不能合理处理消息
    
    例子:生产者发送了10条消息,有2个消费者,则每个消费者都处理5条消息

    3. 公平策略

    优点:解决消费者消费能力不足问题,降低消费时间

    例子:能者多劳,消费者处理完当前的1条消息后,再处理下1条。生产者发送了6(a~f)条消息
    
    时间/s             1           2           3           4           5
    
    消费者1            a           c           d                        f
    
    消费者2            b                                     e

    如图:

    p:生产者      红色:队列      c:消费者

    代码:

    1.添加pom.xml依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    View Code

    2.添加消息生产者代码

    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    class sender{
        private static final String QUEUE_Name = "hello";
    
        public static void main(String[] args){
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            try (Connection connection = factory.newConnection();
                 Channel channel = connection.createChannel()){
                channel.queueDeclare(QUEUE_Name, false, false, false, null);
                //一次发送10条消息
                for(Integer i = 0; i<10; i++){
                    String msg = "hi hutao " + i;
                    System.out.println(msg);
                    channel.basicPublish("", QUEUE_Name, null, msg.getBytes("utf-8"));
                }
            }
            catch (Exception e){
    
            }
        }
    }
    View Code

    3.添加消息消费者代码

    公平策略代码:1表示消费者每次处理完1条消息再从生产者处获取消息

    channel.basicQos(1);

    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    /**
     * 可以拷贝整个class代码模拟多个消费者接收消息
     * 再通过修改TimeUnit.SECONDS.sleep模拟消费者处理消息速度
     *
     * */
    class receiver {
        private static final String QUEUE_Name = "hello";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
            channel.queueDeclare(QUEUE_Name, false, false, false, null);
    
            //公平策略,能让处理消息快的消费者处理更多消息
            //限制消费者每次消费1个,处理完再消费下一个
            channel.basicQos(1);
    
    //                官网的测试方法
    //                DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    //                String message = new String(delivery.getBody(), "UTF-8");
    //                System.out.println(" [x] Received '" + message + "'");
    //            };
            //自定义的测试方法
            Consumer consumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    //模拟消费者处理速度
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    }
                    catch (Exception e){}
    
                    String msg = new String(body, "utf-8");
                    System.out.println(msg);
                    //手工确认消息 不是多条确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //消费 多个消费者时要关闭消息自动确认
            channel.basicConsume(QUEUE_Name, false, consumer);
        }
    }
    View Code

    二. 发布/订阅模式

    1. 生产者将消息发送到交换机(Exchange),再由交换机转发到队列

    2. 交换机只负责转发消息,不具备存储消息能力,如果没有队列和交换机绑定,或者没有符合的路由规则,则消息会被丢失

    3. 交换机常见四种类型:

    备注:路由键(RoutingKey)

    Direct Exchange(定向)

    a. 将一个队列绑定交换机,要求该消息与一个特定的路由键完全匹配

    b. 处理路由键

    例子:
    
    一个队列绑定交换机的路由键是aaa.bb,则只有被标记为aaa.bb的消息才会被转发,标记为a.bb,aaa.b的都不会被转发

    Fanout Exchange(广播)

    a. 只需要将队列绑定交换机,则发送到交换机上的消息都会被转发到与之绑定的队列中
    
    b. 速度是最快的
    
    c. 不处理路由键

    Topic Exchange(通配符)

    a. topic交换机是一种发布/订阅模式,结合了direct交换机和fanout交换机的特点
    
    b. 将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上
    
    c. 符号“#”匹配一个或者多个词,“*”只匹配一个词
    
    例子:
    
    abc.#可以匹配到abc.aa,abc.ef,abc.ccc.dd
    
    abc.*可以匹配到abc.aa,abc.ef

    Headers Exchange(基本不用)

    如图:

    4.

    a. 发布/订阅模式中消息生产者不再直接面对队列(queque),而是面对交换机(exchange),消息都需要经过交换机进行发送,
    
    b. 所有发往同一个fanout交换机的消息都会被所有监听这个交换机的消费者接收到
    
    c. 应用场景
    
    微信公众号,新浪微博关注等等

    如图:

    代码:

    1.添加生产者代码

    import com.rabbitmq.client.BuiltinExchangeType;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.nio.charset.StandardCharsets;
    
    public class sender {
        private static final String EXCHANGE_NAME = "exchange_fanout";
    
        public static void main(String[] args){
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            //创建连接
            //jdk7之后版本会自动关闭
            try (Connection connection = factory.newConnection();
                 //创建信道
                 Channel channel = connection.createChannel()){
                //绑定交换机,fanout扇形,即广播模式
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
                String msg = "测试广播模式";
                channel. basicPublish(EXCHANGE_NAME, "", null, msg.getBytes(StandardCharsets.UTF_8));
            }
            catch (Exception e){
    
            }
        }
    }
    View Code

    2.添加消费者代码

    import com.rabbitmq.client.*;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.concurrent.TimeoutException;
    
    /**
     * 可以拷贝多个此类来模拟多个消费者
     *
     * */
    public class recvier {
        //交换机名称
        private static final String EXCHANGE_NAME = "exchange_fanout";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            //配置连接rabbitmq
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("12345");
            factory.setVirtualHost("/dev");
    
            //创建连接和信道
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
    
            //绑定交换机,fanout扇形,即广播模式
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
            //获取队列,会自动分配队列
            //可以在rabbitmq管理界面 http://localhost:15672/
            // Queues -> Overview列表 -> Name中查看
            String queueName = channel.queueDeclare().getQueue();
            //绑定交换机和队列 fanout模式不需要routingKey
            channel.queueBind(queueName, EXCHANGE_NAME, "");
    
            Consumer consumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String msg = new String(body, StandardCharsets.UTF_8);
                    //控制台打印发布者传过来的消息
                    System.out.println(msg);
                    //手工确认消息 不是多条确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //消费 多个消费者时要关闭消息自动确认
            channel.basicConsume(queueName, false, consumer);
        }
    }
    View Code

    三. 路由模式

    1. 交换机类型是Direct

    2. 队列和交换机绑定,需要指定路由键(RoutingKey)

    3. 生产者发送消息给交换机,需要指定路由键

    4. 交换机根据路由键,将消息发送给指定的队列

    如图:

     代码:

    1.添加生产者代码

    import com.rabbitmq.client.BuiltinExchangeType;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.nio.charset.StandardCharsets;
    
    public class sender {
        private static final String EXCHANGE_NAME = "exchange_direct";
    
        public static void main(String[] args){
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            //创建连接
            //jdk7之后版本会自动关闭
            try (Connection connection = factory.newConnection();
                 //创建信道
                 Channel channel = connection.createChannel()){
                //绑定交换机,直连交换机 direct
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
                String msgError = "错误信息";
                String msgInfo = "提示信息";
                String msgDebug = "调试信息";
                //路由模式需要指定routingKey
                channel. basicPublish(EXCHANGE_NAME, "errorRoutingKey", null, msgError.getBytes(StandardCharsets.UTF_8));
                channel. basicPublish(EXCHANGE_NAME, "infoRoutingKey", null, msgInfo.getBytes(StandardCharsets.UTF_8));
                channel. basicPublish(EXCHANGE_NAME, "debugRoutingKey", null, msgDebug.getBytes(StandardCharsets.UTF_8));
            }
            catch (Exception e){
    
            }
        }
    }
    View Code

    2.添加消费者代码

    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.concurrent.TimeoutException;
    
    /**
     * 可以拷贝多个此类来模拟只接收info消息的消费者
     * 绑定交换机和队列时,不绑定errorRoutingKey和debugRoutingKey即可
     *
     * */
    public class recvier {
        //交换机名称
        private static final String EXCHANGE_NAME = "exchange_direct";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            //配置连接rabbitmq
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            //创建连接和信道
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
    
            //绑定交换机,直连交换机(direct)
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            //获取队列,会自动分配队列
            //可以在rabbitmq管理界面 http://localhost:15672/
            // Queues -> Overview列表 -> Name中查看
            String queueName = channel.queueDeclare().getQueue();
            //绑定交换机和队列 直连模式direct需要指定routingKey
            channel.queueBind(queueName, EXCHANGE_NAME, "errorRoutingKey");
            channel.queueBind(queueName, EXCHANGE_NAME, "infoRoutingKey");
            channel.queueBind(queueName, EXCHANGE_NAME, "debugRoutingKey");
    
            Consumer consumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String msg = new String(body, StandardCharsets.UTF_8);
                    //控制台打印发布者传过来的消息
                    System.out.println(msg);
                    //手工确认消息 不是多条确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //消费 多个消费者时要关闭消息自动确认
            channel.basicConsume(queueName, false, consumer);
        }
    }
    View Code

    四. 主题模式

    1. 交换机是topic

    2. 可以实现发布订阅模式(fanout)和路由模式(direct)功能,更加灵活,支持模式匹配,通配符等

    3. 交换机通过通配符转发对对应队列

    #代表一个或者多个词

    *代表一个词

    例子:

    order.#会匹配order.log,order.log.info等等

    order.*会匹配order.log,order.new等等

    4.

    生产者发送消息需要指定具体路由键

    交换机和队列绑定使用通配符路由键

    如图:

     代码:

    1.添加生产者代码

    import com.rabbitmq.client.BuiltinExchangeType;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.nio.charset.StandardCharsets;
    
    public class sender {
        private static final String EXCHANGE_NAME = "exchange_topic";
    
        public static void main(String[] args){
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            //创建连接
            //jdk7之后版本会自动关闭
            try (Connection connection = factory.newConnection();
                 //创建信道
                 Channel channel = connection.createChannel()){
                //绑定交换机,topic交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
                String msgError = "订单错误信息";
                String msgInfo = "订单提示信息";
                String msgDebug = "商品调试信息";
                //topic模式需要指定routingKey
                channel. basicPublish(EXCHANGE_NAME, "order.log.error", null, msgError.getBytes(StandardCharsets.UTF_8));
                channel. basicPublish(EXCHANGE_NAME, "order.log.info", null, msgInfo.getBytes(StandardCharsets.UTF_8));
                channel. basicPublish(EXCHANGE_NAME, "product.log.debug", null, msgDebug.getBytes(StandardCharsets.UTF_8));
            }
            catch (Exception e){
    
            }
        }
    }
    View Code

    2.添加消费者代码

    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.concurrent.TimeoutException;
    
    /**
     * 可以拷贝多个此类来模拟只接收订单日志错误信息的消费者
     * 绑定交换机和队列时,只要将routingKey改为order.log.error即可
     *
     * */
    public class recvier {
        //交换机名称
        private static final String EXCHANGE_NAME = "exchange_topic";
    
        public static void main(String[] args) throws IOException, TimeoutException {
            //配置连接rabbitmq
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            factory.setUsername("guest");
            factory.setPassword("123456");
            factory.setVirtualHost("/dev");
    
            //创建连接和信道
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
    
            //绑定交换机,topic交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
            //获取队列,会自动分配队列
            //可以在rabbitmq管理界面 http://localhost:15672/
            // Queues -> Overview列表 -> Name中查看
            String queueName = channel.queueDeclare().getQueue();
            //绑定交换机和队列 topic模式需要指定routingKey
            //这里获取所有匹配*log.*的日志信息
            channel.queueBind(queueName, EXCHANGE_NAME, "*.log.*");
    
            Consumer consumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String msg = new String(body, StandardCharsets.UTF_8);
                    //控制台打印发布者传过来的消息
                    System.out.println(msg);
                    //手工确认消息 不是多条确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //消费 多个消费者时要关闭消息自动确认
            channel.basicConsume(queueName, false, consumer);
        }
    }
    View Code
  • 相关阅读:
    LeetCode Longest Uncommon Subsequence II
    Leetcode Longest Uncommon Subsequence I
    LeetCode 557. Reverse Words in a String III
    LeetCode 394. Decode String
    LeetCode 419. Battleships in a Board
    LeetCode 366. Find Leaves of Binary Tree
    LeetCode 408. Valid Word Abbreviation
    LeetCode 532. K-diff Pairs in an Array
    LeetCode Minimum Absolute Difference in BST
    LeetCode 414. Third Maximum Number
  • 原文地址:https://www.cnblogs.com/tianhengblogs/p/15337433.html
Copyright © 2020-2023  润新知