消費者側の制限
何が消費者側を制限していますか?
まず、我々はRabbitMQの、シーンを想定1万を介してサーバ未処理のメッセージを、私たちは消費者のクライアントを開いて、以下の条件を表示されます。
すべてのプッシュオーバーのインスタントメッセージの膨大な量は、しかし、我々は、単一のクライアントが同時にので、多くのデータを扱うことができない持っています!
制限消費者エンドのRabbitMQ 提供ソリューション
RabbitMQのは提供したQoS (サービス品質)を備えてい、すなわち非自動受信確認メッセージを、メッセージの特定の数ならばという前提の下(もとを通じて消費者やチャンネルた設定QoSが前に確認されていない、新しいニュースの消費値)
ボイドBasicQos(UINT PREFETCHSIZE、USHORT prefetchCount、BOOLグローバル)。
PREFETCHSIZE :0は、メッセージのサイズを制限しません
PREFETCHSIZE :「言うでしょRabbitMQのより多くの消費者にプッシュしていないが、N個の一回という、メッセージNのメッセージはしていないACK 、消費者がしますブロックのメッセージまでオフ(遮断)ACK
Global:true\false是否将上面设置应用于Channel;简单来说,就是上面限制是Channel级别的还是Consumer级别
注意:
prefetchSize和global这两项,RabbitMQ没有实现,暂且不研究;
prefetch_count在no_ask=false的情况下生效,即在自动应答的情况下,这两个值是不生效的;
自定义消费端代码
package com.huang.rabbitmqapi.limit; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import java.io.IOException; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 16:13 */ public class MyConsumer extends DefaultConsumer { private Channel channel ; public MyConsumer(Channel channel) { super(channel); this.channel = channel; } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.err.println("-----------consume message----------"); System.err.println("consumerTag: " + consumerTag); System.err.println("envelope: " + envelope); System.err.println("properties: " + properties); System.err.println("body: " + new String(body)); channel.basicAck(envelope.getDeliveryTag(), false); } }
消费端代码
package com.huang.rabbitmqapi.limit; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 16:17 */ public class Consumer { public static void main(String[] args) throws Exception { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); String exchangeName = "test_qos_exchange"; String queueName = "test_qos_queue"; String routingKey = "qos.#"; channel.exchangeDeclare(exchangeName, "topic", true, false, null); channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, exchangeName, routingKey); //1 限流方式 第一件事就是 autoAck设置为 false channel.basicQos(0, 1, false); channel.basicConsume(queueName, false, new MyConsumer(channel)); } }
提供方代码
package com.huang.rabbitmqapi.limit; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:00 */ public class Producer { public static void main(String[] args) throws Exception { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); String exchange = "test_qos_exchange"; String routingKey = "qos.save"; String msg = "Hello RabbitMQ QOS Message"; for(int i =0; i<5; i ++){ channel.basicPublish(exchange, routingKey, true, null, msg.getBytes()); } } }
消息的ACK与重回队列
消费端手工ACK与NACK
消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿
如果由于服务器宕机等严重问题,那么我们就需要手工进行ACK,保障消费端消费成功!
消费端的重回队列
消费端重回队列是为了对没有处理成功的消息,把消息重新回递给Broker!
一般我们在实际应用中,都会关闭重回队列,也就是设置为False;因为重回队列消息有很大概率依然会处理失败!
自定义消费者代码
package com.huang.rabbitmqapi.ack; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import java.io.IOException; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:03 */ public class MyConsumer extends DefaultConsumer { private Channel channel ; public MyConsumer(Channel channel) { super(channel); this.channel = channel; } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.err.println("-----------consume message----------"); System.err.println("body: " + new String(body)); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if((Integer)properties.getHeaders().get("num") == 0) { // 手动签收,重回队列 channel.basicNack(envelope.getDeliveryTag(), false, true); } else { channel.basicAck(envelope.getDeliveryTag(), false); } } }
消费者代码
package com.huang.rabbitmqapi.ack; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:05 */ public class Consumer { public static void main(String[] args) throws Exception { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); String exchangeName = "test_ack_exchange"; String queueName = "test_ack_queue"; String routingKey = "ack.#"; channel.exchangeDeclare(exchangeName, "topic", true, false, null); channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, exchangeName, routingKey); // 手工签收 必须要关闭 autoAck = false channel.basicConsume(queueName, false, new MyConsumer(channel)); } }
生产者代码
package com.huang.rabbitmqapi.ack; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.util.HashMap; import java.util.Map; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:07 */ public class Producer { public static void main(String[] args) throws Exception { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); String exchange = "test_ack_exchange"; String routingKey = "ack.save"; for(int i =0; i<5; i ++){ Map<String, Object> headers = new HashMap<String, Object>(); headers.put("num", i); AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() .deliveryMode(2) .contentEncoding("UTF-8") .headers(headers) .build(); String msg = "Hello RabbitMQ ACK Message " + i; channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes()); } } }
TTL消息
TTL是Time To Live的缩写,也就是生存时间
RabbitMQ支持消息的过期时间,在消息发送时可以进行指定
RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过了队列的超时时间配置,那么消息自动的清除
纯控制台操作(演示TTL队列消息特点)
针对队列,只要是这个队列的消息,就只有这么长的存活时间
注意:主要针对消息设置,跟交换机、队列、消费者设置毫无关系
消费端代码
package com.huang.rabbitmqapi.ttl; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.QueueingConsumer; import java.util.Map; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:05 */ public class Consumer { public static void main(String[] args) throws Exception { //1 创建一个ConnectionFactory, 并进行配置 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); //2 通过连接工厂创建连接 Connection connection = connectionFactory.newConnection(); //3 通过connection创建一个Channel Channel channel = connection.createChannel(); //4 声明(创建)一个队列 String queueName = "test001"; channel.queueDeclare(queueName, true, false, false, null); //5 创建消费者 QueueingConsumer queueingConsumer = new QueueingConsumer(channel); //6 设置Channel channel.basicConsume(queueName, true, queueingConsumer); while(true){ //7 获取消息 QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery(); String msg = new String(delivery.getBody()); System.err.println("消费端: " + msg); Map<String, Object> headers = delivery.getProperties().getHeaders(); System.err.println("headers get my1 value: " + headers.get("my1")); //Envelope envelope = delivery.getEnvelope(); } } }
生产端代码
package com.huang.rabbitmqapi.ttl; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.util.HashMap; import java.util.Map; /** * @auther 宇晨 * @site www.cnblogs.com/bf6rc9qu/ * @company * @create 2019-12-28 17:05 */ public class Procuder { public static void main(String[] args) throws Exception { //1 创建一个ConnectionFactory, 并进行配置 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.188.131"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/"); //2 通过连接工厂创建连接 Connection connection = connectionFactory.newConnection(); //3 通过connection创建一个Channel Channel channel = connection.createChannel(); Map<String, Object> headers = new HashMap<>(); headers.put("my1", "111"); headers.put("my2", "222"); AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() .deliveryMode(2) .contentEncoding("UTF-8") .expiration("10000") .headers(headers) .build(); //4 通过Channel发送数据 for(int i=0; i < 5; i++){ String msg = "Hello RabbitMQ!"; //1 exchange 2 routingKey channel.basicPublish("", "test001", properties, msg.getBytes()); } //5 记得要关闭相关的连接 channel.close(); connection.close(); } }