五、RabbitMQ之工作队列(Work Queue)之公平分发(Fair Dispatch)

一、模型

Work Queue模型

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

    公平分发(Fair Dispatch)消费者1(C1)从队列(Queue)中获取一条消息之后,处理完消息之后手动发送反馈回执,告诉RabbitMQ我处理完了,接着就会再从队列中获取一条消息 ;在消费者1(C1)获取消息后处理业务之时消费者2(C2)也会从队列中获取一条消息之后,处理完消息之后同样会手动发送反馈回执,告诉RabbitMQ我处理完了,接着也会再从队列中获取一条消息。两个消费者(C1&C2)不需要彼此等待对方处理完业务才能获取消息,这样一来业务处理时间短的消费者就能多处理消息,做到“能者多劳”,提高工作效率。

二、公平分发(Fair Dispatch)需要注意点

    1、保证消费者一次只接收一条消息(Channel.basicQos(1));

    2、使用公平分发需要消费者手动反馈,所以必须关闭自动应答(ack 改为手动,具体在消费者代码中注释会体现)。

三、Java编程实现

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

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

package com.rabbitMQ.workfair;

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

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

/**
 * 工作对列之公平分发(Fair dispatch)-生产者
 * @author zhoujin
 * @data 2019-1-17
 */
public class WorkFairProducer {
	
	private static final String QUEUE_NAME = "work_fair_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.限制发送给同一个消费者不得超过一条消息
			 * 每个消费者发送确认消息之前,消息队列不发送下一条消息至消费者,一次只处理一条消息
			 */
			channel.basicQos(1);
			// 5.发送消息入队列
			for (int i = 1; i <= 20; i++) {
				String message = "This is fair work queue, 发送第" + i + "条消息!";
				channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
				
				Thread.sleep(i * 200);
			}
			
			System.out.println("======================= Fair work Queue send message end! =======================");
		
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			try {
				ConnectionUtils.closeConnection(channel, conn);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (TimeoutException e) {
				e.printStackTrace();
			}
		}
	}
}

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

package com.rabbitMQ.workfair;

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;

/**
 * 工作对列之公平分发(Fair dispatch)-消费者1
 * @author zhoujin
 * @data 2019-1-17
 */
public class WorkFairFirstConsumer {
	
	private static final String QUEUE_NAME = "work_fair_queue";
	
	public static void main(String[] args) {
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			final Channel channel = conn.createChannel();
			// 3.声明队列
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			// 4.保证一次只接受一条消息
			channel.basicQos(1);
			// 5.创建消费者
			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 fair work consumer received a message! 【Content:" + message + "】 =======================");
					
					// 休眠,模拟业务处理
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally {
						// 6.手动发送反馈回执
						channel.basicAck(envelope.getDeliveryTag(), false);
					}
				}
				
			};
			// 7.监听队列(关闭自动应答,即第二个参数设置为false)
			channel.basicConsume(QUEUE_NAME, false, consumer);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}
	}

}

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

package com.rabbitMQ.workfair;

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;

/**
 * 工作对列之公平分发(Fair dispatch)-消费者2
 * @author zhoujin
 * @data 2019-1-18
 */
public class WorkFairSecondConsumer {
	
	private static final String QUEUE_NAME = "work_fair_queue";
	
	public static void main(String[] args) {
		try {
			// 1.获取连接
			Connection conn = ConnectionUtils.getConnection();
			// 2.从连接中获取通道
			final Channel channel = conn.createChannel();
			// 3.声明队列
			channel.queueDeclare(QUEUE_NAME, false, false, false, null);
			// 4.保证一次只接受一条消息
			channel.basicQos(1);
			// 5.创建消费者
			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 fair work consumer received a message! 【Content:" + message + "】 =======================");
					
					// 休眠,模拟业务处理
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally {
						// 6.手动反馈回执
						channel.basicAck(envelope.getDeliveryTag(), false);
					}
				}
				
			};
			// 7.监听队列(关闭自动应答,即第二个参数设置为false)
			channel.basicConsume(QUEUE_NAME, false, 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、第二个消费者控制台输出

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

五、总结

    由两个消费者的控制台输出可以看出,消费者1(C1)业务时长为2秒,其处理9了条消息;消费者2(C2)业务时长为1秒,其处理了11条消息。由于两个消费者处理业务时长相差不大,所以处理消息条数差不多,可以尝试把两者处理业务时长差距变大,查看结果。虽然处理消息数据差不多,但看到消费者2(C2)接连处理了第2条、第3条和第5条、第6条数据,不再是顺序轮流获取消息了,也体现了“能者多劳”的公平分发(Fair Dispatch)。


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

猜你喜欢

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