1.轮询分发
队列给每一个消费者都发送一样的量的数据。
以下为例:
生产者发送数据:
public class Send
{
//队列名称
private static final String QUEUE_NAME = "QUEUE1";
public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionFactoy.newConnection("生产者");
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 10; i++)
{
String message = "这是第" + i+"次发送消息";
System.out.println("[send]:" + message);
//发送消息
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("utf-8"));
Thread.sleep(20 * i);
}
channel.close();
connection.close();
}
catch (IOException | TimeoutException | InterruptedException e)
{
e.printStackTrace();
}
}
}
运行结果:
[send]:这是0次发送消息
[send]:这是1次发送消息
[send]:这是2次发送消息
[send]:这是3次发送消息
[send]:这是4次发送消息
[send]:这是5次发送消息
[send]:这是6次发送消息
[send]:这是7次发送消息
[send]:这是8次发送消息
[send]:这是9次发送消息
消费者1:
public class Receive1
{
//队列名称
private static final String QUEUE_NAME = "QUEUE1";
public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionFactory.new Connection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义消费者
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("这是接收的第" + message);
try
{
//消费者休息1s处理业务
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
运行结果:
这是接收的第:这是0次发送消息
这是接收的第:这是2次发送消息
这是接收的第:这是4次发送消息
这是接收的第:这是6次发送消息
[这是接收的第:这是8次发送消息
消费者2:
public class Receive2
{
//队列名称
private static final String QUEUE_NAME = "QUEUE1";
public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionFactory.new Connection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义消费者
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("这是接收的第" + message);
try
{
//消费者休息2s处理业务
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
运行结果:
这是接收的第:这是1次发送消息
这是接收的第:这是3次发送消息
这是接收的第:这是5次发送消息
这是接收的第:这是7次发送消息
[这是接收的第:这是9次发送消息
总结从两个消费者消费消息的模式来看,不难理解默认的轮询分发方式,就是采取一人一条的顺序执行来消费消息的。两个消费者得到的消息数量是相等的,从运行时可以看出消费者1先消费完,然后才轮到消费者2消费。并不会因为两个消费者的处理速度不一样使得两个消费者处理的消费信息量不一样。从mq界面可以看到,虽然mq中的消息很快就没有了,但是消费者依然在处理消息,这种轮询的分发方式存在着很大的隐患。
2.公平分发
消费者设置每次从队列里取出一条数据,并且关闭自动回复机制,每次取出一条数据,手动回复并继续取下一条数据。
代码测试:
生产者代码同上:
消费者1:
public class Receive1
{
//队列名称
private static final String QUEUE_NAME = "QUEUE_NAME1;
public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//设置每次从队列里取一条数据
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义消费者
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("接收的第" + message);
try
{
//消费者休息1s处理业务
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
//手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//设置手动应答
boolean autoAck = false;
//监听队列
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
运行结果:
这是接收的第:这是1次发送消息
这是接收的第:这是2次发送消息
这是接收的第:这是4次发送消息
这是接收的第:这是5次发送消息
这是接收的第:这是6次发送消息
这是接收的第:这是7次发送消息
这是接收的第:这是9次发送消息
消费者2:
public class Receive2
{
//队列名称
private static final String QUEUE_NAME = "QUEUE_NAME1";
public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionFactory.new Connection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//保证一次只分发一个
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义消费者
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("接收消息第" + message);
try
{
//消费者休息2s处理业务
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
//手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//设置手动应答
boolean autoAck = false;
//监听队列
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
运行结果:
这是接收的第:这是3次发送消息
这是接收的第:这是8次发送消息
总结
消费者1处理了8条消息,消费者2处理了2条消息。
与轮询不同的是,当每个消费都设置了每次只会从队列中取一条数据时,而且关闭自动应答,在每次处理完数据后手动给队列发送确认收到的数据,这样队列就会公平给消费者发送数据。而且在管理界面可以看到消费完数据而减少的,而不是一下子全部发送完。显然公平分发更符合设计规范。