public partial class Producer : Form
{
RabbitmqHelper helper = new RabbitmqHelper();
IModel chan;
public Producer()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Publish(richTextBox1.Text);
}
private void Declare()
{
chan = helper.GetChannel();
chan.ExchangeDeclare("TestExchange", ExchangeType.Topic, true, false, null);
chan.QueueDeclare("TestQueue1", true, false, false, null);
chan.QueueBind("TestQueue1", "TestExchange", "key.#");
}
private void Publish(string msg)
{
if (chan == null)
{
Declare();
}
IBasicProperties msgProps = chan.CreateBasicProperties();
msgProps.DeliveryMode = 2;//持久化消息
chan.BasicPublish("TestExchange", "key", msgProps, Encoding.UTF8.GetBytes(msg));
}
private void Producer_FormClosing(object sender, FormClosingEventArgs e)
{
helper.CloseConsumer();
}
}
public partial class Consumer : Form
{
RabbitmqHelper helper = new RabbitmqHelper();
public Consumer()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
SetText("start consuming \n");
IModel chan = helper.GetChannel();
chan.ExchangeDeclare("TestExchange", ExchangeType.Topic, true, false, null);
chan.QueueDeclare("TestQueue1", true, false, false, null);
chan.QueueBind("TestQueue1", "TestExchange", "key.#");
EventingBasicConsumer consumer = new EventingBasicConsumer(chan);
consumer.Received += (model, ea) =>
{
string message = Encoding.UTF8.GetString(ea.Body);
SetText(message);
chan.BasicAck(ea.DeliveryTag, false);//手动确认
};
chan.BasicConsume("TestQueue1", false, consumer);//开始消费 false为手动确认
}
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.richTextBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.richTextBox1.AppendText(text + "\n");
this.richTextBox1.Refresh();
}
}
private void Consumer_FormClosing(object sender, FormClosingEventArgs e)
{
SetText("stop consuming \n");
Thread.Sleep(1000);
helper.CloseConsumer();
}
}
public class RabbitmqHelper
{
ConnectionFactory factory;
IConnection conn;
IModel chan;
public RabbitmqHelper()
{
factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.UserName = "guest";
factory.Password = "guest";
}
public IModel GetChannel()
{
if (conn == null || !conn.IsOpen)
{
conn = factory.CreateConnection();
}
if (chan == null || chan.IsClosed)
{
chan = conn.CreateModel();
}
return chan;
}
//如果关闭Consumer不主动关闭channel和connection,进程仍然在后台运行,还能正常进行消费
public void CloseConsumer()
{
if (chan != null || chan.IsOpen)
{
chan.Close();
}
if (conn != null || conn.IsOpen)
{
conn.Close();
}
}
}
tips:
每CreateConnection()则会新建一个connection,每CreateModel()则会新建一个信道
exchange和queue为多对多关系,同一个exchange和queue也可以根据不同的规则进行多次绑定
topic
a.*=>a.、a.123as
a.#=>a、a.、a.123、a.123.、a.123.213
Testqueue1
Testqueue2
Testqueue3
当chan.BasicPublish("TestExchange2", "key", msgProps, Encoding.UTF8.GetBytes(msg));时,只有queue1和queue2收到了消息。
如果把参数routingkey由"key"改为"key."或者"key.123"则会只有queue2和queue3收到消息
如果把参数routingkey由改为"key.123.123"则会只有queue2收到消息
已声明的exchange和queue再用不同参数声明会报错
消息会发到指定exchange,根据routingkey的规则,分发到该exchange下的符合routingkey规则的队列中保存。
队列只有在和exchange绑定后才会收到消息。例如给exchange发送完消息后,exchange分发给队列,不管消息是否被消费,新绑定到exchange的队列无法收到之前发送的消息。(消息是存在队列中,而不是exchange中,exchange只是分发器,hub,一拖二,一拖三。不存储消息)