三、RabbitMQ之简单队列(Simple Queue)

一、简单队列模型

简单队列模型

  • P(producer):生产者——发送消息
  • Q(Queue): 消息队列(图中红色方形)——存储消息
  • C(consumer): 消费者——接收消息

二、Java编程实现

    1、导入AMQP协议jar包(RabbitMQ是基于AMQP通讯协议的);

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>3.6.5</version>
</dependency>

    spring-amqp-1.3.5.RELEASE下载

    2、创建RabbitMQ连接工具类;

package com.rabbitMQ.util;

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

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

public class ConnectionUtils {
	
       /**
         * 获取RabbitMQ的连接
	 * @return Connection  
	 * @author zhoujin
	 * @throws TimeoutException 
	 * @throws IOException 
	 * @date 2019-1-13
	 */
	public static Connection getConnection() throws IOException, TimeoutException{
		// 1.定义连接工厂
		ConnectionFactory factory = new ConnectionFactory();
		// 2.设置连接参数
		factory.setHost("127.0.0.1");           // 连接地址
		factory.setPort(5672);                  // 监听端口
		factory.setVirtualHost("/vhost_study");  // vhost
		factory.setUsername("study_user");      // 用户名
		factory.setPassword("123");             // 密码
		// 3.返回连接对象
		return factory.newConnection();
	}
	
	/**
	 * 关闭连接
	 * @param connection
	 * @throws IOException
	 * @return void
	 * @author zhoujin
	 * @data 2019-1-13
	 */
	public static void closeConnection(Connection connection) throws IOException{
		if (connection != null) {
			connection.close();
		}
	}
	
	/**
	 * 关闭通道以及连接(先后顺序不能颠倒)
	 * @param connection
	 * @throws IOException
	 * @return void
	 * @author zhoujin
	 * @throws TimeoutException 
	 * @data 2019-1-13
	 */
	public static void closeConnection(Channel channel, Connection connection) throws IOException, TimeoutException{
		if (channel != null) {
			channel.close();
		}
		if (connection != null) {
			connection.close();
		}
	}

}

    3、创建生产者发送消息;

package com.rabbitMQ.simple;

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

import com.rabbitMQ.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 消息生产者
 * @author zhoujin
 * @data 2019-1-13
 */
public class SimpleProducer {
	
	private static final String QUEUE_NAME = "simple_queue";
	
	public static void main(String[] args) {
		Connection conn = null;
		Channel channel = null;
		try {
			// 1.获取连接
			conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			channel = conn.createChannel();
			/*
			 * 3.声明队列
			 * param_1:队列名称
			 * param_2:是否持久化(重启是否消失)
			 * param_3:是否独占(仅创建者Connection私有,断开连接后自动删除)
			 * param_4:没有连接时是否自动删除
			 * param_5:此队列的其他属性设置
			 */
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			/*
			 * 4.发送消息到队列中
			 * param_1:交换机名称(若没有定义交换机,则此处填"",使用默认交换机)
			 * param_2:Routing Key(路由键)
			 * param_3:此消息的其他属性设置
			 * param_4:需要发送的消息内容
			 * ★ 注:默认交换机(AMQP default),它是direct类型的。默认交换机隐式地绑定到每个队列,Routing Key(路由键)等于队列名称。
			 */
			String message = "This is simple MQ!";
			channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
			
			System.out.println("======================= Simple MQ send message end! 【Content:" + message + "】 =======================");
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} finally {
			try {
				ConnectionUtils.closeConnection(channel, conn);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (TimeoutException e) {
				e.printStackTrace();
			}
		}
		
	}

}

    4、创建消费者接收消息。

package com.rabbitMQ.simple;

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

import com.rabbitMQ.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.rabbitmq.client.ShutdownSignalException;

/**
 * 消息消费者(接收者)
 * @author zhoujin
 * @data 2019-1-13
 */
public class SimpleConsumer {
	
	private static final String QUEUE_NAME = "simple_queue";
	
	public static void main(String[] args) {
		// 4.x以前的版本使用旧方法
		// oldMethod();
		
		// 4.x以后的版本使用新方法
		newMethod();
	}
	
	public static void newMethod(){
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			Channel channel = conn.createChannel();
			/*
			 * 3.声明队列
			 * param_1:队列名称
			 * param_2:是否持久化(重启是否消失)
			 * param_3:是否独占(仅创建者Connection私有,断开连接后自动删除)
			 * param_4:没有连接时是否自动删除
			 * param_5:此队列的其他属性设置
			 */
			channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null);
			// 4.定义消费者获取消息(将客户端与队列绑定,若该队列中有消息,便会执行回调函数handleDelivery)
			DefaultConsumer consumer = new DefaultConsumer(channel){
				@Override    // envelope对象中是生产者相关信息(比如交换机名、路由键等),body是消息实体
				public void handleDelivery(String consumerTag,
						Envelope envelope, BasicProperties properties,
						byte[] body) throws IOException {
					
					String message = new String(body, "UTF-8");
					System.out.println("=======================NewMethod: Simple MQ received a message! 【Content:" + message + "】 =======================");
				}
			};
			/*
			 *  5.监听队列,回复消息接收反馈,通知将此消息在队列中移出(RabbitMQ中的消息确认机制)
			 *  param_1: 队列名称
			 *  param_2: autoAck:是否自动ack。如果不自动ack,需要使用channel.ack、channel.nack、channel.basicReject进行消息应答。
			 *  param_3: 消费者对象
			 */
			channel.basicConsume(QUEUE_NAME, true, consumer);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 4.x版本之前
	 * @return void
	 * @author zhoujin
	 * @data 2019-1-13
	 */
	public static void oldMethod(){
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			Channel channel = conn.createChannel();
			// 3.定义队列的消费者(4.x版本开始废弃)
			QueueingConsumer consumer = new QueueingConsumer(channel);
			// 4.监听队列
			channel.basicConsume(QUEUE_NAME, true, consumer);
			// 5.获取消息
			while(true){
				Delivery delivery = consumer.nextDelivery();
				String message = new String(delivery.getBody(), "UTF-8");
				System.out.println("======================= Simple MQ received a message! 【Content:" + message + "】 =======================");
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (ShutdownSignalException e) {
			e.printStackTrace();
		} catch (ConsumerCancelledException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

三、运行代码以及控制台输出

    1、运行生产者代码;

    1.1、 控制台输出

生产者控制台输出

    1.2、在web管理页面查看消息

web页面查看消息_1

    1.3、在web管理页面查看获取消息 

web页面查看消息_2

    2、运行消费者程序。

    2.1、消费者控制台输出(旧方法)

消费者控制台输出

    2.2、新方法控制台输出

消费者控制台输出_NewMethod

四、简单队列(Simple Queue)的不足

    耦合性高,生产者一一对应消费者,若想有多个消费者获取队列中的消息,就无法获取。 队列名变更,就需要生产者和消费者两边同时变更。


发布了47 篇原创文章 · 获赞 16 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/zorro_jin/article/details/86648173