Getting Started with RabbitMQ

Introduction to RabbitMQ

1.MQ is Message Queue, which is a communication method between applications and applications.

2.RabbitMQ is an open source, complete and reusable enterprise messaging system based on AMQP.

3. Support mainstream operating systems, Linux, Windows, MacOX, etc.

4. Multiple development language support, Java, Python, Ruby, .NET, PHP, C/C++, node.js, etc.

 

Official website: http://www.rabbitmq.com/

 

First, the simple queue of the queue:



 

P: the producer of the message

C: the consumer of the message

red: queue

 

Producers send messages to queues and consumers get messages from queues.

 

<dependency>
	<groupId>com.rabbitmq</groupId>
	<artifactId>amqp-client</artifactId>
	<version>3.4.1</version>
</dependency>
public class ConnectionUtil {
    public static Connection getConnection() throws Exception {
        //define the connection factory
        ConnectionFactory factory = new ConnectionFactory();
        //Set the service address
        factory.setHost("localhost");
        //port
        factory.setPort(5672);
        //Set account information, username, password, vhost
        factory.setVirtualHost("/mytest");
        factory.setUsername("mytest");
        factory.setPassword("mytest");
        // Get the connection through the project
        Connection connection = factory.newConnection();
        return connection;
    }
}

   

Producer sends message to queue

public class Send {
    private final static String QUEUE_NAME = "test_queue";
    public static void main(String[] argv) throws Exception {
        // Get the connection and the mq channel
        Connection connection = ConnectionUtil.getConnection();
        // create the channel from the connection
        Channel channel = connection.createChannel();
        // declare (create) the queue
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // Message content
        String message = "Hello World!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");
        //Close the channel and connection
        channel.close();
        connection.close();
    }
}

 

Consumers get messages from the queue

public class Recv {
    private final static String QUEUE_NAME = "test_queue";
    public static void main(String[] argv) throws Exception {
        // Get the connection and the mq channel
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // declare the queue
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // define the consumer of the queue
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // listen to the queue
        channel.basicConsume(QUEUE_NAME, true, consumer);
        // get message
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
        }
    }
}

 

2. Those who can work in the Work mode of the queue work hard:

 

 

One producer, two consumers.

A message can only be fetched by one consumer.

 

 Consumer 1

 

public class Recv {

    private final static String QUEUE_NAME = "test_queue_work";

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

        // Get the connection and the mq channel
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // declare the queue
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // The server will only send one message to the consumer at the same time
        channel.basicQos(1);

        // define the consumer of the queue
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // Listen to the queue and manually return to complete
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // get message
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            //休眠
            Thread.sleep(10);
            // 返回确认状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

  

 

 消费者2

public class Recv2 {

    private final static String QUEUE_NAME = "test_queue_work";

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

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成状态
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            // 休眠1秒
            Thread.sleep(1000);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

 生产者

  向队列中发送100条消息。

 

public class Send {

    private final static String QUEUE_NAME = "test_queue_work";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        for (int i = 0; i < 100; i++) {
            // 消息内容
            String message = "" + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println(" [x] Sent '" + message + "'");

            Thread.sleep(i * 10);
        }

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

 

能者多劳,实现公平调度。

实现公平调度的方式就是让每个消费者在同一时刻会分配一个任务。 通过channel.basicQos(1);可以设置

// 同一时刻服务器只会发一条消息给消费者
 channel.basicQos(1);

 

消息的确认模式

消费者从队列中获取消息,服务端如何知道消息已经被消费呢?

 

模式1:自动确认

只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都认为是消息已经成功消费。

模式2:手动确认

消费者从队列中获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态。

 

为了确保消息一定被消费者处理,rabbitMQ提供了消息确认功能,就是在消费者处理完任务之后,就给服务器一个回馈,服务器就会将该消息删除,

如果消费者超时不回馈,那么服务器将就将该消息重新发送给其他消费者。

 

 

手动模式:

public class Recv {
    private final static String QUEUE_NAME = "test_queue_work";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        boolean autoAck = false;//true : 自动确认  false:手动确认
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            //休眠
            Thread.sleep(10);
            // 返回确认状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 自动模式:

  代码片段

 // 监听队列,手动返回完成
        boolean autoAck = true;//true : 自动确认  false:手动确认
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
        }

 

、队列之订阅模式:



 

 解读:

1、1个生产者,多个消费者

2、每一个消费者都有自己的一个队列

3、生产者没有将消息直接发送到队列,而是发送到了交换机

4、每个队列都要绑定到交换机

5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的

 

RabbitMQ 三种Exchange:

Fanout Exchange

Direct Exchange

Topic Exchange

 


 
 

 消息的生产者:

public class Send {

    private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

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

 注意:消息发送到没有队列绑定的交换机时,消息将会丢失,因为,交换机没有存储消息的能力,消息只能存在在队列中。

 

消费者1:

 

public class Recv {

    private final static String QUEUE_NAME = "test_queue_work";
    private final static String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

消费者2:

 

public class Recv2 {

    private final static String QUEUE_NAME = "test_queue_work2";
    private final static String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 测试结果:

  同一个消息被多个消费者获取。

 

 

、队列之路由模式:


 

 

1. 消息传递时需要一个“RouteKey”,可以简单的理解为要发送到的队列名字。任何发送到Direct Exchange的消息都会被转发到RouteKey中指定的Queue。

2.如果vhost中不存在RouteKey中指定的队列名,则该消息会被抛弃。

 

图示:



 

生产者:

 

public class Send {

    private final static String EXCHANGE_NAME = "test_exchange_direct";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "key2", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

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

 

消费者1:

 

public class Recv {
    private final static String QUEUE_NAME = "test_queue_work";
    private final static String EXCHANGE_NAME = "test_exchange_direct";
    public static void main(String[] argv) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key");
        // The server will only send one message to the consumer at the same time
        channel.basicQos(1);
        // define the consumer of the queue
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // Listen to the queue and manually return to complete
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // get message
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}
 Consumer 2:

 

public class Recv2 {
    private final static String QUEUE_NAME = "test_queue_work2";
    private final static String EXCHANGE_NAME = "test_exchange_direct";
    public static void main(String[] argv) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key2");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

、队列之通配符模式:


 
 

 

Topic Exchange :将路由键和某模式进行匹配。此时队列需要绑定到一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。

 

因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*”只会匹配到“audit.irs”

 

 

图示:


 

任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上

1.这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一个“标题”(RouteKey),Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列。

2.这种模式需要RouteKey,也许要提前绑定Exchange与Queue。

3.在进行绑定时,要提供一个该队列关心的主题,如“#.log.#”表示该队列关心所有涉及log的消息(一个RouteKey为”MQ.log.error”的消息会被转发到该队列)。

4.“#”表示0个或若干个关键字,“*”表示一个关键字。如“log.*”能与“log.warn”匹配,无法与“log.warn.timeout”匹配;但是“log.#”能与上述两者匹配。

5.同样,如果Exchange没有发现能够与RouteKey匹配的Queue,则会抛弃此消息。

 

 

生产者:

public class Send {
    private final static String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "item.insert", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

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

 

 消费者1:

public class Recv {
    private final static String QUEUE_NAME = "test_queue_topic_work";
    private final static String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] argv) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        //channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.update");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.delete");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

 

消费者2:

public class Recv2 {
    private final static String QUEUE_NAME = "test_queue_topic_work2";
    private final static String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] argv) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        //channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "*.*");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.#");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326636806&siteId=291194637