RabbitMQ: starting from scratch

1. Introduction

RabbitMQ is a message middleware based on the AMQP protocol. The server side is written in Erlang language and supports a variety of clients. It is used to store and forward messages in a distributed system. It performs well in terms of ease of use, scalability, and high availability.

2. Installation

Go to the official website to download the corresponding version. Before installing rabbitmq, you need to install the corresponding Erlang environment. After the installation is complete, use the command /sbin/service rabbitmq-server start to start. 
http://www.rabbitmq.com/download.html

3. Basic configuration

  1. User permission configuration: The default guest/guest user of rabbitmq can only be accessed on the local machine. If you need access to external network users, you need to create a user separately, create users and grant permissions through commands, through commands: ./rabbitmqctl add_user test test , ./ rabbitmqctl set_user_tags test administrator adds the test user and grants admin privileges. Note: The user created by the command line needs to set permissions in the user management of the next web monitoring page (Set permisson in the figure below), otherwise the connection cannot be successful. 
  2. WEB monitoring configuration: Open the web monitoring page display through the ./rabbitmq-plugins enable rabbitmq_management command. The default port is 15672. You can view the web monitoring page through localhost:15672, which is convenient for later management and viewing of queue messages.

4. Java Demo

Message provider Provider:

package org.rabbitmq.RabbitMq;

import java.io.IOException;

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

/**
 * java访问mq基础demo --provider
 * @author Mr.tanzc
 *
 */
public class MySpreadSend {

     //发送消息
     public static void main(String[] args) throws IOException {
        /*使用工厂类建立Connection和Channel,并且设置参数*/
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");//MQ的IP
        factory.setPort(5672);//MQ端口
        factory.setUsername("test");//MQ用户名
        factory.setPassword("test");//MQ密码
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        /*定义交换机*/
        channel.exchangeDeclare("COLOR_EXCHANGE", "direct");

        /*创建多个消息队列*/
        channel.queueDeclare("BLACK_QUEUE", false, false, false, null);
        channel.queueDeclare("RED_QUEUE", false, false, false, null);

        /*绑定交换机和队列*/
        channel.queueBind("BLACK_QUEUE", "COLOR_EXCHANGE", "black");
        channel.queueBind("RED_QUEUE", "COLOR_EXCHANGE", "red");

        /*通过交换机发送不同类别的消息到不同的队列中,注意,消息是由一个交换机来根据标志发往不同的队列中去*/
        String message = "black";
        channel.basicPublish("COLOR_EXCHANGE", "black", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        message="red";
        channel.basicPublish("COLOR_EXCHANGE", "red", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        /*关闭连接*/
        channel.close();
        connection.close();
          }

}

Define two consumers Comsumer: 
the first consumer MySpreadRecvRed

package org.rabbitmq.RabbitMq;

import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;

public class MySpreadRecvRed {

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        /*建立连接*/
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");//MQ的IP
        factory.setPort(5672);//MQ端口
        factory.setUsername("test");//MQ用户名
        factory.setPassword("test");//MQ密码
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        /*声明要连接的队列*/
        /*定义交换机*/
        channel.exchangeDeclare("COLOR_EXCHANGE", "direct");
        /*绑定交换机和队列*/
        channel.queueBind("RED_QUEUE", "COLOR_EXCHANGE", "red");
        /*创建消费者对象,用于读取消息*/
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume("RED_QUEUE", true, consumer);
        /* 读取队列,并且阻塞,即在读到消息之前在这里阻塞,直到等到消息,完成消息的阅读后,继续阻塞循环*/
        while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");
        }
        }
}

Second consumer MySpreadRecvBlack:

package org.rabbitmq.RabbitMq;

import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;

/**
 * 获取指定队列的消息
 * @author Mr.tanzc
 *
 */
public class MySpreadRecvBlack {

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        /*建立连接*/
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.1.25");//MQ的IP
        factory.setPort(5672);//MQ端口
        factory.setUsername("twk");//MQ用户名
        factory.setPassword("twk");//MQ密码
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        /*声明要连接的队列*/
        /*定义交换机*/
        channel.exchangeDeclare("COLOR_EXCHANGE", "direct");
        /*绑定交换机和队列*/
        channel.queueBind("BLACK_QUEUE", "COLOR_EXCHANGE", "black");        
        /*创建消费者对象,用于读取消息*/
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume("BLACK_QUEUE", true, consumer);
        /* 读取队列,并且阻塞,即在读到消息之前在这里阻塞,直到等到消息,完成消息的阅读后,继续阻塞循环*/
        while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");
        }
        }
}

First run the Provider, check that there are messages in the queue of the mq monitoring station, and then run the Comsumer, you can see the printed consumption information, and the corresponding data in the queue is cleared.

5. Basic API usage

It mainly explains some concepts and functions of RabbitMQ used.

1.MQ usage process

  •    The client connects to the message queue server and opens a channel. 
  •  The client declares an exchange and sets relevant properties. 
  •  The client declares a queue and sets relevant properties. 
  •  The client uses the routing key to establish a binding relationship between the exchange and the queue. 
  •  Clients post messages to exchange. 
  •     After the exchange receives the message, it routes the message according to the key of the message and the binding that has been set, and delivers the message to one or more queues.

2.ConnectionFactory

Connection factory class, we can set some connection parameters through this class

    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("127.0.0.1");//MQ的IP
    factory.setPort(5672);//MQ端口
    factory.setUsername("test");//MQ用户名
    factory.setPassword("test");//MQ密码

3.Connection

It is the rabbitMq connection through the factory class New, and our subsequent interactions with mq are based on this connection.

Connection connection = factory.newConnection();

4.Channel channel

The channel for reading and writing messages can be understood as the path to the queue 


5. Exchange

Defines message routing rules 


6. Queue Queue

is the basic unit for storing messages 


7.Bind

Queue and Exchange are bound, which means that the message that conforms to the routing rules will be placed in which message queue 
. Before using the queue, you need to use the channel to declare the queue. The channel.queueDeclare method will create a queue when the queue does not exist. If the queue exists, it is not created.

    Channel channel = connection.createChannel();

    /*定义交换机*/
    channel.exchangeDeclare("COLOR_EXCHANGE", "direct");
    /*创建多个消息队列*/
    channel.queueDeclare("BLACK_QUEUE", false, false, false, null);
    channel.queueDeclare("RED_QUEUE", false, false, false, null);
    /*绑定交换机和队列*/
    channel.queueBind("BLACK_QUEUE", "COLOR_EXCHANGE", "black");
    channel.queueBind("RED_QUEUE", "COLOR_EXCHANGE", "red");

8.publish

Send a message to a queue: send a message to the specified queue through the channel.basicPublish method, the first parameter is the switch name, the second parameter is the queue name, and the third parameter is used for the priority queue (more on this later, if not Applies to null), the last parameter is the bytes of the message content

/*通过交换机发送不同类别的消息到不同的队列中,注意,消息是由一个交换机来根据标志发往不同的队列中去*/
    String message = "black";
    channel.basicPublish("COLOR_EXCHANGE", "black", null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");

    message="red";
    channel.basicPublish("COLOR_EXCHANGE", "red", null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");

9.consume

Consumers also need to create channels to consume messages, declare specified switches and queues, and then bind queues and consumers through the basicConsume method of the consumer object. The first parameter is the queue name, and the second parameter is whether to automatically ack 
nextDelivery (long timeout ) method can get the message. The parameter is the maximum waiting time. If the message is obtained within this time, it will return the message. If not, it will wait for the maximum time. If there is still no message data, it will return null. 
Then get the message content through the consumer.nextDelivery().getBody() method

    /*创建消费者对象,用于读取消息*/
    QueueingConsumer consumer = new QueueingConsumer(channel);
    channel.basicConsume("RED_QUEUE", false, consumer);
    /* 读取队列,并且阻塞,即在读到消息之前在这里阻塞,直到等到消息,完成消息的阅读后,继续阻塞循环*/
    while (true) {
    QueueingConsumer.Delivery delivery = consumer.nextDelivery();
    String message = new String(delivery.getBody());
    System.out.println(" [x] Received '" + message + "'");
    }
    }

6. ACK mechanism

The message in the QUEUE has an ACK mechanism, that is, the confirmation mechanism of the message.

channel.basicConsume(queueName, false, consumer)

The second parameter is whether to automatically ack. 
In actual project applications, it takes a while for consumers to get the message for processing. When the user aborts the operation or goes down due to network problems, if the automatic ack is set, the consumption will be lost. In order to prevent this from happening, it is generally set to cancel the auto ack, which requires the consumer to send a confirmation receipt before deleting it from the queue to ensure that the message can be correctly consumed. 
After the consumer has processed the message, it can pass

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

To confirm the ack message, the first parameter of this method is the identification tag of the message, and the second is whether to re-enter the queue. If it is set to false, the message will be deleted from the queue. If it is true, it will be re-entered into the queue and wait for consumption again.

7. Persistence of messages

MQ messages support persistent operations, and will save data to the hard disk to prevent data loss due to server downtime or RabbitMQ restart. We need to set the persistence of the queue (queue name recovery after restart) and the persistence of messages (data retention after restart). 
Persistence of the queue:

channel.queueDeclare(queueName, true, false, false, null);

The first parameter is the queue name, and the second parameter is whether to persist the operation. If set to true, the persistence of the persistent 
message of the queue information is realized:

channel.basicPublish("", queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());

The third parameter, MessageProperties.PERSISTENT_TEXT_PLAIN, sets the persistence of the message

8. Fair distribution of information

If there are multiple consumers listening to the same queue, MQ will amortize messages to multiple consumers by default. In the case of different processing times of different consumers, it may cause a consumer to accumulate a lot of unprocessed messages and another A situation where a consumer has no messages to process, to avoid this situation, we can pass

    channel.basicQos(1,true);

The method sets the number of distributions for each consumer. 1 means that the consumer processes at most one message at a time. If you want to get the next message, you must ack the unacked message before you can get the next message. This is achieved. Fair distribution mechanism for messages.

Nine, the priority of the message

The messages of the queue are first in first out by default, which is also the queue supported by RabbitMQ by default. 
MQ before version 3.5.0 does not support priority queues by default, and can only be implemented by installing plugins. 
MQ after version 3.5.0 has integrated this function and can be used directly. 
First, we need to declare the priority queue:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-priority", 100);
channel.queueDeclare(queueName, true, false, false, args);

The last parameter of the queueDeclare method is previously set to null, which is the default non-priority queue, so we pass a map containing the key x-max-priority as a parameter, we can create a priority queue, the value of 100 is set Number of queue maximum supported priorities. 
Note: It should be noted here that after declaring the queue for the first time and creating the queue, if the attributes of the subsequent declaration of this queue are inconsistent, an error will be reported, and the same queue with the same attributes must be declared.

Next, we put the priority of delivering the message when we put it:

BasicProperties props = MessageProperties.PERSISTENT_BASIC.builder().priority(1).build();
channel.basicPublish("", queueCheckName, props, message.getBytes());

First define a BasicProperties variable, pass a variable with a priority of 1, and then pass this parameter to 
the third parameter of the basicPublish method (that is, the previously persisted parameter, the MessageProperties.PERSISTENT_BASIC.builder() method is also persistent)

10. Message routing and distribution

The commonly used Exchange Types in RabbitMQ are fanout, direct, topic, and headers (the AMQP specification also mentions two Exchange Types, namely system and custom, which will not be described here).

The default is to send directly. For example, in the queueDeclare method above, the switch is not specified, but the queue name is directly specified, which is the default switch, and the message is placed in the queue of the corresponding name. In this form, you need to specify the corresponding queue name when sending and consuming messages.

Fanout is the form of broadcasting, which sends messages to all bound queues through the switch.

// 声明一个名称为"exchange_fanout"的exchange
channel.exchangeDeclare("exchange_fanout", "fanout");
// 将消息发送给exchange
channel.basicPublish("exchange_fanout", "", null, msg.getBytes());

Topic is the matching mode. We can set a topic topic for each message type, and bring the topic when putting it in the queue. When consumers get messages, they can get messages by specifying a topic. 
message provider:

    //指定topic类型的交换机
    String exchange = "exchange03"; 
    String msgType= "type1";  
    channel.exchangeDeclare(exchange , "topic") ;  
    String msg = "Hello World!";  
    //发送指定主题的消息  
    channel.basicPublish( exchange , msgType, null , msg.getBytes());

Message consumer:

    String exchange = "exchange03";  
    channel.exchangeDeclare(exchange , "topic") ;    
    String queueName = channel.queueDeclare().getQueue() ;  

    //第三个参数就是type
    channel.queueBind(queueName, exchangeName, "type1") ;           
    QueueingConsumer consumer = new QueueingConsumer(channel) ;  
    channel.basicConsume(queueName, true, consumer) ;  

    //循环获取消息  
    while(true){  
        //获取消息,如果没有消息,这一步将会一直阻塞,可以设定超时时间  
        Delivery delivery = consumer.nextDelivery() ;  
        String msg = new String(delivery.getBody()) ;    
        System.out.println("received message[" + msg + "] from " + exchangeName);  
    }  

Eleven, Spring integration 

RabbitMQ can be seamlessly integrated with Spring, there is no need to deal with the client, and it is more convenient to use.

spring-rabbit.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/rabbit
    http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <description>rabbitmq 连接服务配置</description>
    <context:component-scan base-package="com.mop.self.mq"/>
    <context:property-placeholder location="classpath:conf/rabbitmq.properties"/>

    <!-- 连接配置 -->
    <rabbit:connection-factory id="connectionFactory" host="${mq.host}" username="${mq.username}" password="${mq.password}" port="${mq.port}"/>
    <rabbit:admin connection-factory="connectionFactory"/>

    <!-- spring template声明-->
    <rabbit:template exchange="test-mq-exchange" id="amqpTemplate"  connection-factory="connectionFactory"  message-converter="jsonMessageConverter" />

    <!-- 队列说明 -->
    <rabbit:queue id="test_queue" name="test_queue" durable="true" auto-delete="false" exclusive="false" />

    <!-- direct 交换器 -->
    <rabbit:direct-exchange name="test-mq-exchange" durable="false" auto-delete="false" id="test-mq-exchange">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue" key="test_queue_key"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" message-converter="jsonMessageConverter">
        <rabbit:listener ref="mqListener" queues="test_queue" />
    </rabbit:listener-container>

    <!-- 消息对象json转换类 -->
    <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />
</beans>

rabbitmq.propreties:

mq.host=127.0.0.1
mq.username=tzc
mq.password=tzc
mq.port=5672

Define the MQ messaging class and production consumer:

MqMessage:

package com.mop.self.mq;

/**
 * Author: Mr.tan
 * Date:  2017/08/04
 * Description:MQ消息封装
 */
public class MqMessage {

    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MqMessage{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

MqProducer:

package com.mop.self.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Author: Mr.tan
 * Date:  2017/08/04
 * Description: MQ生产者
 */
@Component
public class MqProducer {

    private static final Logger logger = LoggerFactory.getLogger(MqProducer.class);

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendMessage(Object message){
        logger.debug("produce message:"+message);
        amqpTemplate.convertAndSend("test_queue_key",message);
    }
}

MqListener:

package com.mop.self.mq;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.stereotype.Component;

/**
 * Author: Mr.tan
 * Date:  2017/08/04
 * Description: mq监听
 */
@Component
public class MqListener implements  MessageListener {

    private static final Logger logger = LoggerFactory.getLogger(MqListener.class);

    public void onMessage(Message message) {
        MqMessage mqMessage = JSON.parseObject(new String(message.getBody()),MqMessage.class);
        logger.debug("get message:"+mqMessage);
    }

}

Test code:

package com.mop.self.mq;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Author: Mr.tan
 * Date:  2017/08/04
 * Description:
 */
public class MqTest {

    public static void main(String[]args){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:conf/spring-core.xml");
        MqProducer mqProducer = (MqProducer) applicationContext.getBean("mqProducer");
        MqMessage mqMessage = new MqMessage();
        mqMessage.setId("34");
        mqMessage.setName("测试");
        mqProducer.sendMessage(mqMessage);
    }
}

Execute the method, the producer sends a message, listens to get the message and prints it

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324896943&siteId=291194637