rabbitMq學習筆記(未完)

木坦坦發表於2016-01-05

連線

AMQP支援在一個TCP連線上啟用多個MQ通訊channel,每個channel都可以被應用作為通訊流。每個AMQP程式至少要有一個連線和一個channel。

        ConnectionFactory factory = new ConnectionFactory();
        Address[] addrs = new Address[] { new Address("192.168.1.10"),new Address("192.168.1.11"), new  Address("192.168.1.12") };
        factory = new ConnectionFactory();
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/myvhost");
        factory.setConnectionTimeout(10);

        try {
            Connection connection =factory.newConnection(addrs);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Channel channel = connection.createChannel();  

每個channel都被分配了一個整數標識,自動由Connection()類的.createChannel()方法維護。或者,你可以使用.createChannel(x)來指定channel標識,其中x是你想要使用的channel標識。通常情況下,推薦使用.createChannel()方法來自動分配channel標識,以便防止衝突。

佇列宣告

現在我們已經有了一個可以用的連線和channel。現在,我們的程式碼將分成兩個應用,生產者(producer)和消費者(consumer)。我們先建立一個消費者程式,他會建立一個叫做“test_push”的佇列和一個叫“sorting_room”的交換機:

String queue = "test_push";
boolean durable = true;
boolean exclusive = false;
boolean autoDelete = false;
Map<java.lang.String,java.lang.Object> arguments = null;
channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments);

String exchange="sorting_room";
String type="direct";
//boolean durable=true;
boolean auto_delete=false;
//Map<java.lang.String,java.lang.Object> arguments = null;
channel.exchangeDeclare(exchange, type, durable,auto_delete, arguments);

這段程式碼首先,它建立了一個名叫“test_push”的佇列,它是durable的(重啟之後會重新建立),並且最後一個消費者斷開的時候不會自動刪除(auto_delete=False)。在建立durable的佇列(或者交換機)的時候,將auto_delete設定成false是很重要的,否則佇列將會在最後一個消費者斷開的時候消失,與durable與否無關。如果將durable和auto_delete都設定成True,只有尚有消費者活動的佇列可以在RabbitMQ意外崩潰的時候自動恢復。

(你可以注意到了另一個標誌,稱為“exclusive”。如果設定成True,只有建立這個佇列的消費者程式才允許連線到該佇列。這種佇列對於這個消費者程式是私有的)。

還有另一個交換機宣告,建立了一個名字叫“sorting_room”的交換機。auto_delete和durable的含義和佇列是一樣的。但是,.exchangeDeclare() 還有另外一個引數叫做type,用來指定要建立的交換機的型別(如前面列出的): fanout, direct 和 topic.

繫結

到此為止,你已經有了一個可以接收訊息的佇列和一個可以傳送訊息的交換機。不過我們需要建立一個繫結,把它們連線起來。

//String queue="test_push";
//String exchange="sorting_room";
String routing_key="jason";
channel.queueBind(queue, exchange, routing_key);

這個繫結的過程非常直接。任何送到交換機“sorting_room”的具有路由鍵“jason” 的訊息都被路由到名為“test_push” 的佇列。

接收訊息呼叫

現在,你有兩種方法從佇列當中取出訊息。
第一個是呼叫chan.basic_get(),主動從佇列當中拉出下一個訊息(如果佇列當中沒有訊息,channel.basicGet()會返回None, 因此下面程式碼當中response.getBody() 會在沒有訊息的時候崩掉):

boolean autoAck = false;
GetResponse response = channel.basicGet(queue, autoAck);
String message = new String(response.getBody(), "UTF-8");
System.out.println(message);
boolean ack = true;
channel.basicAck(response.getEnvelope().getDeliveryTag(), ack);  

但是如果你想要應用程式在訊息到達的時候立即得到通知怎麼辦?這種情況下不能使用channel.basicGet(),你需要用chan.basic_consume()註冊一個新訊息到達的回撥。

def recv_callback(msg):
    print 'Received: ' + msg.body
chan.basic_consume(queue='po_box', no_ack=True,
callback=recv_callback, consumer_tag="testtag")
while True:
    chan.wait()
chan.basic_cancel("testtag")

chan.wait() 放在一個無限迴圈裡面,這個函式會等待在佇列上,直到下一個訊息到達佇列。chan.basic_cancel() 用來登出該回撥函式。引數consumer_tag 當中指定的字串和chan.basic_consume() 註冊的一直。在這個例子當中chan.basic_cancel() 不會被呼叫到,因為上面是個無限迴圈…… 不過你需要知道這個呼叫,所以我把它放在了程式碼裡。

需要注意的另一個東西是no_ack引數。這個引數可以傳給chan.basic_get()和chan.basic_consume(),預設是false。當從佇列當中取出一個訊息的時候,RabbitMQ需要應用顯式地回饋說已經獲取到了該訊息。如果一段時間內不回饋,RabbitMQ會將該訊息重新分配給另外一個繫結在該佇列上的消費者。另一種情況是消費者斷開連線,但是獲取到的訊息沒有回饋,則RabbitMQ同樣重新分配。如果將no_ack 引數設定為true,則py-amqplib會為下一個AMQP請求新增一個no_ack屬性,告訴AMQP伺服器不需要等待回饋。但是,大多數時候,你也許想要自己手工傳送回饋,例如,需要在回饋之前將訊息存入資料庫。回饋通常是通過呼叫chan.basic_ack()方法,使用訊息的delivery_tag屬性作為引數。參見chan.basic_get() 的例項程式碼。

傳送呼叫

個生產者。下面的程式碼示例表明如何將一個簡單訊息傳送到交換區“sorting_room”,並且標記為路由鍵“jason” :

msg = amqp.Message("Test message!")
msg.properties["delivery_mode"] = 2
chan.basic_publish(msg,exchange="sorting_room",routing_key="jason")

你也許注意到我們設定訊息的delivery_mode屬性為2,因為佇列和交換機都設定為durable的,這個設定將保證訊息能夠持久化,也就是說,當它還沒有送達消費者之前如果RabbitMQ重啟則它能夠被恢復。

關閉

剩下的最後一件事情(生產者和消費者都需要呼叫的)是關閉channel和連線:

chan.close()
conn.close()

相關文章