RabbitMQのクライアントの開発
このブログマインドマップ:
RabbitMQのクラスが持つキーChannel
、Connection
、ConnectionFactory
、Consumer
というように、スルー動作のAMQPプロトコル側面Channel
インタフェース(C#は今IModel
インタフェース)
Connection
有効にするにはChannel
、RabbitMQの開発作業は、基本的に焦点を当てているConnetion
とChannel
2クラスが拡大しました。
Benpian完全なプロセスを経て動作を説明します:
- 接続
- 宣言と結合スイッチとキュー
- メッセージを送ります
- 消費者ニュース
- メッセージ受信確認
- 接続を閉じます
1.接続のRabbitMQ
1.1接続とチャンネルを作成します
RabbitMQの接続パラメータが与えられると、複数の作成Channel
:
//创建连接工厂,提供用户名等信息
ConnectionFactory factory = new ConnectionFactory();
factory.UserName = "test";
factory.Password = "123465";
factory.VirtualHost = "virtual";
factory.HostName = "127.0.0.1";
factory.Port = 2020;
//创建连接
IConnection connection = factory.CreateConnection();
//创建多个信道
var channel1 = connection.CreateModel();
var channel2 = connection.CreateModel();
あなたが見ることができるConnection
複数作成することができますChannel
が、私たちはそれぞれのスレッドを開く必要がありますChannel
ので、Channel
インスタンスがされているスレッドセーフでない、同時状況がネットワーク上に表示されてインターレース誤った通信フレームをもたらすことができる、それはメカニズムを確認するために、送信者に影響を与えます(パブリッシャ確認)操作。
作成したChannel
後に、私たちはメッセージを送信または受信するためにそれを使用することができます。
1.2接続状態決意
IsOpenプロパティ決意
Channle
そして、Connection
それが持っているIsOpen
プロパティを、プロパティを判断することができるChannel
とConnection
開くかどうか、それは推奨されない本番環境で使用するためIsOpen
のプロパティが依存しているため、CloseReason
(閉鎖の理由)このプロパティがある場合、null
に代わってChannel
、またはConnection
開いています。
しかし、必要が注意
あります:
CloseReason
それは競争なので、使用生じることがIsOpen
判断をConnection
してChannel
いるオンラインの信頼性がありません。
コードのこの部分の下に見て:
if (channel.IsOpen) //此刻IsOpen为true,判断通过
{
//做某些事
//...
//....
//做某些事的这段时间之间,channel发生离线
//也就是说CloseReason不为null,执行下面的代码程序就会报错
channel.BasicQos(10, 10, false); //因为离线,所以执行失败,可能导致程序奔溃
}
上のRabbitMQのIsOpen
要約:
上のRabbitMQのCloseReason
要約:
try / catchの決意
私たちが使用している場合Channel
、それは閉じた状態にある場合、時間を、RabbitMQのようながスローされることがありRabbitMQ.Client.Exceptions.OperationInterruptedException
、エラーをして、のように私たちはこれらのゲームをすることができキャプチャする必要があります。
try
{
//...
channel.BasicQos(10,10,false);
}
catch (RabbitMQ.Client.Exceptions.OperationInterruptedException e)
{
channel.Close();
throw e;
}
2.文のスイッチとキュー
スイッチExchange
およびキューがQueue
ありAMQP
、我々は宣言(に持っていたときにそれらを使用し、それらを使用する時に必ずそれが存在することを確認するために、高レベルのアプリケーションのニーズを建てモジュールDelare
)それら。
スイッチおよびキューを宣言するためにどのように次のコードに示します:
//声明一个持久化、非自动删除、Direct类型的交换机
channel.ExchangeDeclare(
exchange: EXCHANGE_NAME,
type: ExchangeType.Direct,
durable: true,
autoDelete: false,
arguments: null);
//声明一个默认的队列(非持久化、排他的、自动删除),名称由RabbitMQ自动生成
string queueName = channel.QueueDeclare().QueueName;
//绑定队列和交换机
channel.QueueBind(queueName, EXCHANGE_NAME, ROUTING_KEY);
もちろん、あなたは自分のニーズに応じて、デフォルトのキューの設定パラメータを宣言することはできません。
2.1 ExchangeDeclare方法の詳細
方法1:ExchangeDeclare
void ExchangeDeclare(
string exchange,
string type,
bool durable,
bool autoDelete,
IDictionary<string, object> arguments);
詳細なパラメータ:
- 為替:スイッチの名前
- タイプ:スイッチタイプは、あなたが列挙型を使用することができます
ExchangeType
セットを - 耐久性:持続性の真の表現に設定すると、永続的、非永続的な反対を示しているかどうか。永続性情報は、スイッチのスイッチを失ったことはありません、サーバーが再起動された場合でも、ハードドライブの存在かもしれません。
- 自動削除:自動的に削除するかどうかを、真の表現が自動的に削除します。自動的に持つすべてのバインディングの後にキューまたは少なくとも一つのスイッチとバインドを削除する前に、前提条件を削除するアンバンドリング、自動的に削除実行されます。
- 引数:他のパラメータの構造、指定された拡張属性
方法2:ExchangeDeclareNoWait
void ExchangeDeclareNoWait(
string exchange,
string type,
bool durable,
bool autoDelete,
IDictionary<string, object> arguments);
この方法は、ステートメントを特徴とする必要がないサービス・ノード(ブローカー)は肯定応答を返し、ExchangeDeclare
メソッド宣言スイッチAMQPコマンドを返すためにサーバが必要Exchange.Declare-Ok
スイッチはサーバで作成されています。
使用ExchangeDeclareNoWait
方法は、switch文の直後に表示されることがありますが、サーバーはすべてのシナリオのための特別な理由がない、この状況を作成していない可能性があり、お勧めしません、このメソッドのステートメント・スイッチを使用しました。
方法3:ExchangeDeclarePassive
void ExchangeDeclarePassive(string exchange);
スイッチがすでに正常に戻る、その後、サービスノードに存在する場合に例外がスローされないがある場合、このスイッチは有無を検出するための方法を指定し、404 channel exception
チャンネルが閉鎖されている間は、この方法は実用的なアプリケーションでは非常に一般的です。
私たちは、スイッチの有無を検出するためのユーティリティメソッドを書くことができます。
public bool IsExchangeExisted(IModel channel, string exchangeName)
{
bool result = false;
try
{
channel.ExchangeDeclarePassive(exchangeName);
result = true;
}
catch (Exception e) //不存在则抛出错误
{
//no - op
}
return result;
}
削除スイッチ
次のように消去方法は次のとおりです。
void ExchangeDelete(string exchange, bool ifUnused);
対応するパラメータ:
- 為替:為替名
- ifUnuserは:スイッチを設定するかどうか使用されていない場合を削除するには、真のセットにのみ使用されていない。このスイッチの場合には削除されます。
もちろんありますExchangeDeleteNoWait
方法は、同様ExchangeDeleteしかしセットがtrueにNOWAIT 。
void ExchangeDeleteNoWait(string exchange, bool ifUnused);
2.2 QueueDeclare方法の詳細
キューを宣言するために、次の4つの方法があります。
方法1:QueueDeclare
QueueDeclareOk QueueDeclare(
string queue,
bool durable,
bool exclusive,
bool autoDelete,
IDictionary<string, object> arguments);
方法2:DECLAREデフォルトのキュー名は、RabbitMQのによって生成されます
public static QueueDeclareOk QueueDeclare(this IModel model, string queue = "", bool durable = false, bool exclusive = true, bool autoDelete = true, IDictionary<string, object> arguments = null);
方法3:QueueDeclarePassive
QueueDeclareOk QueueDeclarePassive(string queue);
方法4:QueueDeclareNoWait
void QueueDeclareNoWait(
string queue,
bool durable,
bool exclusive,
bool autoDelete,
IDictionary<string, object> arguments);
switch文は、パラメータの役割に似て前にパラメータがあること、それは、ノートを詳しく説明しません。exclusive
排他的:キューが真の排他的なキューに設定されているセットの排他的かどうか、
キューが排他的にキューに設定されている場合は、キューは、次のような特徴を持っています
-
その可視接続の最初の宣言
-
切断された場合、自動的に(でも、永続的に設定されている場合)を削除
別の接続(第一の点についてConnection
)同じキューを宣言することができず、代わりに、チャネル(チャネル)の違いのみ接続(コネクション)、すなわち、同じ接続(Connection
異なるチャネルの)は(Channel
)同時にこのキューにアクセスすることができます。
あなたが排他的なキューを別の接続やアクセスで再宣言してみてください(例えば、公開など、消費)した場合、リソースはエラーをロックされます。
ESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'UserLogin2'
第二の点について:RabbitMQの設定に関係なく自動的にキューが(永続として宣言されているかどうかの、キューを削除しますDurable =true
)。言い換えれば、クライアントは、排他的なプログラムになるだろう宣言キュー場合でも、Durable
呼接続限り、Close
方法やクライアントプログラムを終了し、RabbitMQのは、キューを削除します。注意は、それが切断するまでの時間であることを、チャネルが切断されません。前者は実際にと一致している、とだけでなく、相違接続チャネル。
これは、同時に送信されたクライアントキューおよびアプリケーションシナリオを読んだメッセージに適用されます。
注意点
生産者と消費者が使用することができQueueDeclare
た後に、キューを宣言するのではなく、消費者が別のキューに同じチャンネルに加入している場合、あなたはキューを宣言することはできません、あなたのサブスクリプションをキャンセルする必要があり、その後、チャネルは「転送」モードに設定されていますキューを宣言します。
キューを削除します。
QueueDelete
//Returns the number of messages purged during queue deletion
uint QueueDelete(string queue, bool ifUnused, bool ifEmpty);
これは、戻りuint
時に明確なメッセージを削除するキューの数を表し、値型を。
複数のパラメータが異なるスイッチを削除しifEmpty
、このパラメータがtrueに設定されている場合、示しキュー情報の蓄積がないことの場合は削除することができます。
QueuePurgeこのメソッドは、キュー自体を削除せずにキューにメッセージを削除するために使用されます
uint QueuePurge(string queue);
バインディング3.
3.1とスイッチキュー間の結合
スイッチの方法次の2つのキューは、結合、および以前の方法と同様に定義することができます。
QueueBind
void QueueBind(
string queue,
string exchange,
string routingKey, I
Dictionary<string, object> arguments);
QueueBindNoWait
void QueueBindNoWait(
string queue,
string exchange,
string routingKey,
IDictionary<string, object> arguments);
3.2スイッチとスイッチの間の結合
2台のスイッチ間結合、キューと結合を切り替えることができるだけでなく、二つのスイッチを結合した後、メッセージができsource
、スイッチによって転送destination
ある程度、スイッチdestination
スイッチはキューとみなすことができます。
ExchangeBind
void ExchangeBind(
string destination,
string source,
string routingKey, I
Dictionary<string, object> arguments);
ExchangeBindNoWait
void ExchangeBindNoWait(
string destination,
string source,
string routingKey,
IDictionary<string, object> arguments);
これらのパラメータは:
- 先:宛先スイッチ
- ソース:ソーススイッチ
下の画像に表示される結合関係をどのように達成するかを考えてみて
コード:
//声明源交换机
channel.ExchangeDeclare(
exchange: "source",
type: ExchangeType.Direct);
//声明目的交换机
channel.ExchangeDeclare(
exchange: "destination",
type: ExchangeType.Fanout);
//声明队列
channel.QueueDeclare(
queue: "queue");
//绑定
channel.ExchangeBind(destination: "destination", source: "source", routingKey: "routingKey");
channel.QueueBind(queue: "queue", exchange: "destination", "routingKey");
//发布消息至源交换机
channel.BasicPublish(
exchange: "source",
routingKey: "routingKey",
basicProperties: channel.CreateBasicProperties(),
Encoding.UTF8.GetBytes("message"));
4.メッセージの送信と消費
4.1メッセージを送ります
使用Channel
のBasicPublish
方法は、メッセージを送信することができます。
コードで示されるように、例えば、いわゆる「myExchange」スイッチに「ハロー」メッセージを送信します。
channel.BasicPublish(
exchange: "myExchange",
routingKey: "routingKey",
basicProperties: channel.CreateBasicProperties(),
Encoding.UTF8.GetBytes("hello"));
我々は見てBasicPublish
の方法:
void BasicPublish(
string exchange,
string routingKey,
bool mandatory,
IBasicProperties basicProperties,
byte[] body)
類似した他のパラメータ、主な関心事mandatory
とbasicProperties
これら2つのパラメータ:
- 必須:
- basicProperties:メッセージ属性の基本セット、属性メンバーを複数含みます
basicProperties
あなたは、私たちが今(メッセージの優先度を定義し、デフォルトの属性が前に使用されている、特定のメッセージ属性を定義することができますpriority
)とコンテンツタイプ:
var prop = channel.CreateBasicProperties();
prop.Priority = 2; //优先级设置为2
prop.ContentType = "text/plan"; //content-type
//发布消息至源交换机
channel.BasicPublish(
exchange: "source",
routingKey: "routingKey",
basicProperties: prop,
Encoding.UTF8.GetBytes("message"));
また、有効期限を定義することができますexpiration
)(:
var prop = channel.CreateBasicProperties();
prop.Expiration = "60000";
また、ヘッダを定義することができますheader
)(:
var prop = channel.CreateBasicProperties();
Dictionary<string, object> header =
new Dictionary<string, object>();
header.Add("localtion", "here");
header.Add("time", "today");
prop.Headers = header;
4.2消費者のニュース
2つのメッセージRabbitMQの消費の方法があります:プッシュモード(Push
)とプルモードは、( Pull
)。プロトコル対応AMQPプッシュモードであるBasic.Consume
AMQPプロトコルに対応するプルモードですBasic.Get
。
プッシュモード
プッシュモードでは、我々はによってできる持续订阅
、消費者へのメッセージの道を。
使用BasicConsume
我々が設定できるという方法を、キューを購読します:
string BasicConsume(
string queue,
bool autoAck,
string consumerTag,
bool noLocal,
bool exclusive,
IDictionary<string, object> arguments,
IBasicConsumer consumer);
どのパラメータには:
- キュー:キュー名
- AUTOACK:かどうかを設定し、自動的に確認するために(falseにお勧めのセット、すなわち消費者の手動確認)
- consumerTag:消費者ラベルは、複数の消費者を区別するために使用します
- NOLOCAL:プロデューサによって送信されたメッセージとの接続セットは、消費者(自家製かどうか)は、この接続によって消費することができます
- 引数:設定された他のパラメータの消費者
- 消費者:(書き換えIBasicConsumerメソッドを継承することができる)、消費者のコールバック・クラス
私たちは見て継承されたIBasicConsumer接口
消費者のクラスをどのようにされています。
public class TestConsumer : IBasicConsumer
{
public IModel Model { get; private set; }
public TestConsumer(IModel channel)
{
this.Model = channel;
}
public event EventHandler<ConsumerEventArgs> ConsumerCancelled;
/// <summary>
/// 消费者显式(basicCancel)以外的其它原因被取消时订阅式调用,例如:队列被删除
/// </summary>
/// <param name="consumerTag"></param>
public void HandleBasicCancel(string consumerTag)
{
throw new NotImplementedException();
}
/// <summary>
/// 消费者通过显式(basicCancel)取消订阅后调用
/// </summary>
/// <param name="consumerTag"></param>
public void HandleBasicCancelOk(string consumerTag)
{
Console.WriteLine("Cancel-OK");
//throw new NotImplementedException();
}
/// <summary>
/// 在其他方法前都会调用此方法
/// </summary>
/// <param name="consumerTag"></param>
public void HandleBasicConsumeOk(string consumerTag)
{
Console.WriteLine("Ok");
}
/// <summary>
/// 接收到任一消息时调用
/// </summary>
public void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body)
{
Console.WriteLine("message arrive");
//....消费消息....
//消费完后手动Ack
// Model.BasicAck(deliveryTag, false);
}
/// <summary>
/// Channel和Connetion关闭的时候调用
/// </summary>
/// <param name="model"></param>
/// <param name="reason"></param>
public void HandleModelShutdown(object model, ShutdownEventArgs reason)
{
throw new NotImplementedException();
}
}
たとえば、私たちの例では、クライアントが呼び出すBasicCancel()
メソッドを:
channel.BasicCancel("MyConsumerTag");
この場合、この方法は、順番に呼び出されますHandleBasicConsumeOk
==>HandleBasicCancelOk
それに注意してください。
そしてプロデューサーとして、消費者は、クライアントに考慮する必要がある线程安全
問題を。これらのクライアントの消費者はcallback
に割り当てられるChannel
消費者は安全ないくつかのクライアントを呼び出すことができる手段は異なるスレッド、阻塞
例えば、方法channel.QueueDeclare
、 channel.BasicCancel
でも(あなたが長い時間で消費者HandleBasicDeliver方法でブロックされ、影響を与えない場合クライアント・コール)
一般的には、各点であるChannel
消費者への対応はもちろん、またもChannel
より多くの消費者保護手段が、これがあれば、問題を持っていますChannel
消費者が実行された、それは他の消費者をブロックします。
プルモード
プルモードのchannel.BasicGet
戻り値は、単一のメッセージの取得方法BasicGetResult
(のGetResponse)
BasicGet方法:
BasicGetResult BasicGet(string queue, bool autoAck);
次のコードは、プルモードそのキーコードです。
BasicGetResult result = channel.BasicGet("MyQueue", false);
Console.WriteLine(result.Body);
channel.BasicAck(result.DeliveryTag, false);
それに注意してください。
ただ、単一のメッセージを取得するのではなく、サブスクライブプルモードを使用することをお勧めしますが、私たちがすることはできません続けたいBasicGet
置くWhile
の代わりにループをBasic.Consume
、その意志严重影响
のRabbitMQのパフォーマンス。
するには、高いスループットを実現する、プッシュモードを使用することをお勧めします。
消費者側の認識と拒否
メッセージの信頼性を向上させるために、RabbitMQのは、提供メッセージ確認機構。
我々は、サブスクリプションキューがあり、前にautoAck
自動確認モードを設定するかどうかを定義するためのパラメータは、それをすることが推奨されfalse
、どちらも自動的に確認されません。我々が設定されている場合、この方法では、RabbitMQのは、明示的メッセージ内のメモリ(またはディスク)から削除した後に確認信号を返信させていただきます、(実際には削除マークし、後に実際に削除を打つ)true
、RabbitMQのメッセージは、セットを入れて自動的に送信しましたとにかかわらず、最終消費者が実際にこれらのメッセージを受け取ったかどうか、認め、その後、メモリやディスクから削除されました。
メッセージ受信確認
BasicAck方法
void BasicAck(ulong deliveryTag, bool multiple);
パラメータ:
- deliveryTag:各メッセージを区別するためのメッセージを一意の番号
- 複数:バッチかどうかを確認するために、
設定した場合autoAck
のパラメータfalse
の時間を、RabbitMQのはあったであろう等待
消費者のメッセージを明示的に呼び出すBasic.Ack
コマンドは、削除して、次のメッセージを送信します。
この場合、メッセージキューは、2つの部分に分割されます。
- これは、消費者に配信されているが、消費者は、メッセージ確認信号を受信していません
- ニュースの消費者への配信を待っています
RabbitMQのは、消費者の確認信号を受信したことがない、と消費者は、この時点で切断された場合、それは消費者が、元消費することも可能であることはもちろん、消費者に次の配信を待って、再び列にRabbitMQのメッセージングを手配します人。
ウェブ上では、メッセージキューの2種類を見ることができます:
- レディ:配達を待っているメッセージの数
- Unacked:メッセージの数が掲載されていないことを確認し
RabbitMQのは、個人消費は、メッセージがあなたが必要とするかどうかを判断するのに長い時間がかかることができるように再配達に基礎1つのメッセージのみをされています。消費者が切断するかどうか
メッセージは拒否しました
確認のメッセージがあり、そこになります拒否のメッセージを、メッセージが拒否されたコマンドはBasic.Reject
、消費者が対応する呼び出すことができChannel.BasicReject
RabbitMQのニュースを伝えることを拒否した方法を。
BasicReject方法
void BasicReject(ulong deliveryTag, bool requeue);
パラメータ:
- deliveryTag:メッセージ番号、区別各メッセージ
- 再キューイング:再入力欄にか
場合はrequeue
セットtrue
、そしてこれらは、サブスクリプション・コンシューマ・キューを送信するために次の、後列にRabbitMQのメッセージングを拒否されます。設定した場合false
、RabbitMQのは、すぐにこのメッセージを削除し、再び支出されることはありません。
注意Basic.Reject
メッセージをのみ拒否することができますが、大部分を必要とする場合は、メッセージを拒否し、あなたが使用することができBasic.Nack
、このコマンドを、に対応するクライアントメソッドchannel.BasicNack
void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
この方法は、以上であるBasicReject
1つの以上のパラメータmultiple
をtrueに設定した場合、一括拒否メッセージは、それがこの拒否することを示しているかどうかをセットにdeliveryTag
現在のラベルの前には、ニュースを確認するために、すべての消費者ではありません。
パラメータをオンにすることができます `False``をrequeue`不能キュー機能に設定されている(1下記参照)。
6.近くに接続
明示的に近いです
アプリケーションが終了した後、私たちはする必要があり、接続クローズリソースを解放します:
channel.Close();
connection.Close();
明示的に近い、それは良い習慣ですが、必須ではありません、ときconnection
に閉じ、channel
自動的にシャットダウンします。
ライフサイクル
connection
そして、channel
それは同じように使用されているネットワーク管理、内部エラーと明示的にクローズ接続に失敗したので、彼らは、ライフサイクルの同じ種類を持っています:
- オープン:現在のオブジェクトの代わりに開いた状態では、使用することができます
- クロージング:閉じた状態。このオブジェクトは明示的に呼び出す方法(シャットダウン)ので、この状態ではあるだろうに知らせるために、閉じられた、要求が退場し、閉鎖動作の完了を待っています
- 休館:すでにオフ
connection
そして、channel
最終的になるClosed
(たとえば、ネットワーク異常、または異常なクライアントへの明示的な呼び出しとして)にかかわらず、原因となるものの状態、
綿密なモニタリング
私たちはを購読することができConnectionShutdown
、接続が閉じられたときに、イベント、それがこのイベントをトリガします
private static void Connection_ConnectionShutdown(object sender, ShutdownEventArgs e)
{
//TODO:日志记录等
}
ShutdownEventArgs
パラメータは閉鎖の理由が挙げられるShutdownInitiator
閉位置特定情報が含まれています。
また、使用することができ connection.CloseReason
、その後、接続が使用可能な状態で残っている場合は、閉じたリターンに関連する情報を取得する方法をnull
ShutdownEventArgs reason = connection.CloseReason