當需要調用遠端計算機的函數并等待結果,這模式通常被稱為遠程過程調用或RPC。
BasicProperties:
消息屬性 這AMQP協議預先確定了消息中的14個屬性。常用的有:
-
deliveryMode
將一個消息標記為持久化(值為2)或者瞬態的(其他值)。之前發送隊列消息時的用法:channel.basicPublish("", "task_queue",MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());
contentType
用來描述媒體類型的編碼。例如常常使用的JSON編碼:application/json。replyTo
通常來命名回收隊列的名字。correlationId
對RPC加速響應請求是很有用的。
相關性ID(Correlation Id):
為每一個RPC請求創建一個回收隊列,這個效率十分低下。每一個客戶端創建一個單一的回收隊列。 用Correlation Id判斷隊列中的響應是屬于哪個請求的。
當在回收隊列中接收消息時,依據這個屬性值,刻意將每個響應匹配到對應的請求上。如果是未知的Correlation Id值,就丟棄這個消息,因為它不屬于任何一個我們的請求。
請求RPC服務:
服務端:
package testrabbitmq;
import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.BasicProperties;
/**
* Created by zzhblh on 2016/8/29.
*/
public class RPCServer {
private static final String RPC_QUEUE_NAME = "rpc_queue";
private static int fib(int n) throws Exception {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
public static void main(String[] argv) throws Exception
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平分發機制
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
System.out.println(" [x] Awaiting RPC requests");
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
BasicProperties props = delivery.getProperties();
BasicProperties replyProps = new BasicProperties
.Builder()
.correlationId(props.getCorrelationId())
.build();
String message = new String(delivery.getBody());
int n = Integer.parseInt(message);
System.out.println(" [.] fib(" + message + ")");
String response = "" + fib(n);
channel.basicPublish( "", props.getReplyTo(), replyProps, response.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
客戶端:
package testrabbitmq;
import com.rabbitmq.client.*;
/**
* Created by zzhblh on 2016/8/29.
*/
public class RPCClient {
private Connection connection;
private Channel channel;
private String requestQueueName = "rpc_queue";
private String replyQueueName;
private QueueingConsumer consumer;
public RPCClient() throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
replyQueueName = channel.queueDeclare().getQueue();
consumer = new QueueingConsumer(channel);
channel.basicConsume(replyQueueName, true, consumer);
}
public String call(String message) throws Exception {
String response = null;
String corrId = java.util.UUID.randomUUID().toString();
AMQP.BasicProperties props = new AMQP.BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueName)
.build();
channel.basicPublish("", requestQueueName, props, message.getBytes());
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
if (delivery.getProperties().getCorrelationId().equals(corrId)) {
response = new String(delivery.getBody());
break;
}
}
return response;
}
public void close() throws Exception {
connection.close();
}
}
- contentType
用來描述媒體類型的編碼。例如常常使用的JSON編碼,這是一個好的慣例,設置這個屬性為:application/json。 - replyTo
通常來命名回收隊列的名字。 - correlationId
對RPC加速響應請求是很有用的。
相關性ID(Correlation Id):
為每一個RPC請求創建一個回收隊列,這個效率十分低下。每一個客戶端創建一個單一的回收隊列。 用Correlation Id判斷隊列中的響應是屬于哪個請求的。
當在回收隊列中接收消息時,依據這個屬性值,刻意將每個響應匹配到對應的請求上。如果是未知的Correlation Id值,就丟棄這個消息,因為它不屬于任何一個我們的請求。
請求RPC服務:
服務端:
package testrabbitmq;
import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.BasicProperties;
/**
* Created by zzhblh on 2016/8/29.
*/
public class RPCServer {
private static final String RPC_QUEUE_NAME = "rpc_queue";
private static int fib(int n) throws Exception {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
public static void main(String[] argv) throws Exception
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平分發機制
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
System.out.println(" [x] Awaiting RPC requests");
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
BasicProperties props = delivery.getProperties();
BasicProperties replyProps = new BasicProperties
.Builder()
.correlationId(props.getCorrelationId())
.build();
String message = new String(delivery.getBody());
int n = Integer.parseInt(message);
System.out.println(" [.] fib(" + message + ")");
String response = "" + fib(n);
channel.basicPublish( "", props.getReplyTo(), replyProps, response.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
客戶端:
package testrabbitmq;
import com.rabbitmq.client.*;
/**
* Created by zzhblh on 2016/8/29.
*/
public class RPCClient {
private Connection connection;
private Channel channel;
private String requestQueueName = "rpc_queue";
private String replyQueueName;
private QueueingConsumer consumer;
public RPCClient() throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
replyQueueName = channel.queueDeclare().getQueue();
consumer = new QueueingConsumer(channel);
channel.basicConsume(replyQueueName, true, consumer);
}
public String call(String message) throws Exception {
String response = null;
String corrId = java.util.UUID.randomUUID().toString();
AMQP.BasicProperties props = new AMQP.BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueName)
.build();
channel.basicPublish("", requestQueueName, props, message.getBytes());
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
if (delivery.getProperties().getCorrelationId().equals(corrId)) {
response = new String(delivery.getBody());
break;
}
}
return response;
}
public void close() throws Exception {
connection.close();
}
}
TTL
- Queue Message TTL
設置某隊列中所有消息的TTL,通過在 queue.declare 中設置 x-message-ttl 參數,可以控制被 publish 到 queue 中的 message 被丟棄前能夠存活的時間。值得注意的是,當一個 message 被路由到多個 queue 中時,他們之間不會互相影響。
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);//存活時間最大為 60 秒
channel.queueDeclare("myqueue", false, false, false, args);
- Per-Message TTL
TTL 設置可以具體到每一條 message 本身,只要在通過 basic.publish 命令發送 message 時設置 expiration 字段。expiration 字段必須為字符串類型。
AMQP.BasicProperties properties = new AMQP.BasicProperties();
properties.setExpiration("60000");
channel.basicPublish("myexchange", "routingkey", properties, message.getBytes());
對于第一種設置隊列TTL屬性的方法,一旦消息過期,就會從隊列中抹去。而第二種方法里,即使消息過期,也不會馬上從隊列中抹去,在過期 message 到達 queue 的頭部時被真正的丟棄。
- Queue TTL
queue 被自動刪除前可以處于未使用狀態的時間。未使用的意思是 queue 上沒有任何 consumer ,queue 沒有被重新聲明,并且在過期時間段內未調用過 basic.get 命令。在服務器重啟后,持久化的 queue(本來還未過期的) 的超時時間將重新計算。
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-expires", 1800000);
channel.queueDeclare("myqueue", false, false, false, args);
DLX(死信)
利用DLX,當消息在一個隊列中變成死信后,它能被重新publish到另一個Exchange,這個Exchange就是DLX。消息變成死信一向有以下幾種情況:
- 消息被拒絕(basic.reject or basic.nack)并且requeue=false
- 消息TTL過期
- 隊列達到最大長度
DLX也是一下正常的Exchange同一般的Exchange沒有區別,它能在任何的隊列上被指定,實際上就是設置某個隊列的屬性,當這個隊列中有死信時,RabbitMQ就會自動的將這個消息重新發布到設置的Exchange中去,進而被路由到另一個隊列, publish可以監聽這個隊列中消息做相應的處理,這個特性可以彌補RabbitMQ 3.0.0以前支持的immediate參數中的向publish確認的功能。
channel.exchangeDeclare("some.exchange.name", "direct");
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "some.exchange.name");
channel.queueDeclare("myqueue", false, false, false, args);
你也可以為這個DLX指定routing key,如果沒有特殊指定,則使用原隊列的routing key
args.put("x-dead-letter-routing-key", "some-routing-key");
http://memorynotfound.com/produce-consume-rabbitmq-spring-json-message-queue/
http://blog.csdn.net/jiao_fuyou/article/details/22923935