RabbitMq创建生产者和消费者例子

 

生产者

package com.ly.liyong.rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class PublisherController {
    private static final String EXCHANGE_NAME = "exchange_ly_demo";
    private static final String ROUTING_KEY = "routing_ly_demo";
    private static final String QUEUE_NAME = "queue_ly_demo";
    private static final String IP_ADDRESS = "47.105.121.99";
    private static final int PORT = 5672;

    public static void main(String[] args) throws IOException,
            TimeoutException, InterruptedException {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername("lpadmin");
        factory.setPassword("lpadmin");
        //创建连接
        Connection connection = factory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //direct模式的持久化、非自动删除的交换器
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
        //持久化、非排他的、非自动删除的队列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //将交换器与队列通过路由键绑定
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

        for (int i = 1; i < 5; i++) {
            //发送一条持久的消息
            String msg = "大帅哥,你好!" + i;
            byte[] msgByte = msg.getBytes();
            System.out.println("send: " + msg);
//            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, msgByte);

            //设置消息相关属性
            //delivery_mode设置为2,即消息会被持久化(即存入磁盘)在服务器中
            //priority设置这条消息的优先级为0
//            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
//                    new AMQP.BasicProperties.Builder()
//                            .contentType("text/plain")
//                            .deliveryMode(2)
//                            .priority(1)
//                            .build(), msgByte);

            //发送带有headers的消息,并设置消息过期时间为10s
            Map headers = new HashMap();
            headers.put("localtion", "here");
            headers.put("time", "tody");
            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
                    new AMQP.BasicProperties.Builder()
                            .headers(headers)
                            .expiration("10000")
                            .build(), msgByte);
        }
        //关闭资源
        channel.close();
        connection.close();
    }
}

 

basicPublish()中特殊参数讲解:

mandatory:参数为true时,交换器无法找到对应的队列时,RabbitMq会调用Basic.Return命令将消息返回给生产者(客户端可通过ReturnListener监听这个事件)。为false时,消息直接被丢弃。(当备份交换器一起使用的时候,此参数设置失效)

immediate:参数为true时,如果交换器在将消息路由到队列上并不存在任何消费者,那么这条消息将不会存入队列中。当路由键匹配的所有队列都么有消费者时,该消息会通过Basic.Return返回至生产者(RabbitMq3.0版本开始去掉了对immediate参数的支持,原因是此参数会影响镜像队列的性能,增加了代码复杂性,建议采用TTL和DLX的方法替代)

 

TTL:

1.队列过期时间和消息过期时间

2.以上两个同时使用时,则消息的过期时间以两者之间较小的那个数值为准

3.如果将消息的过期时间设置为0,则表示除非此时可以直接将消息投递到消费者,否则改消息会被立即丢弃(此属性可以代替immediate参数)

4.队列的过期删除不能保证很及时。在RabbitMq重启后,持久化的队列的过期时间会被重新计算切TTL不能设置为0

 

消费者(推模式)

package com.ly.liyong.rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ConsumerController {
    private static final String QUEUE_NAME="queue_ly_demo";
    private static final String IP_ADDRESS="47.105.121.99";
    private static final int PORT=5672;

    public static void main(String[] args) throws IOException,
            TimeoutException, InterruptedException {
        Address[] addresses = new Address[]{
                new Address(IP_ADDRESS, PORT)
        };
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("lpadmin");
        factory.setPassword("lpadmin");
        //创建连接
        Connection connection = factory.newConnection(addresses);
        //创建信道
        final Channel channel = connection.createChannel(50);
        //设置客户端最多接收未被ack的消息的个数
        channel.basicQos(64);
        //推模式
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                throws IOException {
                System.out.println("推模式: " + new String(body));
                //1s后消费
                try{
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //手动ack确认消费
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //消费模式
        channel.basicConsume(QUEUE_NAME, consumer);

        //拉模式(每次只拉取一条消息)
//        GetResponse response = channel.basicGet(QUEUE_NAME, false);
//        System.out.println("拉模式: " + new String(response.getBody()));
//        channel.basicAck(response.getEnvelope().getDeliveryTag(), false);

        //请求Broker重新发送未被确认的消息(1次)
//        channel.basicRecover();
        //等待回调函数执行完毕之后,关闭资源
//        TimeUnit.SECONDS.sleep(5);
//        channel.close();
//        connection.close();
    }
}

basicConsume()

queue:队列名

autoAck:设置是否自动确认。建议设置false,即不自动确认

consumerTag:消费者标签,用来区分多个消费者

noLocal:设置为true则表示不能将同一个Connection中生产者发送的消息传递给这个Connection中的消费者

exclusive:是否排他

arguments:设置消费者的其他参数

callback:设置消费者的回调函数。用来处理RabbitMq推送过来的消息,比如DefaultConsumer,使用时需要客户端重写(override)其中的方法。

 

注意:

1.和生产者一样,消费者客户端同样需要考虑线程安全的问题。消费者客户端的这些callback会被分配到Channel不同的线程池上,这意味着消费者客户端可以安全第调用这些阻塞方法,比如:channel.queueDeclare()、channel.basicCancel()等

2.每个Channel都拥有自己独立的线程。最常用的做法是一个Channel对应一个消费者,也就是意味着消费者彼此之间没有任何关联。当然也可以在一个Channel钟维持多个消费者,但是要注意一个问题,如果Channel中一个消费者一直运行,那么其他消费者的callback会被“耽搁”。

3.在投递(推)模式期间,RabbitMq会不断地推送消息给消费者,当然推送消息的个数还是会收到Basic.Qos的限制。

4.如果只想从队列获得单条消息而不是持续订阅,建议还是使用Basic.Get进行消费。

 

你可能感兴趣的:(rabbitMQ,java)