Arguments (dependent on the proxy itself)A vswitch can have two statuses: durable and transient ). Persistent switches will still exist after the broker is restarted, while temporary switches will not (they need to be declared again after the proxy is launched again ). However, not all application scenarios require persistent switches.
The following two types of switches are described in this article: directly connected switch (the switch type used in the previous examples) and slice switch (the switch type to be used in this article)
Direct Connect Switch
Direct exchange can use the routing key carried by messages to deliver messages to the corresponding queue. The unicast route (unicast routing) used to process messages. It can also process multicast routes.
How does it work?
- Bind a queue to a vswitch and specify a route key for the binding)
- When a message carrying a route key of R is sent to a directly connected switch, the switch routes it to a queue with the same bound value of R.
Directly Connected switches are often used to distribute tasks cyclically to multiple workers. When doing so, you must understand that the load balancing of messages occurs between consumers, instead of queue.
Example of a directly connected vswitch:
Slice Switch
A fan-sector switch (funout exchange) routes messages to all queues bound to it, regardless of the bound route key (routing key ). The Slice switch is used to process the broadcast routing (broadcast routing) of messages ).
Slice switches are suitable for the following scenarios because they ship messages to all queues bound to them:
- Large-scale multi-user online (MMO) games can be used to handle global events such as ranking updates.
- Sports News websites can use it to distribute score updates to mobile clients in near real time.
- The distribution system uses it to broadcast various statuses and Configuration updates.
- In group chat, it is used to distribute messages to users involved in group chat. (AMQP does not have the built-in presence concept, so XMPP may be a better choice)
Sector switch legend
Create exchange
Channel. ExchangeDeclare (exchange: "log_exchange", // exchange name
Type: ExchangeType. Fanout, // exchange type
Durable: false,
AutoDelete: false,
Arguments: null );
Temporary queue
In the previous examples, we have specified a name for each famous queue, because we want the consumer to point to the correct queue. When we want to share a queue between producers and consumers, it is very important to name the queue.
However, the log system we want to implement only wants to get all the messages, and is only interested in the messages currently being transmitted, and does not care about the queue name. To meet our needs, there are two things to do:
No matter when we connect to RabbitMQ, we need a new empty queue. To achieve this goal, we can use a random number to create a queue or let the server provide us with a random name.
Once the consumer is disconnected from RabbitMQ, the queue accepted by the consumer should be deleted automatically.
Create temporary queue
// Create an unnamed new message queue,
QueueDeclareOk queue = channel. QueueDeclare (queue: "", // queue name, which is automatically assigned when it is null
Durable: false,
Exclusive: false,
AutoDelete: true, // automatic deletion. If the queue does not have any subscribed consumers, the queue will be automatically deleted. This type of queue applies to temporary queues.
Arguments: null );
// Or
// Queue = channel. QueueDeclare ();
Bind
We have created a fan-type switch (fanout) and a queue. Now we need to tell the switch how to send messages to our queue. The connection between the switch and the queue is called binding)
Relationship between creating a vswitch and a queue
// The fan-sector switch (funout exchange) routes messages to all queues bound to it, regardless of the bound routing key)
// Fanout exchange does not need to specify the routing key.
// Notify exchange of the message queues to which messages are sent by binding.
Channel. QueueBind (queue: queueName, exchange: EXCHANGE_NAME, routingKey: ROUTING_KEY, arguments: null );
Complete code:
Producer Pub_SubProducer.cs
Using System;
Using System. Collections. Generic;
Using System. Linq;
Using System. Text;
Using System. Threading. Tasks;
Using RabbitMQ. Client;
Namespace RabbitMQProducer
{
Public class Pub_SubProducer
{
Const string EXCHANGE_NAME = "log_exchange ";
Const string ROUTING_KEY = "";
// Send messages directly to the vswitch
Public static void Publish ()
{
Var factory = new ConnectionFactory ()
{
HostName = "127.0.0.1"
};
Using (var connection = factory. CreateConnection ())
{
Using (IModel channel = connection. CreateModel ())
{
Channel. ExchangeDeclare (exchange: EXCHANGE_NAME, // exchange name
Type: ExchangeType. Fanout, // exchange type
Durable: false,
AutoDelete: false,
Arguments: null );
Parallel. For (1,100, item =>
{
String message = $ "log Content {DateTime. Now. ToString ()}";
Channel. BasicPublish (exchange: EXCHANGE_NAME, routingKey: ROUTING_KEY, basicProperties: null, body: Encoding. UTF8.GetBytes (message ));
Console. WriteLine (message );
});
Console. WriteLine ("Press [enter] to exit .");
Console. ReadLine ();
}
}
}
}
}
Consumer Pub_SubConsumer.cs
Using RabbitMQ. Client;
Using System;
Using System. Collections. Generic;
Using System. Linq;
Using System. Text;
Using System. Threading. Tasks;
Using RabbitMQ. Client. Events;
Using System. IO;
Namespace RabbitMQConsumer
{
Public class Pub_SubConsumer
{
Const string EXCHANGE_NAME = "log_exchange ";
Const string ROUTING_KEY = "";
// Output to the screen
Public static void Subscribe ()
{
Var factory = new ConnectionFactory ()
{
HostName = "127.0.0.1"
};
Using (var connection = factory. CreateConnection ())
{
Using (IModel channel = connection. CreateModel ())
{
Channel. ExchangeDeclare (exchange: EXCHANGE_NAME, // exchange name
Type: ExchangeType. Fanout, // exchange type
Durable: false,
AutoDelete: false,
Arguments: null );
// Create an unnamed new message queue,
QueueDeclareOk queue = channel. QueueDeclare (queue: "", // queue name, which is automatically assigned when it is null
Durable: false,
Exclusive: false,
AutoDelete: true, // automatic deletion. If the queue does not have any subscribed consumers, the queue will be automatically deleted. This type of queue applies to temporary queues.
Arguments: null );
// Or
// Queue = channel. QueueDeclare ();
String queueName = queue. QueueName;
// The fan-sector switch (funout exchange) routes messages to all queues bound to it, regardless of the bound routing key)
// Fanout exchange does not need to specify the routing key.
// Notify exchange of the message queues to which messages are sent by binding.
Channel. QueueBind (queue: queueName, exchange: EXCHANGE_NAME, routingKey: ROUTING_KEY, arguments: null );
EventingBasicConsumer consumer = new EventingBasicConsumer (channel );
Consumer. Received + = (sender, args) =>
{
String message = Encoding. UTF8.GetString (args. Body );
Console. WriteLine (message );
};
Channel. BasicConsume (queue: queueName, noAck: true, consumer: consumer );
Console. WriteLine ("Press [enter] to exit .");
Console. ReadLine ();
}
}
}
/// <Summary>
/// Output to file
/// </Summary>
Public static void SubscribeFile ()
{
Var factory = new ConnectionFactory ()
{
HostName = "127.0.0.1"
};
Using (var connection = factory. CreateConnection ())
{
Using (IModel channel = connection. CreateModel ())
{
Channel. ExchangeDeclare (exchange: EXCHANGE_NAME, // exchange name
Type: ExchangeType. Fanout, // exchange type
Durable: false,
AutoDelete: false,
Arguments: null );
// Create an unnamed new message queue,
QueueDeclareOk queue = channel. QueueDeclare (queue: "", // queue name, which is automatically assigned when it is null
Durable: false,
Exclusive: false,
AutoDelete: true, // automatic deletion. If the queue does not have any subscribed consumers, the queue will be automatically deleted. This type of queue applies to temporary queues.
Arguments: null );
// Or
// Queue = channel. QueueDeclare ();
String queueName = queue. QueueName;
// The fan-sector switch (funout exchange) routes messages to all queues bound to it, regardless of the bound routing key)
// Fanout exchange does not need to specify the routing key.
// Notify exchange of the message queues to which messages are sent by binding.
Channel. QueueBind (queue: queueName, exchange: EXCHANGE_NAME, routingKey: ROUTING_KEY, arguments: null );
EventingBasicConsumer consumer = new EventingBasicConsumer (channel );
Consumer. Received + = (sender, args) =>
{
String message = Encoding. UTF8.GetString (args. Body );
// Write logs to the txt file
Using (StreamWriter writer = new StreamWriter (@ "c: \ log \ log.txt", true, Encoding. UTF8 ))
{
Writer. WriteLine (message );
Writer. Close ();
}
Console. WriteLine (message );
};
Channel. BasicConsume (queue: queueName, noAck: true, consumer: consumer );
Console. WriteLine ("Press [enter] to exit .");
Console. ReadLine ();
}
}
}
}
}
Run the above instance code to find that each subscriber instance can get the same content.