151 lines
6.8 KiB
ReStructuredText
151 lines
6.8 KiB
ReStructuredText
Getting started
|
|
===============
|
|
|
|
This guide is a step by step introduction. If you want to know more about how Tapeti works, for example how it determines the exchange and routing keys, see :doc:`indepth`.
|
|
|
|
Install packages
|
|
----------------
|
|
I'll assume you are familiar with installing NuGet.org packages into your project.
|
|
|
|
Find and install the *Tapeti* package. This will also install *Tapeti.Annotations*, which contains the various attributes.
|
|
|
|
You will need an integration package as well for your IoC (Inversion of Control) container of choice. At the time of writing, one is provided for `SimpleInjector <https://simpleinjector.org/>`_. Simply install *Tapeti.SimpleInjector* as well.
|
|
|
|
.. note:: If you need support for your favourite library, implement *IDependencyContainer* using the *Tapeti.SimpleInjector* source as a reference and replace *SimpleInjectorDependencyResolver* with your class name in the example code below.
|
|
|
|
Configuring Tapeti
|
|
------------------
|
|
First create an instance of TapetiConfig, tell it which controllers to register and have it gather all the information by calling Build. Then pass the result to a TapetiConnection.
|
|
|
|
::
|
|
|
|
using SimpleInjector;
|
|
using Tapeti;
|
|
|
|
internal class Program
|
|
{
|
|
private static void Main()
|
|
{
|
|
var container = new Container();
|
|
var config = new TapetiConfig(new SimpleInjectorDependencyResolver(container))
|
|
.RegisterAllControllers()
|
|
.Build();
|
|
|
|
using (var connection = new TapetiConnection(config)
|
|
{
|
|
Params = new TapetiConnectionParams
|
|
{
|
|
Host = "localhost"
|
|
}
|
|
})
|
|
{
|
|
connection.SubscribeSync();
|
|
|
|
// Start service
|
|
}
|
|
}
|
|
}
|
|
|
|
.. note:: RegisterAllControllers without parameters searches the entry assembly. Pass an Assembly parameter to register other or additional controllers. You can call RegisterAllControllers multiple times with different assemblies.
|
|
|
|
.. caution:: Tapeti attempts to register it's default implementations in the IoC container during configuration, as well as when starting the connection (to register IPublisher). If your container is immutable after the initial configuration, like SimpleInjector is, make sure that you run the Tapeti configuration before requesting any instances from the container.
|
|
|
|
Defining a message
|
|
------------------
|
|
A message is simply a plain object which can be serialized using `Json.NET <http://www.newtonsoft.com/json>`_.
|
|
|
|
::
|
|
|
|
public class RabbitEscapedMessage
|
|
{
|
|
public string Name { get; set; }
|
|
public string LastKnownHutch { get; set; }
|
|
}
|
|
|
|
|
|
Creating a message controller
|
|
-----------------------------
|
|
To handle messages you need what Tapeti refers to as a "message controller". It is similar to an ASP.NET MVC controller if you're familiar with those, but it handles RabbitMQ messages instead of HTTP requests.
|
|
|
|
All you need to do is create a new class and annotate it with the MessageController attribute and a queue attribute. The name and folder of the class is not important to Tapeti, though you might want to agree on a standard in your team.
|
|
|
|
The queue attribute can be either *DynamicQueue* or *DurableQueue*. The attribute can be set for the entire controller (which is considered the default scenario) or specified / overridden per message handler.
|
|
|
|
DynamicQueue will create a queue with a name generated by RabbitMQ which is automatically deleted when your service stops. Bindings will be added for the messages handled by the controller. You will typically use dynamic queues for scenarios where handling the message is only relevant while the service is running (for example, updating a service's cache or performing live queries).
|
|
|
|
DurableQueue requires a queue name as the parameter. By default, the queue is assumed to be present already and Tapeti will throw an exception if it does not. If you want Tapeti to create and update the durable queues as well, see :ref:`declaredurablequeues` in :doc:`indepth`.
|
|
|
|
::
|
|
|
|
[MessageController]
|
|
[DynamicQueue("monitoring")]
|
|
public class MonitoringController
|
|
{
|
|
}
|
|
|
|
.. note:: Notice the parameter to DynamicQueue. This defines the prefix. If specified, the queue name will begin with the supplied value, followed by a unique identifier, so it can be more easily recognized in the RabbitMQ management interface.
|
|
|
|
|
|
Handling incoming messages
|
|
--------------------------
|
|
Any public method in a message controller is considered a message handler. There are a few requirements which are enforced by Tapeti. Below are the default requirements, although some extension packages (like the :doc:`flow`) add their own or alter these requirements.
|
|
|
|
- The first parameter must be the message class.
|
|
- The return type can be void, Task, Task<message class> or a message class.
|
|
|
|
The name of the method is not important to Tapeti. Any parameter other than the first will be resolved using the IoC container, although it is considered best practice to use the constructor for dependency injection instead.
|
|
|
|
A new controller is instantiated for each message, so it is safe to use public or private fields to store state while handling the message. Just don't expect it to be there for the next message. If you need this behaviour, take a look at the :doc:`flow`!
|
|
|
|
::
|
|
|
|
[MessageController]
|
|
[DynamicQueue]
|
|
public class MonitoringController
|
|
{
|
|
public void LogEscape(RabbitEscapedMessage message)
|
|
{
|
|
Logger.Warning($"This is a beige alert. {message.Name} has escaped." +
|
|
$"It was last seen in {message.LastKnownHutch}.");
|
|
}
|
|
}
|
|
|
|
.. note:: If you're doing anything asynchronous in the message handler, make it async as well! Simply change the return type to "Task" or "async Task".
|
|
|
|
If the method returns a message object, that object is published as if it was a reply to the incoming message, maintaining the correlationId and respecting the replyTo header. See :doc:`indepth` for request-response requirements.
|
|
|
|
|
|
Publishing messages
|
|
-------------------
|
|
To send a message, get a reference to IPublisher using dependency injection and call the Publish method. For example, to broadcast another message from a message handler:
|
|
|
|
::
|
|
|
|
public class LogMessage
|
|
{
|
|
public string Level { get; set; }
|
|
public string Description { get; set; }
|
|
}
|
|
|
|
|
|
[MessageController]
|
|
[DynamicQueue]
|
|
public class MonitoringController
|
|
{
|
|
private readonly IPublisher publisher;
|
|
|
|
public MonitoringController(IPublisher publisher)
|
|
{
|
|
this.publisher = publisher;
|
|
}
|
|
|
|
public async Task LogEscape(RabbitEscapedMessage message)
|
|
{
|
|
await publisher.Publish(new LogMessage
|
|
{
|
|
Level = "Beige",
|
|
Description = $"{message.Name} has escaped." +
|
|
$"It was last seen in {message.LastKnownHutch}."
|
|
});
|
|
}
|
|
} |