【EasyNetQ笔记】Publish/Subscribe模式(发布订阅)

介绍

EasyNetQ支持的最简单的消息模式是发布/订阅.这个模式是一个极好的方法用来解耦消息提供者消费者。消息发布者只要简单的对世界说“这里有些事发生” 。它不关心有没有人监听,或者接收者是谁,或者接收者在那里。我们能够添加和移除特定类型的消息的订阅者,不需发布者做任何的重新配置。
假如你开始去发布消息,而没有任何订阅者曾经定义此消息,那么这个消息就简单的消失了。因为发布者不会创建队列
一个EasyNetQ订阅者订阅一种消息类型。通过调用Subcribe方法一旦对一个类型设置了订阅,一个持久化队列就会在RabbitMQ broker上被创建,这个类型的任何消息都会被发送到这个队列上。订阅者无论什么时候连接上,RabbitMQ都将会将消息从队列中发送给订阅者。

消息发布(PublishPublishAsync

EasyNetQ支持最简单的消息模式是发布和订阅。发布消息后,任意消费者可以订阅该消息,也可以多个消费者订阅。

var _bus = RabbitHutch.CreateBus("host=xxxxxx;port=5672;virtualHost=my_vhost;username=admin;password=admin;timeout=30;publisherConfirms=true");//连接字符串末尾不要加";"
var message = new MyMessage { Text = "Hello Rabbit" };
_bus.Publish<MyMessage>(message);

消息订阅(SubscribeSubscribeAsync

EasyNetQ提供了消息订阅,当调用Subscribe方法时候,EasyNetQ会创建一个用于接收消息的队列,不过与消息发布不同的是,消息订阅增加了一个参数,subscribe_id

_bus.Subscribe<MyMessage>("subscribe_id", myMessage => {
    lbxMessage1.Invoke(new Action(() => { lbxMessage1.Items.Add(myMessage.Text); }));
});

第一个参数是订阅id,另外一个是delegate参数,用于处理接收到的消息。
subscribe_id参数很重要,假如开发者用同一个subscribe_id订阅了同一种消息类型两次或者多次,RabbitMQ会以轮训的方式给每个订阅的队列发送消息。

//不同的subscribe_id将会创建两个队列,消息同时发给两个队列
private void btn_subscribe11_Click(object sender, EventArgs e)
{
    _bus.Subscribe<MyMessage>("", myMessage => {
        lbxMessage1.Invoke(new Action(() => { lbxMessage1.Items.Add(myMessage.Text); }));
    });
}

private void btnSubscribe22_Click(object sender, EventArgs e)
{
    _bus.Subscribe<MyMessage>("s1", myMessage => {
        lbxMessage2.Invoke(new Action(() => { lbxMessage2.Items.Add(myMessage.Text); }));
    });
}

如果用不同的subscribeid订阅同一种消息类型,它们都会收到该消息。RabbitMQ根据消息类型subscribe_id创建队列,不同的subscribe_id会创建不同的队列。最终生成的队列名是“{queueName}_{subsicribe_id}”,queueName是消息类型上的Attribute[Queue(queueName:"TestQueue")]指定
以上案例中生成的队列名是'TestQueue'、TestQueue_s1;
交换器是根据消息类型创建的,交换器名称是:“{消息类型全名},{所在程序集名称}”,类型是“topic”,绑定是“#”

异步订阅

在收到消息处理消息时候,不要占用太多的时间,会影响消息的处理效率,所以,遇到占用长时间的处理方法,最好用异步处理。代码如下:

bus.SubscribeAsync<MyMessage>("subscribe_async_test", message => 
    new WebClient().DownloadStringTask(new Uri("http://localhost:1338/?timeout=500"))
        .ContinueWith(task => 
            Console.WriteLine("Received: '{0}', Downloaded: '{1}'", 
                message.Text, 
                task.Result)));

取消订阅

所有subscribe方法都返回一个ISubscriptionResult。它包含描述底层IConsumerIExchangeIQueue的属性,如果需要,可以使用高级API进一步操作这些属性。

var subscriptionResult = _bus.Subscribe<MyMessage>("s1", myMessage =>
{
    lbxMessage1.Invoke(new Action(() => { lbxMessage1.Items.Add(myMessage.Text); }));
});
subscriptionResult.Dispose();

这将阻止EasyNetQ从队列中消耗并关闭消费者的频道。

非泛型发布订阅

在运行期间,你怎么去发现消息类型?例如:你可能有一些系统加载外部插件,希望能订阅他们自己的消息类型。EasyNetQ为了这个目标提供了非泛型的发布和订阅方法。
加using

using EasyNetQ.NonGeneric;

提供如下非泛型的发布订阅方法:

//订阅
public static IDisposable Subscribe(
    this IBus bus,
    Type messageType,
    string subscriptionId,
    Action<object> onMessage,
    Action<ISubscriptionConfiguration> configure)    
public static IDisposable SubscribeAsync(    
    this IBus bus,     
    Type messageType,     
    string subscriptionId,     
    Func<object, Task> onMessage,     
    Action<ISubscriptionConfiguration> configure)
 
//发布
 public static void Publish(
    this IBus bus, 
    Type messageType, 
    object message, 
    string topic)
 public static Task PublishAsync(
    this IBus bus, 
    Type messageType, 
    object message, 
    string topic)

基于Topic路由

基于Topic的路由,允许订阅者基于多个标准过滤消息。订阅时可以通过WithTopic方法设置binding_key,这样只有匹配的Routing_key才会路由到该队列

//===================发布====================
_bus.PublishAsync<MyMessage>(message,"X.A")
//===================订阅====================
private void btnSubscribe_Click(object sender, EventArgs e)
{
    _bus.Subscribe<OrderMessage>("s1", myMessage =>
    {
        lbxMessage1.Invoke(new Action(() => { lbxMessage1.Items.Add(myMessage.Text); }));
    }, config => config.WithTopic("*.A"));
}

多态发布和订阅

您可以订阅一个接口,然后发布该接口的实现。
我们来看一个例子。 我有一个接口IAnimal和两个实现猫和狗:

public interface IAnimal
{
    string Name { get; set; }
}

public class Cat : IAnimal
{
    public string Name { get; set; }
    public string Meow { get; set; }
}

public class Dog : IAnimal
{
    public string Name { get; set; }
    public string Bark { get; set; }
}

我可以订阅IAnimal并获得猫和狗类:

bus.Subscribe<IAnimal>("polymorphic_test", @interface =>
    {
        var cat = @interface as Cat;
        var dog = @interface as Dog;

        if (cat != null)
        {
            Console.Out.WriteLine("Name = {0}", cat.Name);
            Console.Out.WriteLine("Meow = {0}", cat.Meow);
        }
        else if (dog != null)
        {
            Console.Out.WriteLine("Name = {0}", dog.Name);
            Console.Out.WriteLine("Bark = {0}", dog.Bark);
        }
        else
        {
            Console.Out.WriteLine("message was not a dog or a cat");
        }
    });

让我们发布一只猫和一只狗:

var cat = new Cat
{
    Name = "Gobbolino",
    Meow = "Purr"
};

var dog = new Dog
{
    Name = "Rover",
    Bark = "Woof"
};

bus.Publish<IAnimal>(cat);
bus.Publish<IAnimal>(dog);

请注意,我必须明确指定我发布IAnimal。 EasyNetQ使用“发布”和“订阅”方法中指定的泛型类型将发布路由到订阅。

posted @ 2020-09-05 22:41  .Neterr  阅读(1058)  评论(0编辑  收藏  举报