rabbitmq--工作队列(单发多收)模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jasnet_u/article/details/84197251

上一篇,我们介绍了rabbitmq中最简单的一种模型——收发模型,收、发都只有一方,即一个应用,并且不涉及到exchange (实际上这种模型中也是有exchange的,exchange的类型为direct,且名称为空字符串"",只不过这里的exchange是透明的,故常被我们所忽略了,好像收、发之间是直接连通的)。

 本篇我们介绍一下rabbitmq中的另一种模型——工作队列模型,也叫单发多收模型,它是收发模型的延伸。它支持一方发送消息,由多方共同接收处理,并且支持给多个消费方设置不同的QOS 。 如图:

例如,一个大系统中每天都与产生大量的任务(消息),需要发布给各个子系统去共同处理,这时候就可以采用工作队列模型 。

示例代码:  

任务生产(发布)者: 

package com.tingcream.rabbitmq.workQueues;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
/**
 * (AMQP DEFAULT)  
 * @author jelly
 *
 */
public class NewTask {
	
  private final static String QUEUE_NAME = "direct_task_queue";
	
  public static void main(String[] args) throws Exception {
	  
		
		ConnectionFactory factory = new ConnectionFactory();
		//主机  端口  vhost 用户名 密码
		factory.setHost("192.168.9.102");
		factory.setUsername("rabbitmq");
		factory.setPassword("rabbitmq123");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setVirtualHost("/");
		
		
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
		
 
		boolean  durable=true ;
		channel.queueDeclare(QUEUE_NAME, durable, false,false, null);    
	    //String message = "Hello rabbitmq";
		//exchagne名称默认为 (AMQP DEFAULT)  direct
		//channel.basicPublish(exchange, routingKey, props, body);
	    //channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
		//System.out.println(" [x] Sent '" + message + "'");
		
		for(int i=0;i<20;i++) {
			String message="hello direct task message E "+i;
			//发布消息 
			channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
			System.out.println(" [x] Sent '" + message + "'");
		}
		
		//关闭连接
		channel.close();
		connection.close();
	
  }
	
}

任务处理(消费)者A:

package com.tingcream.rabbitmq.workQueues;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class WorkA {
	
	private final static String QUEUE_NAME = "direct_task_queue";
	

    public static void main(String[] args) throws Exception {
    	
        ConnectionFactory factory = new ConnectionFactory();
          
		//主机  端口  vhost 用户名 密码
		factory.setHost("192.168.9.102");
		factory.setUsername("rabbitmq");
		factory.setPassword("rabbitmq123");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setVirtualHost("/");
		
		
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
      
    //  channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments)
		boolean  durable=true ;
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);//channel向服务器声明一个队列,设置durable为true,则当rabbitmq 服务器重启时,队列不会丢失
        System.out.println("WorkerA  Waiting for messages");
 
        //每次从队列获取的message的数量
         channel.basicQos(1);
     //   prefetchCount maximum number of messages that the server will deliver, 0 if unlimited

     
        final Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("WorkerA  Received :" + message );
                try {
                     
                    doWork(message);
                }catch (Exception e){
                    channel.abort();
                }finally {
                    System.out.println("WorkerA Done");
                    channel.basicAck(envelope.getDeliveryTag(),false);//当消息处理完毕后 在finally中 回复一个ack  手动ack
                }
            }
        };
        boolean autoAck=false;
         
        //消息消费完成确认   不自动 ack  非自动ack
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
    private static void doWork(String task) {
    	try {
			Thread.sleep(5000);// 暂停2秒钟
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
    }

}

任务处理(消费)者B:

package com.tingcream.rabbitmq.workQueues;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class WorkB {
	
	private final static String QUEUE_NAME = "direct_task_queue";
	

    public static void main(String[] args) throws Exception {
    	
        ConnectionFactory factory = new ConnectionFactory();
          
		//主机  端口  vhost 用户名 密码
		factory.setHost("192.168.9.102");
		factory.setUsername("rabbitmq");
		factory.setPassword("rabbitmq123");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setVirtualHost("/");
		
		
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
      
    //  channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments)
		boolean  durable=true ;//channel向服务器声明一个队列,设置durable为true,则当rabbitmq 服务器重启时,队列不会丢失
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
        System.out.println("WorkerB  Waiting for messages");
 
        //每次从队列获取的message数量
        channel.basicQos(2);
     //   prefetchCount maximum number of messages that the server will deliver, 0 if unlimited

     
        final Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("WorkerB  Received :" + message );
                try {
                     
                    doWork(message);
                }catch (Exception e){
                    channel.abort();
                }finally {
                    System.out.println("WorkerB Done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoAck=false;
         
        //消息消费完成确认   不自动 ack  非自动ack
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
    private static void doWork(String task) {
    	try {
			Thread.sleep(3000);// 暂停2秒钟
		} catch (InterruptedException e) {
			 
			e.printStackTrace();
		} 
    }

}

注意:

1 、任务消费者A和B 可以共同消费处理任务生产者(NewTask)发布的消息。消费者A和B的qos可以根据服务器性能的不同设置不同的qos,例如本例中消费者A的qos设置为了1,而消费者B的qos设置为了2 。

2 、consumer中有个重要的动作就是设置autoAck为false (默认为true),表示需要消费者程序手动ack。注意一旦consumer将autoAck设置为false之后,一定要记得处理完消息之后,向服务器发送确认消息,否则服务器将会一直转发该消息。当设置了autoAck为false,若消费者A意外宕机,处理的消息还没有ack回去,则rabbitmq服务器会将消息转给消费者B。

3、生产者发布消息时,设置了一个参数durable为true。这样做的好处是,当这样设置之后,服务器收到消息后就会立刻将消息写入到硬盘,就可以防止突然服务器挂掉,而引起的数据丢失了。  但是服务器如果刚收到消息,还没来得及写入到硬盘,就挂掉了,这样还是无法避免消息的丢失。

4、 在本例(工作队列模型)中,我们还是未看到exchange的身影,这一点同上一种模型——收发模型是一样的。  实际上,在本例(工作队列模型)中,是存在exchange的,exchange的名称为空字符串"",类型为direct ,同样对我们是透明的,也被我们所忽略了。

实际上,我们介绍rabbitmq的收发模型和工作队列模型所采用的exchange就是图中缩圈出的这种。

关于exchange的更多内容会在我们后面的篇章中讲到。

猜你喜欢

转载自blog.csdn.net/jasnet_u/article/details/84197251