由于我们上一节做了一个简单队列,他有很多缺点,比如耦合性高,生产者一一对应消费者(如果我想有多个消费者消费消息,这个时候就不行了),队列名变更,这个时候生产者与消费者要同时变更。
workqueueus工作队列
这个图的意思就是一个生产者把消息生产到队列中,两个消费者去消费他,甚至多个消费者消费他
Simple队列是一一对应的,在实际的开发中,生产者发送消息是毫不费力的,消费者一般是要和业务逻辑相结合的,这个时候消费者接受到消息后就需要处理,可能需要花费时间,这个时候队列就会挤压很多消息,既然这样就会出现多个消费者消费消息。
下面我们修上代码
首先是生产者
public class Send {
private static final String QUEUE_NAME="test_work_queue";
/** |----C1
* P-----Queue-----|
* |----C2
* @param s
*/
public static void main(String[] s) {
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false,false , null);
//发送消息
for(int i = 0;i < 50; i++){
String msg = "hello" + i;
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("生产者发送的消息:"+msg);
Thread.sleep(i*20);
}
channel.close();
connection.close();
}catch (IOException e){
e.printStackTrace();
}catch (TimeoutException e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
}
接下来是消费者
public class Recv {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] s){
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false , false, null);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//队列中有消息就触发这个方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body,"UTF-8");
System.out.println("Recv1:"+msg);
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}catch(Exception e){
e.printStackTrace();
}
}
}
然后是另一个消费者
public class Recv2 {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] s){
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false , false, null);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//队列中有消息就触发这个方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body,"UTF-8");
System.out.println("Recv2:"+msg);
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}catch(Exception e){
e.printStackTrace();
}
}
}
首相需要声明一下,我消费者一和消费者二是消费同一个队列的,但是消费者1消费完一个要2秒,消费者2消费完一个要1秒,按理说能者多劳,消费者二要消费的更多一点,但是事实是这样么。
我们看消费结果:
消费者一的结果(由于太多,这里只截取部分):
Recv1:hello0
Recv1:hello2
Recv1:hello4
Recv1:hello6
Recv1:hello8
Recv1:hello10
Recv1:hello12
消费者二的结果:
Recv2:hello1
Recv2:hello3
Recv2:hello5
Recv2:hello7
Recv2:hello9
Recv2:hello11
Recv2:hello13
可以看出,他们是轮询消费,也就是说你消费一个,我消费一个,不会说我消费的快我消费的就多。
这里说的能者多劳值得就是消费者一和消费者二都在消费,但是分谁的能力强,消费能力强的可以继续消费,消费能力弱的就要比消费能力消费能力强的差一些。他的原理如下图
下面直接上代码
下面是生产者
public static void main(String[] s) {
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false,false , null);
//每个消费者 发送消息确认之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
//限制发送给同一个消费者 不得超过一条。
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//发送消息
for(int i = 0;i < 50; i++){
String msg = "hello" + i;
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("生产者发送的消息:"+msg);
Thread.sleep(i*2);
}
channel.close();
connection.close();
}catch (IOException e){
e.printStackTrace();
}catch (TimeoutException e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
下面是消费者
public static void main(String[] s){
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false , false, null);
channel.basicQos(1);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//队列中有消息就触发这个方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body,"UTF-8");
System.out.println("Recv1:"+msg);
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//自动应答改为false
Boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}catch(Exception e){
e.printStackTrace();
}
}
下面是另一个消费者
public static void main(String[] s){
try{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//获取通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false , false, null);
channel.basicQos(1);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//队列中有消息就触发这个方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body,"UTF-8");
System.out.println("Recv2:"+msg);
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
Boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}catch(Exception e){
e.printStackTrace();
}
}
从结果看来,消费者2处理的要比消费者1多,证明我们上面的代码是没有问题的。