(1) RabbitMQ work queue model structure
The work queue model increases the number of consumers compared to simple queues.
The producer provides the message to the message queue, and the consumer can get the message in the queue. By default, polling distribution is used to distribute messages to consumers in work queues. The so-called polling distribution means that no matter how fast or slow the consumer processes the messages, they will send the messages to the consumer in turn according to the order.
(two) work queue practice (polling distribution)
The code using work queues is basically the same as that of simple queues, with only a few more consumers
2.1 create tool class
The code for creating the tool class has been mentioned in the previous blog. Here is the code:
public class ConnectionUtil { public static Connection getConnection() throws IOException, TimeoutException { //Define a connection factory ConnectionFactory factory=new ConnectionFactory(); //Set service address factory.setHost("127.0.0.1"); //Set AMQP port factory.setPort(5672); //Set VHOSTS factory.setVirtualHost("/vhosts_sdxb"); //Set user name factory.setUsername("user_sdxb"); factory.setPassword("123456"); return factory.newConnection(); } }
2.2 create producers
public class Send { private static final String QUEUE_NAME="work_queue"; public static void main(String[] args) throws IOException, TimeoutException { //1. Get connection Connection connection = ConnectionUtil.getConnection(); //2. Create channel Channel channel = connection.createChannel(); //3. Create queue declaration channel.queueDeclare(QUEUE_NAME,false,false,false,null); for (int i=0;i<50;i++){ String msg="i="+i; channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); } channel.close(); connection.close(); } }
2.3 create consumer one
In order to reflect the speed of message processing by consumers, I set thread sleep for 1s and 2s respectively in the two consumers
public class Receive1 { private static final String QUEUE_NAME="work_queue"; public static void main(String[] args) throws IOException, TimeoutException { //Get connection Connection connection = ConnectionUtil.getConnection(); //Create channel Channel channel = connection.createChannel(); //Create queue declaration channel.queueDeclare(QUEUE_NAME, false, false, false, null); //Create consumer monitoring method 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(msg); try { //Set sleep practice for 1s Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; //listen queue channel.basicConsume(QUEUE_NAME,true,consumer); } }
2.4 create consumer II
public class Receive2 { private static final String QUEUE_NAME="work_queue"; public static void main(String[] args) throws IOException, TimeoutException { 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(msg); try { //Set sleep time for 2s Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }; channel.basicConsume(QUEUE_NAME,true,consumer); } }
Run the two consumers separately, and then run the producer to send 50 messages. It can be found that although the ability of the two consumers to process messages is fast or slow, they get 25 messages. The screenshot of the message obtained by consumer 1 is shown below.
(3) Fair dispatch
In some scenarios, polling distribution is unreasonable, so the work queue also has a fair distribution method. The so-called fair distribution is that people with more advantages and more efforts get more messages when they are fast, and people who are slow get less messages when they are slow. The implementation of fair distribution only needs to make some changes to the code:
3.1 modify the producer
For producers, only one restriction should be added to the channel to restrict the channel from sending more than one message to the same consumer, that is, the second message will be sent to the consumer only after the consumer processes a message. Using the channel.basicQos(); method, set the parameter to 1 to limit no more than 1 message at a time.
public class Send { private static final String QUEUE_NAME="work_queue_fair"; public static void main(String[] args) throws IOException, TimeoutException { Connection connection = ConnectionUtil.getConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,false,false,false,null); //Limit channel to send no more than one message to the same consumer int prefenchCount=1; channel.basicQos(prefenchCount); for (int i=0;i<50;i++){ String msg="i="+i; channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); } channel.close(); connection.close(); } }
3.2 modifying consumers
For consumers, there are three places that need to be modified: the first place is to modify the restriction information of the channel like the producer; the second place is to turn off the automatic response of consumers; the third place is to set the manual receipt, that is, to send the completed instructions to the queue after processing a message manually.
//Ensure only one distribution at a time channel.basicQos(1); //Set manual receipt channel.basicAck(envelope.getDeliveryTag(),false); //Turn off auto answer boolean autoAck=false; channel.basicConsume(QUEUE_NAME,autoAck,consumer);
Here is the revised consumer code
public class Receive1 { private static final String QUEUE_NAME="work_queue_fair"; public static void main(String[] args) throws IOException, TimeoutException { Connection connection = ConnectionUtil.getConnection(); final Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); //Ensure only one distribution at a time 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(msg); //Set manual receipt channel.basicAck(envelope.getDeliveryTag(),false); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; //Turn off auto answer boolean autoAck=false; channel.basicConsume(QUEUE_NAME,autoAck,consumer); } }
After setting the work queue, it becomes a fair distribution method. Test results:
3.3 about automatic response
When modifying the consumer code, we turned off auto answer
boolean autoAck=false; channel.basicConsume(QUEUE_NAME,autoAck,consumer);
This is the second parameter of basicConsume
When autoAck=true, it means auto answer is turned on. Once rabbitmq sends the message in the queue to the consumer, the message will disappear from the queue. But if the consumer hangs up at this time, the news will disappear completely.
When autoAck=false, turn off auto answer, rabbitmq will send the message in the queue to the consumer, and the message in the queue will be deleted only after the consumer returns to confirm.