-
Notifications
You must be signed in to change notification settings - Fork 7
3. Configuration
Platibus can be configured programmatically, declaratively, or with a mixture of both styles. In any case, the objective is to initialize an IPlatibusConfiguration
implementation that will be passed to the Platibus.Bus
constructor. This interface provides access to all of the configuration and dependencies needed by the bus itself.
Additional configuration may be required to configure the bus host itself. In general, these configurations inherit from and extend IPlatibusConfiguration
to provide the additional configuration details for the host. Host-specific configuration is described in more detail in the following sections:
- Loopback Configuration
- HTTP Server Configuration
- IIS Configuration
- RabbitMQ Configuration
- Message Queueing
- Message Journaling
- Subscription Tracking
- Diagnostics
The Platibus.Bus
instance performs the core functions of sending and handling messages. In order to do so, there is a core set of configuration needed by the bus regardless of how it is hosted.
When a message is sent the bus creates and stores a reference to an observable ISentMessage
object for that message. This object reference is held until all replies to that message are received or a reply timeout occurs. The IPlatibusConfiguration.ReplyTimeout
property specifies the maximum amount of time that the sent message reference will be held awaiting a reply.
Declarative example:
<!-- HTTP configuration section -->
<platibus.httpserver replyTimeout="00:05:00">
<!-- Other configuration ... -->
</platibus.httpserver>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
configuration.ReplyTimeout = TimeSpan.FromMinutes(5);
}
}
The serialization service is specified via the IPlatibusConfiguration.SerializationService
property. This object provides ISerializer
implementations based on MIME content types. The default implementation, Platibus.Serialization.DefaultSerializationService
, provides out-of-the-box support for text/plain, application/json, application/xml, and application/octet-stream content types. Support for additional content types can be added via the DefaultSerializationProvider.Add
method.
There is currently no support for declaratively specifying the serialization service. However, a Platibus.IConfigurationHook
can be used to override the default serialization service in the PlatibusConfiguration
that is generated from the declarative configuration before the bus is initialized.
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
var serializationService = new DefaultSerializationService();
// Override the default Newtonsoft Json.Net based serializer
serializationService.Add("application/json", new DataContractJsonSerializer());
configuration.SerializationService = serializationService;
}
}
To facilitate serialization and deserialization, message object types are assigned a message name that serves as a platform-agnostic type identifier. On the sending side, the message object's type is mapped to a message name which is transmitted to the recipient in the message headers. On the receiving side the message name is mapped onto a suitable type (if applicable) to aid in deserializing the message content. The message naming service is the component that the bus uses to map message object types onto message names and vice-versa.
The message naming service is specified by the IPlatibusConfiguration.MessageNamingService
property. The default implementation, Platibus.DefaultMessageNamingService
, simply uses the full name of the message type as the message name. An alternate implementation, Platibus.DataContractMessageNamingService
, will use the XML qualified name of the data contract for message types decorated with System.Runtime.Serialization.DataContractAttribute
and will fall back to the full type name for message types that are not data contracts.
There is currently no support for declaratively specifying the message naming service. However, a Platibus.IConfigurationHook
can be used to override the default message naming service in the PlatibusConfiguration
that is generated from the declarative configuration before the bus is initialized.
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
var namingService = new DataContractMessageNamingService();
configuration.MessageNamingService = namingService;
}
}
Platibus emits diagnostic events that enable consumers to trace calls, troubleshoot issues, or capture performance metrics. Consumers can access these events by registering a IDiagnosticEventSink with the IDiagnosticService exposed by the IPlatibusConfiguration.DiagnosticService
property.
Diagnostic event sinks can also be registered declaratively by adding an element to the <sinks>
element collection nested in the '' configuration element.
Declarative example:
<diagnostics>
<sinks>
<add name="console" provider="Console"/>
</sinks>
</diagnostics>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
var consoleSink = new ConsoleLoggingSink();
configuration.DiagnosticService.AddSink(consoleSink);
}
}
Diagnostic event capture is optional and is not enabled by default. Various diagnostic event sinks and their respective configurations are discussed in more detail in the Diagnostics section.
The message journal is an optional component that can be used to track the sending and receipt of messages for diagnostics, compliance, or other purposes. It is specified via the IPlatibusConfiguration.MessageJournal
property.
The message journal can be specified declaratively by adding a <messageJournaling>
element to the declarative configuration section or programmatically by setting the value of the PlatibusConfiguration.MessageJournal
property from a Platibus.IConfigurationHook
.
Declarative example:
<messageJournaling provider="SQL" connectionName="MyDB"/>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
var connectionStringSettings = CongfigurationManager.ConnectionStrings["MyDB"];
var journal = new SQLJournal(connectionStringSettings);
configuration.MessageJournal = journal;
}
}
Because journaling is optional, there is no default configuration, meaning journaling is disabled by default. The examples above demonstrate an implementation has been provided that stores copies of the messages that are sent, received, or published in a SQL database.
Named topics must be declared before messages can be published to them. This ensures that messages are published to valid topics and enables remote systems to query for lists of topics to which they can subscribe. Topics can be added to the <topics>
element of the declarative configuration section or by adding topics to the PlatibusConfiguration
via the AddTopic
method from an IConfigurationHook
.
Declarative example:
<topics>
<add name="OrderEvents"/>
<add name="CustomerEvents"/>
</topics>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
configuration.AddTopic("OrderEvents");
configuration.AddTopic("CustomerEvents");
}
}
Endpoints describe Platibus instances to which messages can be sent and from which messages can be received by subscription. Each endpoint must be assigned a unique name that is used elsewhere in the configuration to refer to the endpoint (e.g. send rules and subscription rules).
Endpoint hosts may require authentication in order to send messages. In these cases the endpoint configuration should specify the type of credentials required and additional details (username, password, etc.) as applicable. Currently, only basic authentication and Windows authentication are supported.
Endpoints can be added to the <endpoints>
element of the declarative configuration section or by adding endpoints to the PlatibusConfiguration
via the AddEndpoint
method from an IConfigurationHook
.
Declarative example:
<endpoints>
<add name="provisioning-system" address="http://prov.example.com/platibus"
credentialType="Basic" username="user" password='trustno1'/>
<add name="crm-system" address="http://crm.example.com/platibus"
credentialType="Windows" />
</endpoints>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
configuration.AddEndpoint("provisioning-system",
new Endpoint(new Uri("http://provisioning.example.com/platibus"),
new BasicAuthCredentials("user", "trustno1")));
configuration.AddEndpoint("crm-system",
new Endpoint(new Uri("http://crm.example.com/platibus"),
new DefaultNetworkCredentials()));
}
}
When messages are sent they are delivered to the distinct set of endpoints for which there are send rules that apply to the message. The criteria for determining whether a send rule applies to a message
are encapsulated in IMessageSpecification
objects that implement a single IsSatisfiedBy
method. Any rules with message specifications that return true
for a given message apply to that message, and the message will be delivered to the associated endpoints.
While declarative configuration is supported for send rules, the message specifications are currently limited to a regular expression matches on the message name. If more complex specifications are required (for example, matching on other message headers or message contents) then the send rules can be added programmatically to the PlatibusConfiguration
via the AddSendRule
method from within an IConfigurationHook
.
Declarative example:
<sendRules>
<add namePattern=".*Order.*Command" endpoint="provisioning-system"/>
<add namePattern=".*Customer.*Command" endpoint="crm-system" />
</sendRules>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
configuration.AddSendRule(new SendRule(
new MessageNamePatternSpecification(".*Order.*Command"),
"provisioning-system"));
configuration.AddSendRule(new SendRule(
new MessageNamePatternSpecification(".*Customer.*Command"),
"crm-system"));
// More complex examples
// Send any message whose content contains the string "error"
// to the "error-logging" endpoint
configuration.AddSendRule(new SendRule(
new DelegateMessageSpecification(m => m.Content.Contains("error")),
"error-logging"));
// Send any message containing anything resembling a credit
// card number to both the "data-loss-prevention" and the
// "compliance" endpoints
configuration.AddSendRule(new SendRule(
new DelegateMessageSpecification(m =>
Regex.IsMatch(m.Content, "(^|\b)\d4(?:-\d4){3}(\b|$)")),
"data-loss-prevention", "compliance"));
}
}
When messages are published to a topic they are delivered to all subscribers of that topic. Subscribers request receipt of published messages via subscriptions. Each subscription indicates the endpoint and topic being subscribed to as well as optional "time to live" or TTL.
Subscription rules can be specified declaratively via the <subscriptions>
collection or registered programmatically to the PlatibusConfiguration
via the AddSubscription
method from within an IConfigurationHook
.
Declarative example:
<subscriptions>
<add endpoint="provisioning-system" topic="OrderEvents" ttl="12:00:00"/>
<add endpoint="crm-system" topic="CustomerEvents" />
</subscriptions>
Programmatic example:
public class ConfigurationHook : IConfigurationHook
{
public void Configure(PlatibusConfiguration configuration)
{
configuration.AddSubscription(new Subscription(
"provisioning-system", "OrderEvents", TimeSpan.FromHours(12)));
configuration.AddSubscription(new Subscription(
"crm-system", "CustomerEvents"));
}
}
If a TTL is specified the subscribing application will renew its subscription periodically at an interval that is half the TTL. If the publishing application does not receive a subscription renewal request by the time the TTL elapses, it will cease sending publications to the subscriber. In other words, if the subscription rule is removed and the subscribing application does not send an explicit request to unsubscribe, the subscription will eventually expire and be removed by the publishing application.
If a renewal request is unsuccessful, the subscribing application will retry at an interval of 30 seconds until it succeeds.
Subscriptions with no TTL do not expire and are not remove without an explicit request to unsubscribe.
Configuration hooks are used to supplement declarative configuration during bus initialization. Implementing a configuration hook is as simple as including a concrete implementation of Platibus.Configuration.IConfigurationHook
with a default constructor in one of the application assemblies (exe or dll). Multiple configuration hooks can be provided and each will be called. However, the order in which multiple configuration hooks are invoked is unspecified. Configuration hooks do not need to be thread-safe.
When the bus is initialized, the declarative configuration is read and used to construct and initialize a PlatibusConfiguration
instance. Once the PlatibusConfiguration
object has been initialized, the configuration manager scans the assemblies in the app domain base directory (as well as in the bin
subdirectory, if one exists, e.g. for web apps) for concrete implementations of IConfigurationHook
. For each type that is found, the configuration manager attempts to instantiate the type using the default constructor, and then passes the PlatibusConfiguration
object to the configuration hook instance. The same PlatibusConfiguration
instance is passed to each hook, so the changes made to the PlatibuscConfiguration
are cumulative.