RabbitMQ 几种模式

普通模式

一个生产者,一个交换机,一个队列,一个消费者.
在这里插入图片描述
生产者

public class Send {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 初始化
        ConnectionFactory factory = new ConnectionFactory();
        
        // 这里需要设置主机名,端口,密码等,这里使用的工厂默认的配置
        
        Connection connection = factory.newConnection();// 获取连接
        Channel channel = connection.createChannel();// 获取通道
        //声明队列
     /** 队列声明的参数
     * @param queue the name of the queue
     * @param durable true if we are declaring a durable queue (the queue will survive a server restart)
     * @param exclusive true if we are declaring an exclusive queue (restricted to this connection)
     * @param autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use)
     * @param arguments other properties (construction arguments) for the queue
     */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //定义消息
        String message = "hello world";
        // 发送消息
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        // 断开连接
        channel.close();
        connection.close();
    }
}

消费者:

public class Recv {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
		
		//使用的是工厂的默认参数(主机名,端口,密码.....)
		
        Connection conection = factory.newConnection();
        Channel channel = conection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //创建消费者,在回调函数中处理结果
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("从队列"+QUEUE_NAME+"接受到:" + message);
            }
        };
        /**
     * @param queue the name of the queue
     * @param autoAck true if the server should consider messages
     * acknowledged once delivered; false if the server should expect
     * explicit acknowledgements.
     * 是否需要自动签收消息,如果是true的话,一旦接收到消息,就返回确认签收,不会管回调
     * 函数的执行. fasle的话为手动签收,等到回调函数逻辑处理完,在手动返回确认签收.
     * 自动确认签收,可能会造成消息丢失.
     * @param callback an interface to the consumer object
     */
     channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

自动确认签收(ack),消息丢失问题.

上面函数 basicConsume(),的autoAck 设置为true的话,是一旦接收到消息,就返回确认签收,如果回调函数出现异常,消息也会从队列中消失,但是这个消息(指令)并没有被执行,这就造成了消息丢失.

解决办法: 将签收方式(autoAck)改为false,手动签收.在回调函数中,用通道(channel)对象调用下面的方法,返回签收(ack).

    /**
     * Acknowledge one or several received
     * messages. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
     * or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method
     * containing the received message being acknowledged.
     * @see com.rabbitmq.client.AMQP.Basic.Ack
     * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
     * @param multiple true to acknowledge all messages up to and
     * including the supplied delivery tag; false to acknowledge just
     * the supplied delivery tag.
     * @throws java.io.IOException if an error is encountered
     */
    void basicAck(long deliveryTag, boolean multiple) throws IOException;

work queue 模式

在这里插入图片描述
一个生产者,多个消费者,一个消息只会被分发到一个消费者.

work queue 模式在代码上没有任何区别,只不过多了一个(或多个)消费端.

生产者通过通道,声明队列,发送消息的时候,指定交换机.
消费者通过通道声明队列,通过通道指定队列名,拿消息.

work queue 问题

因为work queue由多个消费端,把消息发送给谁就是一个问题. RabbitMQ在此模式下,默认使用的是轮询方式. 但这就会带来一个问题, 不同的微服务性能不同, 性能差的在轮询模式下,可能会堆积很多的消息, 所以需要设置 一个消费者, 在同一时间只处理一个消息.

在channel对象 中设置

channel.basicQos(1); //同一时间只处理一个消息

以下是该方法源码

    /**
     * Request a specific prefetchCount "quality of service" settings
     * for this channel.
     *
     * @see #basicQos(int, int, boolean)
     * @param prefetchCount maximum number of messages that the server
     * will deliver, 0 if unlimited
     * @throws java.io.IOException if an error is encountered
     */
    void basicQos(int prefetchCount) throws IOException;

发布/订阅模式(生产者声明交换机,消费者声明队列,绑定到生产者声明的交换机)

前两种模式,共用的一个队列,一个消息只能被一个消费者消费, 发布/订阅模式就是有多个队列,可以同时向多个队列发布消息,被多个消费者消费.
在这里插入图片描述

交换机 fanout模式

生产者通过channel声明交换机(只做声明交换机)

 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
//      使用默认的工厂配置,通过连接工厂拿到连接对象
        Connection connection = connectionFactory.newConnection();
//      通过连接对象拿到通道
        Channel channel = connection.createChannel();
//      通过通道声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//       通过通道发布消息
//       rountingKey 为null
        channel.basicPublish(EXCHANGE_NAME, "", null, "55开牛逼".getBytes("utf-8"));
//        关闭连接对象
        channel.close();
        connection.close();
    }

消费者声明队列,将队列绑定到交换机,rountingKey为nul
消费者1:

public class Receive2 {

    public static final String QUEUE_NAME = "receivor_2";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
//        声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        将队列绑定到交换机,fanout模式不需要指定routingKey
        channel.queueBind(QUEUE_NAME, Send.EXCHANGE_NAME, "");
        Consumer callback = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumerTag" + consumerTag);
                System.out.println("接收到的消息" + new String(body, StandardCharsets.UTF_8));
            }
        };
//        消费消息
        channel.basicConsume(QUEUE_NAME, true,callback);
    }
}

消费者2:
代码与消费者1相同,只不过队列名不同, 这样就是两个队列绑定到一个交换机, 消息就会同时发布到两个队列,两个消费者就会同时接收到.

如果两个两个消费者绑定到同一个队列,就是work queue模式了…

交换机 Direct模式

direct模式和fanout模式差不多, direct模式不在把routingKey设置为 “” ,生产者把消息用通道给到指定的交换机, 并指定routingKey, 只有拥有指定的routingKey的消费者才会接收到消息.

将fanout 模式更改一下

生产者:

//      通过通道声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//       通过通道发布消息,发布到routingKey为news的队列
//       这里的交换机名字要改变一下, 因为再次名字下的交换机已经再fanout模式下声明,直接变到Direct会报错.
        channel.basicPublish(EXCHANGE_NAME, "news", null, "55开牛逼".getBytes("utf-8"));

消费者一:

//        将队列绑定到交换机,设置routingKey为 news,可以绑定多个routingkey
        channel.queueBind(QUEUE_NAME, Send.EXCHANGE_NAME, "news");

消费者二:

//        将队列绑定到交换机,设置routingKey为 news,可以绑定多个routingkey
        channel.queueBind(QUEUE_NAME, Send.EXCHANGE_NAME, "music");

消息只会发布到绑定到交换机routingKey为news的队列.

交换机topic模式

topic 模式是Direct的升级版本,生产者发布和消费者绑定的routingKey可以使用通配符.

‘#’ : 井号代表多个或者一个单词.
‘*’: ✳代表是一个单词.

修改一下Direct模式的代码:

生产者:

        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
//       通过通道发布消息,EXCHANGE_NAME的值需要哦改一下
        channel.basicPublish(EXCHANGE_NAME, "system.music", null, "55开牛逼".getBytes("utf-8"));

消费者一:

//        将队列绑定到交换机,routingKey为system.*
        channel.queueBind(QUEUE_NAME, Send.EXCHANGE_NAME, "system.*");

消费者二:

//        将队列绑定到交换机,routingKey为system.music
        channel.queueBind(QUEUE_NAME, Send.EXCHANGE_NAME, "system.music");

两个消费者都会收到生产者的消息.

持久化

交换机,队列,消息,都可以做持久化, 只需要在声明交换机/队列时将durable设置为true就可以了.

消息持久化
在这里插入图片描述

总结

在普通模式和work queue 模式 生产者通过channel声明队列, 通过默认的交换机发布消息

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //定义消息
        String message = "hello world";
        // 发送消息
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

消费者端也声明相同的队列,

 channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 channel.basicConsume(QUEUE_NAME, true, consumer);

在订阅发布模式,生产者只声明交换机, 消费者只声明队列, 把队列绑定到交换机.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值