2.工作模式
一个生产者,多个消费者,不涉及Exchange
工作模式的优点是,在供大于求或供不应求的情况下,可插拔消费者。
消息生产者:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
/**
* @author Becolette
* @description 请注明类功能
* @datetime 2020/9/24 15:32
*/
public class NewTask {
private static final String TASK_QUEUE_NAME = "task_queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
/**
* 此处是采用了默认的配置,可以ctrl + F3进入ConnectionFactory配置连接参数查看
* 下列的factory.setHost("localhost")相当于
* factory.setHost("localhost")
* factory.setPort(5672)
* factory.setUsername("guest")
* factory.setPassword("guest")
* factory.setVirtualHost("/")
*/
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
try {
/**
* 参考简单模式对应参数说明
* durable 当前保持持久化
*/
channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
/** join是拼接数据为字符串,这里我修改了一下,来个循环发送消息
* String message = String.join(" ", argv)
*/
for (int i = 0; i < 100; i++) {
String message = "消息体" + i;
/**
* MessageProperties.PERSISTENT_TEXT_PLAIN:消息永久保存直到被消费
*/
channel.basicPublish("", TASK_QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
消息消费者公用方法:
public static void recv() throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
/** 声明通道,接受消息 */
channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
channel.basicQos(1);
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
/**
* @see com.rabbitmq.client.AMQP.Basic.Ack
* @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk}
* or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
* 标签可以从GetOk或Deliver种获取
* @param multiple true to acknowledge all messages up to and
* including the supplied delivery tag; false to acknowledge just
* the supplied delivery tag.
* 如果true则消息和标签一起返回,否则返回标签
*/
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
/** 如果要形成交替输出,可以加等待时间 */
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
/** 参考简单模式参数说明 */
channel.basicConsume(TASK_QUEUE_NAME, false, deliverCallback, consumerTag -> {
});
}
建立两个消费者类WokRecv1,WorkRecv2,调用如下:
CommonRecv.recv();
该模式引出三个重点-消息确认:
1.自动模式
消费者从消息队列拿走信息之后,服务端就认为消息被成功消费
2.手动模式
消费者从消息队列获取消息后,服务端并没有标记为成功消费
消费者成功消费后需要将状态返回到服务端
手动模式处理方式:
1.如果消息被正常处理,返回确认信息,队列中删除该消息
2.如果消息正常发出,但是因为确认信息返回丢失,连接被关闭或是TCP连接关闭,则对消息进行重新排队,或则交付给其他消费者处理
3.如果消息消费者不存在,则会一直等到消息被消费
以上是消息确认中的手动确认方式,默认是打开状态,可以用autoAck=true关闭手动确认。
手动确认的优缺点:
优点:保证在消息没有正常消费的情况下,队列中不能被删除,保证消息的准确完整消费
缺点:消费未被消费会不断堆积,导致内存溢出
(Linux:sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged
Windows:rabbitmqctl.bat list_queues name messages_ready messages_unacknowledged)
消息持久化
保证消费者死亡,任务也不会丢失;需要将队列和消息都标记为持久。但不能完全保证,因为消息有可能消费很快,或则在写入磁盘时断电,会丢失数据,但情况很少出现
消息公平消费
保证不会有的消费者很忙,有的却很空闲,出现饥饿或是过饱现象