四、RabbitMQ之工作队列(Work Queue)之轮询分发(Round-Robin)

一、模型

Work Queue模型

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

    轮询分发(Round-Robin)即消费者1(C1)从队列(Queue)中获取一条消息之后,消费者2(C2)再从队列(Queue)中获取一条消息,等消费者2(C2)获取完之后又轮到消费者1(C1)获取,如此轮流获取,与业务时长无关,保证两个消费者获得的消息是均衡的

二、为何会有工作队列(Work Queue)的出现

    简单队列(Simple Queue)是一一对应的,而且我们实际开发中,生产者发送消息是毫不费力的,但消费者一般是跟业务相结合的,消费者接收到消息之后就需要处理,可能需要花费一定的时间,这时候队列就会积压了很多消息。

三、Java编程实现

    1、导入AMQP协议jar包,以及创建RabbitMQ连接工具类,请查看三、RabbitMQ之简单队列(Simple Queue)

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

package com.rabbitMQ.work;

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-16
 */
public class WorkProducer {
	
	private static final String QUEUE_NAME = "work_queue";
	
	public static void main(String[] args) {
		Connection conn = null;
		Channel channel = null;
		try {
			// 1.获取连接
			conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			channel = conn.createChannel();
			// 3.申明队列
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			// 4.发送消息入队列
			for (int i = 1; i <= 20; i++) {
				String message = "This is work queue, 发送第" + i + "条消息!";
				channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
				
				Thread.sleep(i * 200);
			}
			
			System.out.println("======================= Work Queue send message end! =======================");
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 5.关闭通道以及连接
			try {
				ConnectionUtils.closeConnection(channel, conn);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (TimeoutException e) {
				e.printStackTrace();
			}
		}

	}

}

    3、创建消费者1接收消息;

package com.rabbitMQ.work;

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.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

/**
 * 工作队列-消费者1
 * @author zhoujin
 * @data 2019-1-17
 */
public class WorkFirstConsumer {
	
	private static final String QUEUE_NAME = "work_queue";
	
	public static void main(String[] args) {
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			Channel channel = conn.createChannel();
			// 3.声明队列
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			// 4.创建消费者
			DefaultConsumer 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("======================= The first consumer received a message! 【Content:" + message + "】 =======================");
					
					// 休眠,模拟业务处理
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			// 5.监听队列
			channel.basicConsume(QUEUE_NAME, true, consumer);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}

	}

}

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

package com.rabbitMQ.work;

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.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

/**
 * 工作队列-消费者2
 * @author zhoujin
 * @data 2019-1-17
 */
public class WorkSecondConsumer {
	
	private static final String QUEUE_NAME = "work_queue";

	public static void main(String[] args) {
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			Channel channel = conn.createChannel();
			// 3.声明队列
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			// 4.创建消费者
			DefaultConsumer 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("======================= The second consumer received a message! 【Content:" + message + "】 =======================");
					
					// 休眠,模拟业务处理
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			// 5.监听队列
			channel.basicConsume(QUEUE_NAME, true, consumer);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}
	}
	
}

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

    1、运行两个消费者的代码;

    为何要先运行消费者的代码?

    若先启动生产者(Producer),生产者(Producer)就会马上将消息发送至队列(Queue)中。但你启动第一消费者(Consumer)时,启动会它会立即获取队列(Queue)中的消息,从而导致启动另一个消费者(Consumer)时,队列(Queue)中已无消息,看不到接收现象。

    2、运行生产者代码。

    2.1、生产者控制台输出

1.生产者控制台输出

    2.2、第一个消费者控制台输出

2.第二个消费者控制台输出

    2.3、第二个消费者控制台输出

2.第一个消费者控制台输出

五、工作队列(Work Queue)之轮询分发(Round-Robin)的不足

    由于轮询分发(Round-Robin)是不管各个消费者(Consumer)的业务时长而依次轮流分发消息的,就会存在处理业务时长短的消费者(Consumer)等待其他处理业务时长长的消费者(Consumer)的情况,这样一来就会降低工作效率。


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

猜你喜欢

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