簡單易用的.NET免費開源RabbitMQ操作元件EasyNetQ解析
對於目前大多的.NET專案,其實使用的技術棧都是差不多,估計現在很少用控制元件開發專案的了,畢竟一大堆問題。對.NET的專案,目前比較適合的架構ASP.NET MVC,ASP.NET WebAPI,ORM
(較多Dapper.NET
或者其擴充套件,稍大一些的專案用EF
等等),為了提高速度也會採用快取(.NET
自帶的Memcache
,或者Redis
),請求較多的專案,使用Nginx
做負載均衡和使用佇列等等。
上面簡單的介紹一下.NET
的專案的技術架構,具體的技術根據具體的需求做出選擇。介紹到佇列,很多人都會很熟悉,例如MSMQ
,RabbitMQ
等等佇列。既然需要使用佇列,那就要考慮如何使用C#
更好的操作佇列。
一.RabbitMQ
概述
在現在的專案中,訊息佇列的使用比較的頻繁,訊息佇列的種類也較多,如:ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
等。訊息佇列中介軟體是分散式系統中重要的元件,主要解決應用耦合,非同步訊息,流量削鋒等問題。實現高效能,高可用,可伸縮和最終一致性架構。是大型分散式系統不可缺少的中介軟體。
在這裡主要介紹RabbitMQ
訊息佇列,支援開放的高階訊息佇列協議 (AMQP
)。RabbitMQ
的特點:強大的應用程式訊息傳遞;使用方便;執行在所有主要作業系統上;支援大量開發人員平臺;開源和商業支援。訊息佇列的模式有兩種模式:P2P
(Point to Point
),P2P
模式包含三個角色:訊息佇列(Queue),傳送者(Sender),接收者(Receiver)。每個訊息都被髮送到一個特定的佇列,接收者從佇列中獲取訊息。佇列保留著訊息,直到他們被消費或超時。Publish/Subscribe(Pub/Sub)
,包含三個角色主題(Topic),釋出者(Publisher),訂閱者(Subscriber) 。多個釋出者將訊息傳送到Topic
,系統將這些訊息傳遞給多個訂閱者。
上面介紹了RabbitMQ
的相關特點和模式,更多的知識就不再介紹,需要了解安裝和配置,可以進入官網進行細緻的瞭解。
二.EasyNetQ
元件概述
上面介紹了RabbitMQ
的應用場景和使用的模式,在.NET
的專案開發中,較多的使用MSMQ
作為訊息佇列,很多人對於MSMQ
的操作比較熟悉,也屬於輕量級的訊息佇列。對於RabbitMQ
是較為重量級的訊息佇列,有多個語言的版本,作為.NET開發者對於RabbitMQ
的操作可能就比較少。在.NET專案中如何更方便的使用RabbitMQ
,在這裡就介紹一個.NET操作RabbitMQ
的元件EasyNetQ
。
EasyNetQ
的目標是提供一個使.NET中的RabbitMQ
儘可能簡單的庫。在EasyNetQ
中訊息應由.NET
型別表示,訊息應通過其.NET型別進行路由。EasyNetQ
按訊息型別進行路由。釋出訊息時,EasyNetQ
會檢查其型別,並根據型別名稱,名稱空間和裝配體給出一個路由金鑰。在消費方面,使用者訂閱型別。訂閱型別後,該型別的訊息將路由到訂戶。預設情況下,EasyNetQ
使用Newtonsoft.Json
庫將.NET
型別序列化為JSON
。這具有訊息是人類可讀的優點,因此您可以使用RabbitMQ
管理應用程式等工具來除錯訊息問題。
EasyNetQ
是在RabbitMQ.Client
庫之上提供服務的元件集合。這些操作可以像序列化,錯誤處理,執行緒編組,連線管理等。它們由mini-IoC
容器組成。您可以輕鬆地用自己的實現替換任何元件。因此,如果您希望XML
序列化而不是內建的JSON
,只需編寫一個ISerializer
的實現並將其註冊到容器。
以下是官方提供的一個結構圖,這個結構圖可以很好的解析該元件的結構:
三.EasyNetQ
元件使用方式
介紹完畢EasyNetQ
元件的相關背景,現在就要介紹一下該元件的使用方式。EasyNetQ
元件的使用方式比較簡單,跟很多元件都類似,例如:建立連線,進行操作做等等,對於EasyNetQ
元件也是如此。
1.建立連線:
var bus = RabbitHutch.CreateBus(“host=myServer;virtualHost=myVirtualHost;username=mike;password=topsecret”);
與RabbitMQ
伺服器的延遲連線由IBus
介面表示,建立連線的方式連線字串由格式為key = value
的鍵/值對組成,每一個用分號(;)分隔。host
:主機地址;virtualHost
:預設是預設的虛擬主機’/’;username
:使用者名稱,預設為’guest’;password
:密碼,預設是’guest’;
2.關閉連線:
bus.Dispose();
要關閉連線,只需簡單地處理匯流排,這將關閉EasyNetQ
使用的連線,渠道,消費者和所有其他資源。
3.釋出訊息:
var message = new MyMessage { Text = "Hello Rabbit" };
bus.Publish(message);
4.訂閱郵件:
bus.Subscribe<MyMessage>("my_subscription_id", msg => Console.WriteLine(msg.Text));
5.遠端過程呼叫:
var request = new TestRequestMessage {Text = "Hello from the client! "};
bus.Request<TestRequestMessage, TestResponseMessage>(request, response => Console.WriteLine("Got response: '{0}'",response.Text));
6.RPC伺服器:
bus.Respond<TestRequestMessage, TestResponseMessage>(request => new TestResponseMessage{ Text = request.Text + " all done!" });
7.記錄器:
var logger = new MyLogger() ;
var bus = RabbitHutch.CreateBus(“my connection string”, x => x.Register<IEasyNetQLogger>(_ => logger));
8.路由:
bus.Subscribe("my_id", handler, x => x.WithTopic("X.*"));
RabbitMQ
具有非常好的功能,基於主題的路由,允許訂閱者基於多個標準過濾訊息。*
(星號)匹配一個字。#
(雜湊)匹配為零個或多個單詞。
四.EasyNetQ元件核心物件解析
上面簡單的介紹了一下該元件的應用方式,還有比較多的方式沒有做介紹,又需要的可以做深入的瞭解。在這裡介紹一下該元件的一些核心的物件。
1.RabbitHutch.CreateBus()
:
舊版方法
public static IBus CreateBus(ConnectionConfiguration connectionConfiguration, AdvancedBusEventHandlers advancedBusEventHandlers,
Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(connectionConfiguration, "connectionConfiguration");
Preconditions.CheckNotNull(advancedBusEventHandlers, "advancedBusEventHandlers");
Preconditions.CheckNotNull(registerServices, "registerServices");
var container = createContainerInternal();
if (container == null)
{
throw new EasyNetQException("Could not create container. " +
"Have you called SetContainerFactory(...) with a function that returns null?");
}
connectionConfiguration.Validate();
container.Register(_ => connectionConfiguration);
container.Register(_ => advancedBusEventHandlers);
registerServices(container);
ComponentRegistration.RegisterServices(container);
return container.Resolve<IBus>();
}
在RabbitHutch
類中主要包含的方法是CreateBus()
方法,具有12
個過載。該方法主要根據使用者的連線配置資訊,連線服務端。該方法接收三個引數,connectionConfiguration
表示連線例項,advancedBusEventHandlers
用於新增處理程式的AdvancedBusEventHandlers
例項到新建立的IBus.Advanced
”的事件。registerServices
覆蓋預設服務。 ComponentRegistration.RegisterServices(container);
在我們內部的超簡單IoC
容器中註冊預設的EasyNetQ
元件。container.Resolve<IBus>()
獲取所請求的服務的例項。 注意所有服務都是單例的,多次通話Resolve
將返回相同的例項。
EasyNetQ 3.3.4 新版
RabbitHutch.cs
using System;
using System.Collections.Generic;
#if NETFX
using System.Configuration;
#endif
using EasyNetQ.ConnectionString;
using EasyNetQ.DI;
namespace EasyNetQ
{
/// <summary>
/// Static methods to create EasyNetQ core APIs.
/// </summary>
public static class RabbitHutch
{
#if NETFX
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// The RabbitMQ broker is defined in the connection string named 'rabbit'.
/// </summary>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(Action<IServiceRegister> registerServices)
{
var rabbitConnection = ConfigurationManager.ConnectionStrings["rabbit"];
if (rabbitConnection == null)
{
throw new EasyNetQException(
"Could not find a connection string for RabbitMQ. " +
"Please add a connection string in the <ConnectionStrings> section" +
"of the application's configuration file. For example: " +
"<add name=\"rabbit\" connectionString=\"host=localhost\" />");
}
var rabbitConnectionString = rabbitConnection.ConnectionString;
return CreateBus(rabbitConnectionString, registerServices);
}
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// The RabbitMQ broker is defined in the connection string named 'rabbit'.
/// </summary>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus()
{
return CreateBus(c => {});
}
#endif
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// </summary>
/// <param name="connectionString">
/// The EasyNetQ connection string. Example:
/// host=192.168.1.1;port=5672;virtualHost=MyVirtualHost;username=MyUsername;password=MyPassword;requestedHeartbeat=10
///
/// The following default values will be used if not specified:
/// host=localhost;port=5672;virtualHost=/;username=guest;password=guest;requestedHeartbeat=10
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(string connectionString)
{
return CreateBus(connectionString, x => { });
}
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// </summary>
/// <param name="connectionString">
/// The EasyNetQ connection string. Example:
/// host=192.168.1.1;port=5672;virtualHost=MyVirtualHost;username=MyUsername;password=MyPassword;requestedHeartbeat=10
///
/// The following default values will be used if not specified:
/// host=localhost;port=5672;virtualHost=/;username=guest;password=guest;requestedHeartbeat=10
/// </param>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(string connectionString, Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(connectionString, "connectionString");
return CreateBus(x => x.Resolve<IConnectionStringParser>().Parse(connectionString), registerServices);
}
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// </summary>
/// <param name="hostName">
/// The RabbitMQ broker.
/// </param>
/// <param name="hostPort">
/// The RabbitMQ broker port.
/// </param>
/// <param name="virtualHost">
/// The RabbitMQ virtualHost.
/// </param>
/// <param name="username">
/// The username to use to connect to the RabbitMQ broker.
/// </param>
/// <param name="password">
/// The password to use to connect to the RabbitMQ broker.
/// </param>
/// <param name="requestedHeartbeat">
/// The initially requested heartbeat interval, in seconds; zero for none.
/// </param>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(
string hostName,
ushort hostPort,
string virtualHost,
string username,
string password,
ushort requestedHeartbeat,
Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(hostName, "hostName");
Preconditions.CheckNotNull(virtualHost, "virtualHost");
Preconditions.CheckNotNull(username, "username");
Preconditions.CheckNotNull(password, "password");
var connectionConfiguration = new ConnectionConfiguration
{
Hosts = new List<HostConfiguration>
{
new HostConfiguration { Host = hostName, Port = hostPort }
},
Port = hostPort,
VirtualHost = virtualHost,
UserName = username,
Password = password,
RequestedHeartbeat = requestedHeartbeat
};
return CreateBus(connectionConfiguration, registerServices);
}
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// </summary>
/// <param name="connectionConfiguration">
/// An <see cref="ConnectionConfiguration"/> instance.
/// </param>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(ConnectionConfiguration connectionConfiguration, Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(connectionConfiguration, "connectionConfiguration");
return CreateBus(_ => connectionConfiguration, registerServices);
}
/// <summary>
/// Creates a new instance of <see cref="RabbitBus"/>.
/// </summary>
/// <param name="connectionConfigurationFactory">
/// A factory of <see cref="ConnectionConfiguration"/> instance.
/// </param>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static IBus CreateBus(Func<IServiceResolver, ConnectionConfiguration> connectionConfigurationFactory, Action<IServiceRegister> registerServices)
{
var container = new DefaultServiceContainer();
RegisterBus(container, connectionConfigurationFactory, registerServices);
return container.Resolve<IBus>();
}
/// <summary>
/// Registers components of a <see cref="RabbitBus"/>.
/// </summary>
/// <param name="serviceRegister"/>
/// <param name="connectionConfigurationFactory">
/// A factory of <see cref="ConnectionConfiguration"/> instance.
/// </param>
/// <param name="registerServices">
/// Override default services. For example, to override the default <see cref="ISerializer"/>:
/// RabbitHutch.CreateBus("host=localhost", x => x.Register{ISerializer}(mySerializer));
/// </param>
/// <returns>
/// A new <see cref="RabbitBus"/> instance.
/// </returns>
public static void RegisterBus(IServiceRegister serviceRegister,
Func<IServiceResolver, ConnectionConfiguration> connectionConfigurationFactory,
Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(serviceRegister, "serviceRegister");
Preconditions.CheckNotNull(connectionConfigurationFactory, "connectionConfiguration");
Preconditions.CheckNotNull(registerServices, "registerServices");
serviceRegister.Register(c =>
{
var connectionConfiguration = connectionConfigurationFactory.Invoke(c);
connectionConfiguration.Validate();
return connectionConfiguration;
});
serviceRegister.RegisterDefaultServices();
registerServices(serviceRegister);
}
}
}
IBus介面
using System;
using EasyNetQ.Producer;
using EasyNetQ.Scheduling;
namespace EasyNetQ
{
/// <summary>
/// Provides a simple Publish/Subscribe, Request/Response, Send/Receive and Delayed Publish API for a message bus.為訊息匯流排提供簡單的釋出/訂閱,請求/響應,傳送/接收和延遲釋出API。
/// </summary>
public interface IBus : IDisposable
{
/// <summary>
/// Provides a simple Publish/Subscribe API
///提供簡單的釋出/訂閱API
/// </summary>
IPubSub PubSub { get; }
/// <summary>
/// Provides a simple Request/Response API
///提供簡單的請求/響應API
/// </summary>
IRpc Rpc { get; }
/// <summary>
/// Provides a simple Send/Receive API
///提供簡單的傳送/接收API
/// </summary>
ISendReceive SendReceive { get; }
/// <summary>
/// Provides a simple Delayed Publish API
///提供簡單的延遲釋出API
/// </summary>
IScheduler Scheduler { get; }
/// <summary>
/// Return the advanced EasyNetQ advanced API.
///返回高階EasyNetQ高階API
/// </summary>
IAdvancedBus Advanced { get; }
}
}
2.IBus.Publish()
:
舊版本的方法
public virtual void Publish<T>(T message, Action<IPublishConfiguration> configure) where T : class
{
Preconditions.CheckNotNull(message, "message");
Preconditions.CheckNotNull(configure, "configure");
var configuration = new PublishConfiguration(conventions.TopicNamingConvention(typeof(T)));
configure(configuration);
var messageType = typeof(T);
var easyNetQMessage = new Message<T>(message)
{
Properties =
{
DeliveryMode = messageDeliveryModeStrategy.GetDeliveryMode(messageType)
}
};
if (configuration.Priority != null)
easyNetQMessage.Properties.Priority = configuration.Priority.Value;
if (configuration.Expires != null)
easyNetQMessage.Properties.Expiration = configuration.Expires.ToString();
var exchange = publishExchangeDeclareStrategy.DeclareExchange(advancedBus, messageType, ExchangeType.Topic);
advancedBus.Publish(exchange, configuration.Topic, false, easyNetQMessage);
}
該方法用於釋出訊息,該方法是一個虛方法,在子類中可以被重寫。 var configuration = new PublishConfiguration(conventions.TopicNamingConvention(typeof(T)))
用於定義釋出資訊的配置,Message
定義郵件正文內容。
EasyNetQ 3.3.4 新版
PubSubExtensions
擴充套件類
using System;
using System.Threading;
using System.Threading.Tasks;
using EasyNetQ.FluentConfiguration;
using EasyNetQ.Internals;
namespace EasyNetQ.Producer
{
public static class PubSubExtensions
{
/// <summary>
/// Publishes a message with a topic.
/// When used with publisher confirms the task completes when the publish is confirmed.
/// Task will throw an exception if the confirm is NACK'd or times out.
/// </summary>
/// <typeparam name="T">The message type</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// /// <param name="message">The message to publish</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns></returns>
public static Task PublishAsync<T>(this IPubSub pubSub, T message, CancellationToken cancellationToken = default)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.PublishAsync(message, c => {}, cancellationToken);
}
/// <summary>
/// Publishes a message with a topic.
/// When used with publisher confirms the task completes when the publish is confirmed.
/// Task will throw an exception if the confirm is NACK'd or times out.
/// </summary>
/// <typeparam name="T">The message type</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// /// <param name="message">The message to publish</param>
/// <param name="topic">The topic string</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns></returns>
public static Task PublishAsync<T>(this IPubSub pubSub, T message, string topic, CancellationToken cancellationToken = default)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
Preconditions.CheckNotNull(topic, "topic");
return pubSub.PublishAsync(message, c => c.WithTopic(topic), cancellationToken);
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <typeparam name="T">The message type</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="message">The message to publish</param>
/// <param name="cancellationToken">The cancellation token</param>
public static void Publish<T>(this IPubSub pubSub, T message, CancellationToken cancellationToken = default)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
pubSub.Publish(message, c => { }, cancellationToken);
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <typeparam name="T">The message type</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="message">The message to publish</param>
/// <param name="configure">
/// Fluent configuration e.g. x => x.WithTopic("*.brighton").WithPriority(2)
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
public static void Publish<T>(this IPubSub pubSub, T message, Action<IPublishConfiguration> configure, CancellationToken cancellationToken = default)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
pubSub.PublishAsync(message, configure, cancellationToken)
.GetAwaiter()
.GetResult();
}
/// <summary>
/// Publishes a message with a topic
/// </summary>
/// <typeparam name="T">The message type</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="message">The message to publish</param>
/// <param name="topic">The topic string</param>
/// <param name="cancellationToken">The cancellation token</param>
public static void Publish<T>(this IPubSub pubSub, T message, string topic, CancellationToken cancellationToken = default)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
pubSub.Publish(message, c => c.WithTopic(topic), cancellationToken);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. When onMessage completes the message
/// receipt is Ack'd. All onMessage delegates are processed on a single thread so you should
/// avoid long running blocking IO operations. Consider using SubscribeAsync
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static AwaitableDisposable<ISubscriptionResult> SubscribeAsync<T>(
this IPubSub pubSub,
string subscriptionId,
Action<T> onMessage,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.SubscribeAsync(
subscriptionId,
onMessage,
c => { },
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. When onMessage completes the message
/// receipt is Ack'd. All onMessage delegates are processed on a single thread so you should
/// avoid long running blocking IO operations. Consider using SubscribeAsync
/// </param>
/// <param name="configure">
/// Fluent configuration e.g. x => x.WithTopic("uk.london")
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static AwaitableDisposable<ISubscriptionResult> SubscribeAsync<T>(
this IPubSub pubSub,
string subscriptionId,
Action<T> onMessage,
Action<ISubscriptionConfiguration> configure,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
var onMessageAsync = TaskHelpers.FromAction<T>((m, c) => onMessage(m));
return pubSub.SubscribeAsync(
subscriptionId,
onMessageAsync,
configure,
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// Allows the subscriber to complete asynchronously.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. onMessage can immediately return a Task and
/// then continue processing asynchronously. When the Task completes the message will be
/// Ack'd.
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static AwaitableDisposable<ISubscriptionResult> SubscribeAsync<T>(
IPubSub pubSub,
string subscriptionId,
Func<T, Task> onMessage,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.SubscribeAsync<T>(
subscriptionId,
(m, c) => onMessage(m),
c => { },
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. When onMessage completes the message
/// receipt is Ack'd. All onMessage delegates are processed on a single thread so you should
/// avoid long running blocking IO operations. Consider using SubscribeAsync
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static ISubscriptionResult Subscribe<T>(
this IPubSub pubSub,
string subscriptionId,
Action<T> onMessage,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.Subscribe(
subscriptionId,
onMessage,
c => { },
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. When onMessage completes the message
/// receipt is Ack'd. All onMessage delegates are processed on a single thread so you should
/// avoid long running blocking IO operations. Consider using SubscribeAsync
/// </param>
/// <param name="configure">
/// Fluent configuration e.g. x => x.WithTopic("uk.london")
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static ISubscriptionResult Subscribe<T>(
this IPubSub pubSub,
string subscriptionId,
Action<T> onMessage,
Action<ISubscriptionConfiguration> configure,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
var onMessageAsync = TaskHelpers.FromAction<T>((m, c) => onMessage(m));
return pubSub.Subscribe(
subscriptionId,
onMessageAsync,
configure,
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// Allows the subscriber to complete asynchronously.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. onMessage can immediately return a Task and
/// then continue processing asynchronously. When the Task completes the message will be
/// Ack'd.
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static ISubscriptionResult Subscribe<T>(
IPubSub pubSub,
string subscriptionId,
Func<T, Task> onMessage,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.Subscribe<T>(
subscriptionId,
(m, c) => onMessage(m),
c => { },
cancellationToken
);
}
/// <summary>
/// Subscribes to a stream of messages that match a .NET type.
/// Allows the subscriber to complete asynchronously.
/// </summary>
/// <typeparam name="T">The type to subscribe to</typeparam>
/// <param name="pubSub">The pubSub instance</param>
/// <param name="subscriptionId">
/// A unique identifier for the subscription. Two subscriptions with the same subscriptionId
/// and type will get messages delivered in turn. This is useful if you want multiple subscribers
/// to load balance a subscription in a round-robin fashion.
/// </param>
/// <param name="onMessage">
/// The action to run when a message arrives. onMessage can immediately return a Task and
/// then continue processing asynchronously. When the Task completes the message will be
/// Ack'd.
/// </param>
/// <param name="configure">
/// Fluent configuration e.g. x => x.WithTopic("uk.london")
/// </param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>
/// An <see cref="ISubscriptionResult"/>
/// Call Dispose on it or on its <see cref="ISubscriptionResult.ConsumerCancellation"/> to cancel the subscription.
/// </returns>
public static ISubscriptionResult Subscribe<T>(
this IPubSub pubSub,
string subscriptionId,
Func<T, CancellationToken, Task> onMessage,
Action<ISubscriptionConfiguration> configure,
CancellationToken cancellationToken = default
)
{
Preconditions.CheckNotNull(pubSub, "pubSub");
return pubSub.SubscribeAsync(
subscriptionId,
onMessage,
configure,
cancellationToken
).GetAwaiter().GetResult();
}
}
}
五.總結
以上是對該元件的簡單的介紹,如果需要了解更多的內容可以自己去深入的學習和研究。知識在於自己的勤奮,他人只是一個簡單的引導。
相關文章
- 一個.Net簡單、易用的配置檔案操作庫
- .NET 開源免費圖表元件庫,Winform,WPF 通用元件ORM
- .NET下免費開源的PDF類庫(PDFSharp)
- 一些免費、操作簡單的工具軟體
- JavaScript中的DOM和Timer(簡單易用的基本操作)JavaScript
- 開源=免費?
- 一款使用typescript開發的,簡單的,易用的線上表格元件TypeScript元件
- 一個簡單高效低記憶體的.NET操作Excel開源框架 - MiniExcel記憶體Excel框架
- D2 Crud,一款簡單易用的表格元件元件
- 分享10款美感十足的免費開源 TailwindCSS 元件AICSS元件
- DRouter:簡單易用的支援多程式架構的元件化方案架構元件化
- .NET 免費開源工業物聯網閘道器
- .Net 7 被Microsoft的開源免費PowerToys工具獨立附帶ROS
- 十五、.net core(.NET 6)搭建RabbitMQ訊息佇列生產者和消費者的簡單方法MQ佇列
- 一款開源免費的WPF圖表控制元件ModernuiCharts控制元件UI
- 免費API介面:讓開發更簡單更快API
- 開源免費的建站系統
- .NET 智慧元件完全開源元件
- ssh免密登入簡單操作
- 免費,程式碼開源,thinkphp5開發的fivecms內容管理系統,輕便簡潔,易於操作。PHP
- EacooPHP框架【開源、免費、好用】OOPPHP框架
- 一款開源免費美觀的WinForm UI控制元件庫 - ReaLTaiizorORMUI控制元件AI
- .Net Core中使用NEST簡單操作ElasticsearchElasticsearch
- Iris for Mac:簡單易用的錄屏神器Mac
- 簡單易用的任務佇列-beanstalkd佇列Bean
- 簡單易用的macOS音量應用程式Mac
- 一個簡單易用高效的工具庫
- yiigo - 簡單易用的 Golang 輔助庫Golang
- 簡單易用的前端模擬資料前端
- 開源≠免費 常見開源協議介紹協議
- 自研一套通俗易用的操作日誌元件元件
- React實現簡單易用ToastReactAST
- 簡單易用的繪圖軟體:Paint Expert for Mac v2.1免啟用版繪圖AIMac
- 完全開源免費阿里雲域名動態 IP 解析 Shell 小指令碼阿里指令碼
- 好用簡單、且永久免費的內網穿透工具內網穿透
- 免費易用的專案進度管理軟體推薦
- 一款.NET開源的小巧、智慧、免費的Windows記憶體清理工具 - WinMemoryCleanerWindows記憶體
- 關於RabbitMQ的簡單理解MQ