Started refactoring, not in any usable state yet
This commit is contained in:
parent
1f64fea559
commit
e62aa7d482
13
Annotations/DynamicQueueAttribute.cs
Normal file
13
Annotations/DynamicQueueAttribute.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Tapeti.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a non-durable auto-delete queue to receive messages. Can be used
|
||||
/// on an entire MessageController class or on individual methods.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class DynamicQueueAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
9
Annotations/MessageControllerAttribute.cs
Normal file
9
Annotations/MessageControllerAttribute.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Tapeti.Annotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class MessageControllerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Tapeti.Annotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class QueueAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool Dynamic { get; set; }
|
||||
|
||||
|
||||
public QueueAttribute(string name = null)
|
||||
{
|
||||
Name = name;
|
||||
Dynamic = (name == null);
|
||||
}
|
||||
}
|
||||
}
|
25
Annotations/StaticQueueAttribute.cs
Normal file
25
Annotations/StaticQueueAttribute.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Tapeti.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Binds to an existing durable queue to receive messages. Can be used
|
||||
/// on an entire MessageController class or on individual methods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// At the moment there is no support for creating a durable queue and managing the
|
||||
/// bindings. The author recommends https://git.x2software.net/pub/RabbitMetaQueue
|
||||
/// for deploy-time management of durable queues (shameless plug intended).
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class StaticQueueAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
|
||||
public StaticQueueAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tapeti.Connection
|
||||
{
|
||||
public class TapetiPublisher : IPublisher
|
||||
{
|
||||
private readonly TapetiWorker worker;
|
||||
private readonly Func<TapetiWorker> workerFactory;
|
||||
|
||||
|
||||
public TapetiPublisher(TapetiWorker worker)
|
||||
public TapetiPublisher(Func<TapetiWorker> workerFactory)
|
||||
{
|
||||
this.worker = worker;
|
||||
this.workerFactory = workerFactory;
|
||||
}
|
||||
|
||||
|
||||
public Task Publish(object message)
|
||||
{
|
||||
return worker.Publish(message);
|
||||
return workerFactory().Publish(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -6,18 +7,18 @@ namespace Tapeti.Connection
|
||||
{
|
||||
public class TapetiSubscriber : ISubscriber
|
||||
{
|
||||
private readonly TapetiWorker worker;
|
||||
private readonly Func<TapetiWorker> workerFactory;
|
||||
|
||||
|
||||
public TapetiSubscriber(TapetiWorker worker)
|
||||
public TapetiSubscriber(Func<TapetiWorker> workerFactory)
|
||||
{
|
||||
this.worker = worker;
|
||||
this.workerFactory = workerFactory;
|
||||
}
|
||||
|
||||
|
||||
public async Task BindQueues(IEnumerable<IQueueRegistration> registrations)
|
||||
{
|
||||
await Task.WhenAll(registrations.Select(registration => worker.Subscribe(registration)).ToList());
|
||||
await Task.WhenAll(registrations.Select(registration => workerFactory().Subscribe(registration)).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,12 @@ namespace Tapeti.Default
|
||||
private readonly Lazy<DefaultRoutingKeyStrategy> routingKeyStrategy = new Lazy<DefaultRoutingKeyStrategy>();
|
||||
private readonly Lazy<DefaultMessageSerializer> messageSerializer = new Lazy<DefaultMessageSerializer>();
|
||||
private readonly Lazy<ILogger> logger;
|
||||
private IPublisher publisher;
|
||||
|
||||
|
||||
|
||||
public DefaultDependencyResolver(Func<IPublisher> publisherFactory)
|
||||
public DefaultDependencyResolver()
|
||||
{
|
||||
controllerFactory = new Lazy<DefaultControllerFactory>(() => new DefaultControllerFactory(publisherFactory));
|
||||
controllerFactory = new Lazy<DefaultControllerFactory>(() => new DefaultControllerFactory(() => publisher));
|
||||
|
||||
logger = new Lazy<ILogger>(() =>
|
||||
{
|
||||
@ -58,6 +58,12 @@ namespace Tapeti.Default
|
||||
}
|
||||
|
||||
|
||||
public void RegisterPublisher(IPublisher value)
|
||||
{
|
||||
publisher = value;
|
||||
}
|
||||
|
||||
|
||||
public void RegisterController(Type type)
|
||||
{
|
||||
controllerFactory.Value.RegisterController(type);
|
||||
|
@ -10,6 +10,7 @@ namespace Tapeti
|
||||
|
||||
public interface IDependencyInjector : IDependencyResolver
|
||||
{
|
||||
void RegisterPublisher(IPublisher publisher);
|
||||
void RegisterController(Type type);
|
||||
}
|
||||
}
|
||||
|
16
MessageController.cs
Normal file
16
MessageController.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Tapeti.Annotations;
|
||||
|
||||
namespace Tapeti
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for message controllers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this base class is not required, you can add the MessageController attribute
|
||||
/// to any class.
|
||||
/// </remarks>
|
||||
[MessageController]
|
||||
public abstract class MessageController
|
||||
{
|
||||
}
|
||||
}
|
15
README.md
15
README.md
@ -1,3 +1,14 @@
|
||||
'Small to medium-sized and classified as "Least Concern" by the IUCN.'
|
||||
# Tapeti
|
||||
> 'Small to medium-sized and classified as "Least Concern" by the IUCN.'
|
||||
>
|
||||
> [_Wikipedia_](https://en.wikipedia.org/wiki/Tapeti)
|
||||
|
||||
- Wikipedia
|
||||
Tapeti is a wrapper for the RabbitMQ .NET client designed for long-running microservices with a few specific goals:
|
||||
|
||||
1. Automatic registration of message handlers
|
||||
2. Publishing without transport details
|
||||
* Routing key generated based on class name
|
||||
* One exchange (per service / group of services) to publish them all
|
||||
3. Attribute based, no base class requirements (only for convenience)
|
||||
4. Graceful handling of connection issues, even at startup
|
||||
5. Basic Saga support
|
@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using SimpleInjector;
|
||||
using Tapeti.Annotations;
|
||||
@ -7,7 +8,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Tapeti.SimpleInjector
|
||||
{
|
||||
public class SimpleInjectorDependencyResolver : IDependencyResolver
|
||||
public class SimpleInjectorDependencyResolver : IDependencyResolver, IDependencyInjector
|
||||
{
|
||||
private readonly Container container;
|
||||
|
||||
@ -25,6 +26,18 @@ namespace Tapeti.SimpleInjector
|
||||
}
|
||||
|
||||
|
||||
public void RegisterPublisher(IPublisher publisher)
|
||||
{
|
||||
IfUnregistered<IPublisher>(container.GetCurrentRegistrations(), () => container.RegisterSingleton(publisher));
|
||||
}
|
||||
|
||||
|
||||
public void RegisterController(Type type)
|
||||
{
|
||||
container.Register(type);
|
||||
}
|
||||
|
||||
|
||||
public SimpleInjectorDependencyResolver RegisterDefaults()
|
||||
{
|
||||
var currentRegistrations = container.GetCurrentRegistrations();
|
||||
@ -37,20 +50,18 @@ namespace Tapeti.SimpleInjector
|
||||
}
|
||||
|
||||
|
||||
public SimpleInjectorDependencyResolver RegisterAllControllers(Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes().Where(t => t.IsDefined(typeof(QueueAttribute))))
|
||||
container.Register(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private void IfUnregistered<TService, TImplementation>(IEnumerable<InstanceProducer> currentRegistrations) where TService : class where TImplementation: class, TService
|
||||
{
|
||||
// ReSharper disable once SimplifyLinqExpression - not a fan of negative predicates
|
||||
if (!currentRegistrations.Any(ip => ip.ServiceType == typeof(TService)))
|
||||
container.Register<TService, TImplementation>();
|
||||
}
|
||||
|
||||
private void IfUnregistered<TService>(IEnumerable<InstanceProducer> currentRegistrations, Action register) where TService : class
|
||||
{
|
||||
// ReSharper disable once SimplifyLinqExpression - not a fan of negative predicates
|
||||
if (!currentRegistrations.Any(ip => ip.ServiceType == typeof(TService)))
|
||||
register();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Annotations\ExchangeAttribute.cs" />
|
||||
<Compile Include="Annotations\QueueAttribute.cs" />
|
||||
<Compile Include="Annotations\MessageControllerAttribute.cs" />
|
||||
<Compile Include="Annotations\StaticQueueAttribute.cs" />
|
||||
<Compile Include="Annotations\DynamicQueueAttribute.cs" />
|
||||
<Compile Include="Connection\TapetiConsumer.cs" />
|
||||
<Compile Include="Connection\TapetiPublisher.cs" />
|
||||
<Compile Include="Connection\TapetiSubscriber.cs" />
|
||||
@ -58,6 +60,7 @@
|
||||
<Compile Include="Default\ConsoleLogger.cs" />
|
||||
<Compile Include="Default\DevNullLogger.cs" />
|
||||
<Compile Include="ILogger.cs" />
|
||||
<Compile Include="MessageController.cs" />
|
||||
<Compile Include="TapetiConnectionExtensions.cs" />
|
||||
<Compile Include="TapetiConnectionParams.cs" />
|
||||
<Compile Include="TapetiTypes.cs" />
|
||||
|
@ -19,8 +19,20 @@ namespace Tapeti
|
||||
|
||||
public IDependencyResolver DependencyResolver
|
||||
{
|
||||
get { return dependencyResolver ?? (dependencyResolver = new DefaultDependencyResolver(GetPublisher)); }
|
||||
set { dependencyResolver = value; }
|
||||
get
|
||||
{
|
||||
if (dependencyResolver == null)
|
||||
DependencyResolver = new DefaultDependencyResolver();
|
||||
|
||||
return dependencyResolver;
|
||||
}
|
||||
set
|
||||
{
|
||||
dependencyResolver = value;
|
||||
|
||||
var dependencyInjector = value as IDependencyInjector;
|
||||
dependencyInjector?.RegisterPublisher(GetPublisher());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,14 +55,14 @@ namespace Tapeti
|
||||
|
||||
public TapetiConnection WithDependencyResolver(IDependencyResolver resolver)
|
||||
{
|
||||
dependencyResolver = resolver;
|
||||
DependencyResolver = resolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TapetiConnection RegisterController(Type type)
|
||||
{
|
||||
var queueAttribute = type.GetCustomAttribute<QueueAttribute>();
|
||||
var queueAttribute = type.GetCustomAttribute<MessageController>();
|
||||
if (queueAttribute == null)
|
||||
throw new ArgumentException("Queue attribute required on class", nameof(type));
|
||||
|
||||
@ -84,7 +96,7 @@ namespace Tapeti
|
||||
if (!registrations.IsValueCreated || registrations.Value.Count == 0)
|
||||
throw new ArgumentException("No controllers registered");
|
||||
|
||||
var subscriber = new TapetiSubscriber(worker.Value);
|
||||
var subscriber = new TapetiSubscriber(() => worker.Value);
|
||||
await subscriber.BindQueues(registrations.Value);
|
||||
|
||||
return subscriber;
|
||||
@ -93,7 +105,7 @@ namespace Tapeti
|
||||
|
||||
public IPublisher GetPublisher()
|
||||
{
|
||||
return new TapetiPublisher(worker.Value);
|
||||
return new TapetiPublisher(() => worker.Value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace Tapeti
|
||||
{
|
||||
public static TapetiConnection RegisterAllControllers(this TapetiConnection connection, Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes().Where(t => t.IsDefined(typeof(QueueAttribute))))
|
||||
foreach (var type in assembly.GetTypes().Where(t => t.IsDefined(typeof(DynamicQueueAttribute))))
|
||||
connection.RegisterController(type);
|
||||
|
||||
return connection;
|
||||
|
37
Test/MarcoEmitter.cs
Normal file
37
Test/MarcoEmitter.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tapeti;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MarcoEmitter
|
||||
{
|
||||
private readonly IPublisher publisher;
|
||||
|
||||
|
||||
public MarcoEmitter(IPublisher publisher)
|
||||
{
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
var concurrent = new SemaphoreSlim(20);
|
||||
|
||||
//for (var x = 0; x < 5000; x++)
|
||||
while (true)
|
||||
{
|
||||
await concurrent.WaitAsync();
|
||||
try
|
||||
{
|
||||
await publisher.Publish(new MarcoMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
concurrent.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,19 +19,14 @@ namespace Test
|
||||
.WithDependencyResolver(new SimpleInjectorDependencyResolver(container))
|
||||
.RegisterAllControllers(typeof(Program).Assembly))
|
||||
{
|
||||
container.Register(() => connection.GetPublisher());
|
||||
|
||||
container.Register<MarcoEmitter>();
|
||||
|
||||
Console.WriteLine("Subscribing...");
|
||||
connection.Subscribe().Wait();
|
||||
Console.WriteLine("Done!");
|
||||
|
||||
var publisher = connection.GetPublisher();
|
||||
|
||||
//for (var x = 0; x < 5000; x++)
|
||||
while(true)
|
||||
publisher.Publish(new MarcoMessage()).Wait();
|
||||
|
||||
//Console.ReadLine();
|
||||
var emitter = container.GetInstance<MarcoEmitter>();
|
||||
emitter.Run().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MarcoEmitter.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestQueueController.cs" />
|
||||
|
@ -5,10 +5,8 @@ using Tapeti.Annotations;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
//[Exchange("myexchange")]
|
||||
//[Queue("staticqueue")]
|
||||
[Queue]
|
||||
public class TestQueueController
|
||||
[DynamicQueue]
|
||||
public class TestQueueController : MessageController
|
||||
{
|
||||
private readonly IPublisher publisher;
|
||||
|
||||
@ -19,10 +17,10 @@ namespace Test
|
||||
}
|
||||
|
||||
|
||||
public async Task Marco(MarcoMessage message)
|
||||
public PoloMessage Marco(MarcoMessage message)
|
||||
{
|
||||
Console.WriteLine("Marco!");
|
||||
await publisher.Publish(new PoloMessage());
|
||||
return new PoloMessage();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user