Publish/Subscribe Configuration
The part of the <add> entry stating Messages="Messages" means
that the assembly "Messages.dll" contains the message schema.
Specific types can be configured by using their qualified name:
"namespace.type, assembly".
The part stating Endpoint="messagebus" is telling the subscriber's bus object
that the publisher will be accepting subscription requests on that queue.
The queue name "messagebus" is short for "the queue named 'messagebus' on the
local machine". To indicate a queue on a remote machine, you use a format similar to
email: MessageBus@RemoteServer.
The input queue of each process does need to be on the same machine as the process though.
At this point, the bus in the subscriber process knows about the message schema and the
endpoint on which the publisher is willing to accept subscription requests. The bus object
then sees that there is applicative code in the subscriber that wants to handle those messages,
and therefore sends a subscription request to that endpoint.
Subscription Intent
The way that applicative code in the subscriber handles the messages published by the
publisher is by implementing one of the NServiceBus interfaces (IHandleMessages<T>)
as shown below:
This interface requires the single 'Handle' method accepting a parameter of the same type
as declared in the class inheritance. We can ignore the body of the method for now as it
has no bearing on how the publish/subscribe works.
Since the message being handled (EventMessage) belongs to the message assembly previously
described (Messages.dll), and the subscriber's bus knows those messages belong to the publisher
(from the app.config above), and the only way that a process can handle messages belonging to
someone else is for them to be a subscriber, the bus automatically subscribes.
Here's how it works...
Messaging Mechanics
The way that the bus at the subscriber subscribes to the publisher is by sending a message to the
queue that has been configured in its <UnicastBusConfig> section as described above.
In this message, the bus includes the type of message and the input queue of the subscriber.
When the bus at the publisher side receives this message, it stores the information.
It is important to understand that each publisher is responsible for its own information. There isn't
necessarily some logically central broker which stores everything, although NServiceBus does allow for
configuring all publishers to store their information in a physically central location,
such as a database.
There's also nothing that stops a subscriber from also being a publisher.
Here's how simple it is to state that a given process is a publisher:
How to be a Publisher
In order to indicate that a given process is a publisher, all you need to do is reference the
NServiceBus assemblies and write a class which implements IConfigureThisEndpoint and
AsA_Publisher as shown below.
On top of the 3 NServiceBus assemblies referenced, it's also important to reference 'log4net' which
is the open-source library that is used for logging. Logging is configured in NServiceBus slightly
different than the standard log4net model - for more information click here.
We can ignore the interface ISpecifyMessageHandlerOrdering for now.
Subscription Storage
NServiceBus internally sets up the storage where subscription information will be placed. By default,
NServiceBus stores this information in memory but there are also built in storage options on top of
MSMQ and databases. You don't need to specify this yourself either in code or config - see how profiles
work in NServiceBus for more information here.
In order to configure MSMQ as your subscription storage, call ".MsmqSubscriptionStorage()" in the code after
"NServiceBus.Configure.With().Log4Net().DefaultBuilder()". You don't need any configuration changes for this
to work - NServiceBus will automatically use a queue called "NServiceBus_Subscriptions". If you want control
over the queue used to store the subscriptions, add the following config section and subsequent config entry:
<section name="MsmqSubscriptionStorageConfig"
type="NServiceBus.Config.MsmqSubscriptionStorageConfig, NServiceBus.Core" />
<MsmqSubscriptionStorageConfig Queue="YourQueue" />
If you want multiple machines to share the same subscription storage, do not use the MSMQ option outlined
above - instead use the database-backed stored described here.
In order to configure a database as your subscription storage, call ".DBSubcriptionStorage()" in the code
after "NServiceBus.Configure.With().Log4Net().DefaultBuilder()". This option requires the following to
be present in your config:
<section name="DBSubscriptionStorageConfig"
type="NServiceBus.Config.DBSubscriptionStorageConfig, NServiceBus.Core" />
<DBSubscriptionStorageConfig>
<NHibernateProperties>
<add Key="connection.provider"
Value="NHibernate.Connection.DriverConnectionProvider"/>
<add Key="connection.driver_class"
Value="NHibernate.Driver.SqlClientDriver"/>
<add Key="connection.connection_string"
Value="Server=YOUR_DB_SERVER;initial catalog=NServiceBus;Integrated Security=SSPI"/>
<add Key="dialect"
Value="NHibernate.Dialect.MsSql2005Dialect"/>
</NHibernateProperties>
</DBSubscriptionStorageConfig>
If you don't want all of this information in your config, you can specify it in code through the overload
of the DBSubscriptionStorage method which accepts a dictionary of the NHibernate properties above.
The additional 'autoUpdateSchema' parameter, if set to 'true' tells NServiceBus to create the necessary
tables in the configured database to store the subscription information. This table is called
'Subscriptions' and has two columns, 'SubscriberEndpoint' and 'MessageType' - both of them varchars.
For more information on NHibernate configuration see
this page,
specifically Table 3.1 and the optional configuration options in section 3.5.
Table 3.3 will help you configure other databases like Oracle and MySQL.
How to publish
In order to publish a message, you need a reference to the bus object in your code.
In the pub/sub sample, this code is in the ServerEndpoint class in the Server project as shown below:
The 'Bus' property is automatically filled by the infrastructure. This is known as 'Dependency Injection'.
All development done with NServiceBus makes use of these patterns so if you aren't very familiar with
this style of development, please read more about it here
(opens a new window). The technology used as the dependency injection
container by NServiceBus is pluggable, with 5 options available out of the box - Spring is the default.
In the 'Run' method, you'll see the creation of the event message. This can be as simple as
instantiating the relevant class or using the bus object to instantiate messages defined as interfaces.
For more information on whether to use interfaces or classes to represent messages, see here.
Once the event message object has been created, the call to Bus.Publish(eventMessage); tells
the bus object to have the given message sent to all subscribers who had expressed interest in that
type of message. As we saw in the walkthrough, if a subscriber is unavailable, their messages aren't lost -
they're stored until the subscriber comes back online. See the 'store and forward messaging' section
of the architectural principles
of NServiceBus for more information.
Security & Authorizations
You may not want to allow any endpoints to subscribe to a given publisher or event. NServiceBus provides
a way for you to intervene in the subscription process and decide whether a given client should be
allowed to subscribe to a given message. You can see this in the SubscriptionAuthorizer class
in the Server project.
Notice that the class implements the IAuthorizeSubscriptions interface, which requires the
methods AuthorizeSubscribe and AuthorizeUnsubscribe. The implementation that comes in the sample
doesn't do very much - returning true in both. In a real project, you may access some Access Control System,
Active Directory, or maybe just a database to decide if the action should be allowed.
As you can see, there is a lot going on under the covers. NServiceBus provides you full control over
every part of the message exchange all the while abstracting the underlying technologies. Try modifying
the sample a bit, adding your own message handlers, and debugging through the various pieces to get
a better feeling for what's going on.
Next Steps
If you have questions about why things work a certain way,
or how best to use NServiceBus on your project,
the best place to go is the discussion group.
Join over 400 developers like you discussing what problems NServiceBus solves for them.
Listen in on architectural discussions about highly available deployments.
Get advice from people who've been working with it for years.
And we'll never (ever) share your email adress with anyone else.
Signing up is quick and easy:
If you don't feel like signing up right now, that's OK too.
How about taking a look at the other samples that come with NServiceBus?
Continue to the samples.