1. 程式人生 > >.Net下RabbitMQ的使用(7) -- 遠端過程呼叫RPC

.Net下RabbitMQ的使用(7) -- 遠端過程呼叫RPC

RPC是在計算中是一種常見的模式,是通常我要用訊息佇列來實現RPC有3個關鍵點:

1. 服務的定址

2. 訊息的接收

3. 訊息的關聯

在RabbitMQ的.net客戶端裡,提供了2個類:SimpleRpcClient 和 SimpleRpcServer 來讓我們方便的開發RPC應用。

因為RabbitMQ的RPC一定是基於佇列的,所以在客戶端和服務端都需要要一個各自的佇列,客戶端的佇列用來接收服務回覆的資料,服務端的佇列用來快取需要呼叫的服務的Request。這些請求一定不單單從一個客戶端而來。

服務的地址是公開的,地址也就是一個佇列,客戶端只要把引數放進這個服務端監控的佇列中就可以了。服務端每次從佇列中獲得的一個數據即是一次呼叫。

服務不單單為一個客戶端服務,所以在客戶端呼叫的引數裡面,每次都需要加上需要回復的佇列名字,也就是要告訴服務端方法呼叫結果需要放在這個佇列裡。客戶端不僅可以直接把佇列的名字放在引數裡面還可以給出一個路由,例如這樣的格式:exchangeType://exchangeName/routingKey。 exchangeType就是exchange 的型別,四選一。這個佇列裡面的資料就是我呼叫服務的返回值。每一個客戶端都有一個這樣的返回值佇列。你可以用如下命令檢視佇列的情況。

image

客戶端也許呼叫比較頻繁,或者是不是呼叫一個方法,返回值不同,又或者服務端每次返回的時間都不一樣,不能保證回覆的順序和客戶端呼叫的順序一致。那麼程式是怎麼保證這個返回值就是這次呼叫的結果呢?RabbitMQ使用了CorrelationId,他是一個屬性,在每次呼叫的時候,客戶端會生成一個這樣的屬性來代表這次呼叫,Id唯一的。當然服務端也會把這個Id放在返回值裡面一同返回,這個客戶端就可用這個CorrelationId來配對一次呼叫。

如果在呼叫過程中,CorrelationId沒有被匹配到,那麼返回值這個訊息就會被丟棄,而不會出丟擲異常。

一個服務端往往不只提供一個服務(方法),在一個佇列中也許會有不同方法的Request,這種就需要根據每次Request裡面帶的引數來判斷是呼叫具體哪個方法的。一個服務提供多個方法常見也有顯而易見的弊端也,如果某些方法服務處理的時間比較長的話就需要其他呼叫等待。如果服務端採用多執行緒處理,那麼在客戶端呼叫的時候,每個服務端的方法都要一個專門的SimpleRpcClient 去呼叫。也就是說,不同的方法需要有不同的callback佇列,這樣才能避免客戶端會猶豫匹配CorrelationId不對而丟棄訊息。

RPC呼叫的順序如下:

1. 在客戶端初始化的時候,也就是SimpleRpcClient類初始化的時候,它會隨機的建立一個callback佇列,用於存放服務的返回值,這個佇列是exclusive的。連線斷開就沒有了。

2. 客戶端在傳送Request的時候,會加上兩個引數:ReplyTo和CorrelationId,前者用於告訴服務返回值放在哪個佇列裡面(callback的佇列名)或路由,後者用於配對每次的Request。這兩個屬性都放在客戶端傳送訊息的附帶的IBasicProperties字典中。

3. 把訊息放入服務的監控佇列裡,訊息裡面自然有呼叫方法的引數。

4. 服務在所監控的佇列中收到資料後,進行運算,並把返回值放入到客戶端指定的callback佇列中去。

5. 客戶端在傳送完Request後,便去自己建立的callback佇列監聽,如果獲得到資料,則檢視裡面的CorrelationId,如果和呼叫Request一致,則返回結果。

SimpleRpcClient 和 SimpleRpcServer 只是RabbitMQ .net客戶端的一個實現。知道了原理,我們也可以自己實現RPC的功能。

服務的負載均衡

服務端往往有一個佇列來接收客戶端的請求,我們記得在這一篇中,我們看到了RabbitMQ內建的消費者負載均衡功能,那麼對於RPC的服務端,我們是不是也適合呢?答案是肯定的。因為我們RPC服務端其實也就是一個Worker,我們只要執行多個監控同一個佇列的服務端就可以了。所有的被監控佇列中的請求都平均的分配到不同的服務端去了。