RabbitMQ Java 基本API

API

一、exchangeDeclare 交換器聲明

    /**
     * Declare an exchange, via an interface that allows the complete set of
     * arguments.
     * @see com.rabbitmq.client.AMQP.Exchange.Declare
     * @see com.rabbitmq.client.AMQP.Exchange.DeclareOk
     * @param exchange the name of the exchange
     * @param type the exchange type
     * @param durable true if we are declaring a durable exchange (the exchange will survive a server restart)
     * @param autoDelete true if the server should delete the exchange when it is no longer in use
     * @param internal true if the exchange is internal, i.e. can't be directly
     * published to by a client.
     * @param arguments other properties (construction arguments) for the exchange
     * @return a declaration-confirm method to indicate the exchange was successfully declared
     * @throws java.io.IOException if an error is encountered
     */
    Exchange.DeclareOk exchangeDeclare(String exchange,
                                              String type,
                                              boolean durable,
                                              boolean autoDelete,
                                              boolean internal,
                                              Map<String, Object> arguments) throws IOException;

exchange : 交換器名稱
type : 交換器類型 參考:https://my.oschina.net/LucasZhu/blog/1838105
durable : true if we are declaring a durable exchange (the exchange will survive a server restart) 是否耐用,如果設置爲耐用的,當重啓服務,交換器還將繼續存在,否則會消失
autoDelete :true if the server should delete the exchange when it is no longer in use 當建立一次鏈接之後,如果在沒有其他隊列鏈接此交換器,則交換器會自動刪除。同 隊列聲明中autoDelete
internal : true if the exchange is internal, i.e. can't be directly  published to by a client. 是否是內部交換器,如果是內部交換器,則不能與外部client-客戶端進行直接連接,例如死信隊列中設置的exchange。
arguments  如下: 

二、channel.queueDeclare 聲明隊列

channel.queueDeclare(QUEUE_NAME, false, false, false, null);
/**
     * Declare a queue
     * @see com.rabbitmq.client.AMQP.Queue.Declare
     * @see com.rabbitmq.client.AMQP.Queue.DeclareOk
     * @param queue the name of the queue
     * @param durable true if we are declaring a durable queue (the queue will survive a server restart)
     * @param exclusive true if we are declaring an exclusive queue (restricted to this connection)
     * @param autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use)
     * @param arguments other properties (construction arguments) for the queue
     * @return a declaration-confirm method to indicate the queue was successfully declared
     * @throws java.io.IOException if an error is encountered
     */
    Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
                                 Map<String, Object> arguments) throws IOException;

1. queue:聲明隊列名稱
2. durable :true if we are declaring a durable queue (the queue will survive a server restart) 如果我們聲明爲持久化 , 則在重新啓動的時候該隊列還將存在。
3. exclusive true if we are declaring an exclusive queue (restricted to this connection) 排他隊列,限於創建此隊列的鏈接進行操作
4. autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use) 當建立一次鏈接之後,如果在沒有其他消費者鏈接此隊列,則隊列會自動刪除。
5.隊列如下相關的屬性信息:

三、basicPublish 基礎發送消息:

channel.basicPublish("", QUEUE_NAME, null, (message+"1").getBytes("UTF-8"));

/**
     * Publish a message.
     *
     * Publishing to a non-existent exchange will result in a channel-level
     * protocol exception, which closes the channel.
     *
     * Invocations of <code>Channel#basicPublish</code> will eventually block if a
     * <a href="http://www.rabbitmq.com/alarms.html">resource-driven alarm</a> is in effect.
     *
     * @see com.rabbitmq.client.AMQP.Basic.Publish
     * @see <a href="http://www.rabbitmq.com/alarms.html">Resource-driven alarms</a>.
     * @param exchange the exchange to publish the message to
     * @param routingKey the routing key
     * @param props other properties for the message - routing headers etc
     * @param body the message body
     * @throws java.io.IOException if an error is encountered
     */
    void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

/**
 * Publish a message.
 *
 * Publishing to a non-existent exchange will result in a channel-level
 * protocol exception, which closes the channel.
 *
 * Invocations of <code>Channel#basicPublish</code> will eventually block if a
 * <a href="http://www.rabbitmq.com/alarms.html">resource-driven alarm</a> is in effect.
 *
 * @see com.rabbitmq.client.AMQP.Basic.Publish
 * @see <a href="http://www.rabbitmq.com/alarms.html">Resource-driven alarms</a>.
 * @param exchange the exchange to publish the message to
 * @param routingKey the routing key
 * @param mandatory true if the 'mandatory' flag is to be set
 * @param immediate true if the 'immediate' flag is to be
 * set. Note that the RabbitMQ server does not support this flag.
 * @param props other properties for the message - routing headers etc
 * @param body the message body
 * @throws java.io.IOException if an error is encountered
 */
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
        throws IOException;

exchange:交換器名稱,如果爲空字符串"",則直接發送消息到隊列,不經過交換器。
routingKey : 爲經過交換器中消息的路由規則,如果沒有交換器(""),則它爲隊列的名稱。#匹配0個或多個單詞,*匹配一個單詞  在topic exchange做消息轉發用
props : 設置消息的屬性信息,如消息頭,消息的超時時間等等。
body : 消息體
mandatory : true 如果exchange根據自身類型和消息routeKey無法找到一個符合條件的queue,那麼會調用basic.return方法將消息返回給生產者。false : 出現上述情形broker會直接將消息扔掉。
immediate : true 如果exchange在將消息route到queue(s)時發現對應的queue上沒有消費者,那麼這條消息不會放入隊列中。當與消息routeKey關聯的所有queue(一個或多個)都沒有消費者時,該消息會通過basic.return方法返還給生產者。

簡單來說:mandatory標誌告訴服務器至少將該消息route到一個隊列中,否則將消息返還給生產者;immediate標誌告訴服務器如果該消息關聯的queue上有消費者,則馬上將消息投遞給它,如果所有queue都沒有消費者,直接把消息返還給生產者,不用將消息入隊列等待消費者了。

四、queueBind 隊列和交換器的綁定

channel.queueBind(queueName, EXCHANGE_NAME, severity);

    /**
     * Bind a queue to an exchange, with no extra arguments.
     * @see com.rabbitmq.client.AMQP.Queue.Bind
     * @see com.rabbitmq.client.AMQP.Queue.BindOk
     * @param queue the name of the queue
     * @param exchange the name of the exchange
     * @param routingKey the routine key to use for the binding
     * @return a binding-confirm method if the binding was successfully created
     * @throws java.io.IOException if an error is encountered
     */
    Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException;

queue : 隊列名稱。
exchange : 交換器名稱。
routingKey  : 這裏的routingKey也叫bindingKey 是隊列和交換器的直接綁定規則。

五、channel.basicConsume

推模式獲取消息(消息中間件主動將消息推送給消費者)

推模式將消息提前推送給消費者,消費者必須設置一個緩衝區緩存這些消息。好處很明顯,消費者總是有一堆在內存中待處理的消息,所以效率高。缺點是緩衝區可能會溢出

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(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
	}
};
channel.basicConsume(queueName, true, consumer);

/**
 * Start a non-nolocal, non-exclusive consumer, with
 * a server-generated consumerTag.
 * @param queue the name of the queue
 * @param autoAck true if the server should consider messages
 * acknowledged once delivered; false if the server should expect
 * explicit acknowledgements
 * @param callback an interface to the consumer object
 * @return the consumerTag generated by the server
 * @throws java.io.IOException if an error is encountered
 * @see com.rabbitmq.client.AMQP.Basic.Consume
 * @see com.rabbitmq.client.AMQP.Basic.ConsumeOk
 * @see #basicConsume(String, boolean, String, boolean, boolean, Map, Consumer)
 */
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;

round-robin(推-輪詢)

推的方式採用的輪詢(round-robin)的方式,這裏輪詢的意思不是消息的輪詢,而是推送次數的輪詢,推送可能一次推送一條消息也可能一次推送多條消息。實現方式如上。

chanel.basicQos()

/**
 * Request specific "quality of service" settings.
 *
 * These settings impose limits on the amount of data the server
 * will deliver to consumers before requiring acknowledgements.
 * Thus they provide a means of consumer-initiated flow control.
 * @see com.rabbitmq.client.AMQP.Basic.Qos
 * @param prefetchSize maximum amount of content (measured in
 * octets) that the server will deliver, 0 if unlimited
 * @param prefetchCount maximum number of messages that the server
 * will deliver, 0 if unlimited
 * @param global true if the settings should be applied to the
 * entire channel rather than each consumer
 * @throws java.io.IOException if an error is encountered
 */
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;

/**
 * Request a specific prefetchCount "quality of service" settings
 * for this channel.
 *
 * @see #basicQos(int, int, boolean)
 * @param prefetchCount maximum number of messages that the server
 * will deliver, 0 if unlimited
 * @param global true if the settings should be applied to the
 * entire channel rather than each consumer
 * @throws java.io.IOException if an error is encountered
 */
void basicQos(int prefetchCount, boolean global) throws IOException;

/**
 * Request a specific prefetchCount "quality of service" settings
 * for this channel.
 *
 * @see #basicQos(int, int, boolean)
 * @param prefetchCount maximum number of messages that the server
 * will deliver, 0 if unlimited
 * @throws java.io.IOException if an error is encountered
 */
void basicQos(int prefetchCount) throws IOException;

prefetchSize:0 
prefetchCount:會告訴RabbitMQ不要同時給一個消費者推送多於N個消息,即一旦有N個消息還沒有ack,則該consumer將block掉,直到有消息ack
global:true\false 是否將上面設置應用於channel,簡單點說,就是上面限制是channel級別的還是consumer級別
備註:據說prefetchSize 和global這兩項,rabbitmq沒有實現,暫且不研究

basicQos 中 prefetchSize 參數通過消息的總字節數來限制隊列推送消息的速度
prefetchSize 與 prefetchCount 可以同時設置,達到任何一個限制,則隊列暫停推送消息
global 參數表示前兩個參數的作用域,true 表示限制是針對信道的,false 表示限制是針對消費者的(我還沒試過一個信道支持多個消費者的例子)
可以對同一個信道同時設置 global 爲 true 和 false 的 Qos,表示隊列要考慮每個消費者的限制,同時還要考慮整個信道的限制

fair dispatch(推-公平分發)

上面輪詢推策略方式在服務器性能不均的情況下,帶來消息在某個或某些消費者節點大量堆積的問題。
怎樣才能做到按照每個消費者的能力分配消息呢,聯合使用Qos和Acknowledge就可以做到。

channel.basicQos(1);
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.printf(" [    %2$s<===](%1$s) %3$s\n", name, QUEUE_NAME, message);
        try {
            TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
        }
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
};
channel.basicConsume(QUEUE_NAME, false, consumer);

核心:1.設置通道最多堆積(擁有)一條未提交的消息,當消費完成並且ack之後 channel再進行第二條消息的接收。2.設置ack方式爲手動通知。

basicQos 方法設置了當前信道最大預獲取(prefetch)消息數量爲1,消息從隊列異步推送給消費者,消費者的ack也是異步發送給隊列,從隊列的角度去看,總是會有一批消息已推送但尚未獲得ack確認。Qos(Quality of Service)的prefetchCount參數就是來限制這批未確認消息的數量。設爲1時,隊列只有在收到消費者發回的上一條消息 ack 確認後,纔會向該消費者發送下一條消息。prefetchCount 的默認值爲0,即沒有限制,隊列會將所有消息儘快發給消費者。

六、channel.basicGet 

拉模式獲取消息(消費者主動從消息中間件拉取消息)

拉模式在消費者需要時纔去消息中間件拉取消息,這段網絡開銷會明顯增加消息延遲,降低系統吞吐量。

while(true) {
	GetResponse response = channel.basicGet(queueName, true);
	if(response == null) {
		System.out.println("Get Nothing!");
		TimeUnit.MILLISECONDS.sleep(1000);
	} else {
		String message = new String(response.getBody(), "UTF-8");
		System.out.printf(" [    %2$s<===](%1$s) %3$s\n", "Receiver", queueName, message);
		TimeUnit.MILLISECONDS.sleep(500);
        channel.basicAck(response.getEnvelope().getDeliveryTag(),false);
	}
}

/**
 * Retrieve a message from a queue using {@link com.rabbitmq.client.AMQP.Basic.Get}
 * @see com.rabbitmq.client.AMQP.Basic.Get
 * @see com.rabbitmq.client.AMQP.Basic.GetOk
 * @see com.rabbitmq.client.AMQP.Basic.GetEmpty
 * @param queue the name of the queue
 * @param autoAck true if the server should consider messages
 * acknowledged once delivered; false if the server should expect
 * explicit acknowledgements
 * @return a {@link GetResponse} containing the retrieved message data
 * @throws java.io.IOException if an error is encountered
 */
GetResponse basicGet(String queue, boolean autoAck) throws IOException;

GetResponse basicGet(String queue, boolean autoAck) throws IOException;
非阻塞方法,如果隊列中沒有數據會返回null 

七、channel.basicAck();

/**
 * Acknowledge one or several received
 * messages. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
 * or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method
 * containing the received message being acknowledged.
 * @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}
 * @param multiple true to acknowledge all messages up to and
 * including the supplied delivery tag; false to acknowledge just
 * the supplied delivery tag.
 * @throws java.io.IOException if an error is encountered
 */
void basicAck(long deliveryTag, boolean multiple) throws IOException;

deliveryTag : 該消息的index 用來標識信道中投遞的消息。RabbitMQ 推送消息給 Consumer 時,會附帶一個 Delivery Tag,以便 Consumer 可以在消息確認時告訴 RabbitMQ 到底是哪條消息被確認了。RabbitMQ保證在每個信道中,每條消息的DeliveryTag 從1開始遞增。
multiple : 是否批量 true : 將一次性ack所有小於deliveryTag的消息。false 確認當前消息。

八、channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, true);

/**
 * Reject one or several received messages.
 *
 * Supply the <code>deliveryTag</code> from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
 * or {@link com.rabbitmq.client.AMQP.Basic.GetOk} method containing the message to be rejected.
 * @see com.rabbitmq.client.AMQP.Basic.Nack
 * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
 * @param multiple true to reject all messages up to and including
 * the supplied delivery tag; false to reject just the supplied
 * delivery tag.
 * @param requeue true if the rejected message(s) should be requeued rather
 * than discarded/dead-lettered
 * @throws java.io.IOException if an error is encountered
 */
void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException;

deliveryTag : 該消息的index
multiple : 是否批量. true 將一次性拒絕所有小於deliveryTag的消息。
requeue : 被拒絕的是否重新入隊。

九、channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);

/**
 * Reject a message. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
 * or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method
 * containing the received message being rejected.
 * @see com.rabbitmq.client.AMQP.Basic.Reject
 * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
 * @param requeue true if the rejected message should be requeued rather than discarded/dead-lettered
 * @throws java.io.IOException if an error is encountered
 */
void basicReject(long deliveryTag, boolean requeue) throws IOException;

deliveryTag : 該消息的index 。
requeue : 被拒絕的是否重新入隊。

channel.basicNack 與 channel.basicReject 的區別在於basicNack可以拒絕多條消息,而basicReject一次只能拒絕一條消息

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章