Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jrouaix/mutopic
Micro topic pubsub
https://github.com/jrouaix/mutopic
message-broker pubsub reactive rx topic
Last synced: 10 days ago
JSON representation
Micro topic pubsub
- Host: GitHub
- URL: https://github.com/jrouaix/mutopic
- Owner: jrouaix
- License: apache-2.0
- Created: 2018-05-04T22:02:22.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2019-02-08T14:56:51.000Z (almost 6 years ago)
- Last Synced: 2024-12-14T01:12:41.099Z (about 1 month ago)
- Topics: message-broker, pubsub, reactive, rx, topic
- Language: C#
- Size: 75.2 KB
- Stars: 2
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Mutopic
Micro, yet turbocharged, C# pubsub library.[![Build status](https://ci.appveyor.com/api/projects/status/1mpqa3gly0xkg8wy/branch/master?svg=true)](https://ci.appveyor.com/project/JromeRx/mutopic/branch/master)
![License](https://img.shields.io/badge/License-Apache_2.0-44cc11.svg)
![Nuget](https://img.shields.io/nuget/v/Mutopic.svg)
![Nuget](https://img.shields.io/nuget/v/Mutopic.Reactive.svg)## Installing
Using NuGet Package Manager Console:
`PM> Install-Package Mutopic`## Design principles
- **Topic based** - You publish in a topic name, you subscibe on the topic name too. Any "typed" implementation is just using the GetType().Name as topic name.
- **Fire & Forget** - Publish should never throw any exception
- **Synchronous** - Publish is synchronous and will call the subscribed handler as quick as possible. Then you can achieve asynchronous handling by pluging any task queuing system you need. (example : Reactive Extensions)## Getting started
The best place to start will be examples (when added). For a moment, best examples will be in the unit tests.### Subscribe multiple handlers
```csharp
using Mutopic;
```Build a PubSub
```csharp
const string TOPIC = "topicName";
var sut = new PubSubBuilder().Build();var receivedA = new List();
var receivedB = new List();
var receivedC = new List();
```Subscribe & Publish
```csharp
using (var subscriptionA = sut.Subscribe(TOPIC, receivedA.Add))
using (var subscriptionB = sut.Subscribe(TOPIC, receivedB.Add))
using (var subscriptionC = sut.Subscribe(TOPIC, receivedC.Add))
{
sut.Publish(1, TOPIC);
sut.Publish("topic to rule them all", TOPIC);
sut.Publish("dead letter", "no_one_listen_this_topic"); // no handler subscribed on this topic
}
```Assertions
```csharp
receivedA.ShouldBe(new string[] { "topic to rule them all" });
receivedB.ShouldBe(new int[] { 1 });
receivedC.ShouldBe(new object[] { 1, "topic to rule them all" });
```### Achieve asynchronous handling with Reactive Extensions
```csharp
using Mutopic;
using Mutopic.Reactive;
``````csharp
const string TOPIC = "topicName";
var randy = new Random();
var sut = new PubSubBuilder().Build();var received = new ConcurrentBag();
var count = 0;
using (var observable = sut.SubscribeObservable(TOPIC))
{
using (observable
// without this next "ObserveOn" line, the publishing would be blocked by the long running one
.ObserveOn(TaskPoolScheduler.Default) // after this, all message processing will be asynchronous
.Do(i => Thread.Sleep(randy.Next(20, 50))) // some long running in the pipeline
.Do(i => Interlocked.Increment(ref count))
.Where(i => i == 42) // some filtering provided by reactive extension
.Select(i => $"the answer to life the universe and everything is {i}.") // some transformation ..
.Subscribe(s => received.Add(s)) // this is reactive extensions subscription
)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100; i++)
{
sut.Publish(i, TOPIC);
}
sw.Stop();
sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(0.05)); // This was quick !Thread.Sleep(50 * 100); // Give it time to process all
count.ShouldBe(100);
received.ShouldBe(new[] { "the answer to life the universe and everything is 42." });
}
}
```### Use middleware to build a publishing pipeline
Given this class hierarchy
```csharp
interface IA { }
interface IB { }
interface IC { }class A : IA { }
class B : A, IB { }
class C : IA, IC { }
```Build a pubsub with middleware
```csharp
const string TOPIC = "topic";var sut = new PubSubBuilder()
// this is a middleware
// it will propage messages in all their inheritance topic names
.WithMessageInheritancePublishing()
.Build();
```Publish a message
```csharp
var b = new B();var topicReceived = new List();
var AReceived = new List();
var IBReceived = new List();
using (sut.Subscribe(TOPIC, topicReceived.Add))
using (sut.Subscribe(typeof(A).Name, AReceived.Add))
using (sut.Subscribe(typeof(IB).Name, IBReceived.Add))
{
sut.Publish(b, TOPIC);
}
```Assertions
```csharp
// Message have been propagaged in multiple topic names
topicReceived.ShouldBe(new object[] { b });
AReceived.ShouldBe(new object[] { b });
IBReceived.ShouldBe(new object[] { b });```
### Exception handling
Since no exception will bubble from a handler, a OnSubscriptionException event is exposed to collect eventual exception.
Use it to log errors !```csharp
var sut = new PubSubBuilder().Build();
var received = new List();
var exceptions = new List();
sut.OnSubscriptionException += (sub, mess, ex) => { received.Add(mess); exceptions.Add(ex); };using (var subscription = sut.Subscribe(TOPIC, (object message) => throw new IndexOutOfRangeException()))
{
sut.Publish(42, TOPIC);
sut.Publish("message", TOPIC);
sut.Publish("dead letter", "no_one_listen_topic"); // no handler subscribed on this topic
}received.ShouldBe(new object[] { 42, "message" });
exceptions.Count.ShouldBe(2);
exceptions[0].ShouldBeAssignableTo();
exceptions[1].ShouldBeAssignableTo();
```#