Service Broker services communicate with each other with messages via Dialog: bidirectional conversations between two services, or, to use the proper term,
Conversation Endpoints (Initiating Endpoint and Target Endpoint).
Every Service Broker message belongs to a dialog. Every dialog is automatically assigned a
conversation group: a logical grouping of related dialog conversations.
Each dialog conversation agrees to a contract that specifies which service, or conversation endpoint, can send messages
to one another as well as what Message Types can be used with each message. Message Types define what type of data the
body of a message must contain (or not contain).
When Service Broker delivers messages, it does so in an Exactly-Once-In-Order (EOIO) fashion. This means that exactly
one conversation group worth of dialog conversation worth of messages is delivered to a receiving application at a time.
When messages are retrieved from a receiving application, an entire conversation group worth of dialog conversations is
delivered to it. Each message is marked with a message_sequence_number integer.
The first message of each dialog sent has a message_sequence_number of 0. The second
has a message_sequence_number of 1, and so forth. Dialogs, on the other hand, are marked with
a send_sequence and receive_sequence integer. The first dialog of each conversation group
has a send_sequence of 0. The second has a send_sequence of 1, and so forth.
receive_sequence pertains to how dialogs are delivered to an application receiving messages from a queue.
You don't need to concern yourself with conversation groups in most cases. By default, each dialog is assigned its
own conversation group. This will only change if you manually move an existing dialog conversation from one
conversation group to another. Dialogs and Conversation Groups are identified by UNIQUEIDENTIFIER values.
This ensures that the probability of any two dialogs or conversation groups having the same identifier (called a handle)
is very, very limited.
Dialog conversations are made between two Service Broker services, or Conversation Endpoints.
The Service that has begun a dialog conversation with another Service is called the Initiator. The receiving
Service for that dialog conversation is called the Target. When you send messages to a Service Broker
Service, you're said to be initiating a dialog conversation with it. The initiating Service is interchangeably
termed the Initiator, or Initiating Endpoint, whereas, the Service where the messages are arriving is interchangeably
termed the Target, or Target Endpoint. Because you can send and receive messages to and from the same Service Broker
Service, it's more appropriate to call each end of a dialog conversation a Conversation Endpoint and not a Service.
The Service is simply the interface through which Service Broker sends messages to and from different Conversation
Endpoints. Initiating and Target Conversation Endpoints can exist in the same Service (same database) or completely
different Services (different databases or even different instances of SQL Server). Dialog conversations between two
services is uniquely tracked by means of a conversation_id - a UNIQUEIDENTIFIER value. This identifier exists
at both Conversation Endpoints of a Dialog Conversation.
When Services are created, they're assigned one or more Contracts. Contracts define who can send messages of a particular
Message Type: the Initiator or the Target. Message Types, on the other hand, define what
type of data the body of a message must contain (or not contain). When a dialog is created, it's attached to a Contract.
This means that every message sent through that dialog must obey the rules of the Contract, and since each message is
attached to exactly one Message Type, each message much obey the rules of its assigned Message Type. Each Service Broker
Service created is assigned at least one Contract. The Service ensures that each messages it receives for delivery matches
and obeys its assigned list of Contracts.
Each Message is held in a Queue, which is essentially a table in the database. They're held there
until they're retrieved or, if retention is on, the dialog conversations that they belong to are ended explicitly.
When an application retrieves messages, it receives them one conversation group at a time. Every time a
message is sent or received, the conversation group to which it (they) belongs is locked. During the lock,
no other services can send or receive messages to and from the locked conversation group. This feature
enforces concurrency.
You start dialog conversations by using a T-SQL
BEGIN DIALOG CONVERSATION statement.
You send messages by using a T-SQL
SEND statement.
You end dialog conversations by using a T-SQL END CONVERSATION statement.
You retrieve message by using a T-SQL RECEIVE statement.
Example: Sending messages from one database service to another service in another database:
-- Tested with SQL Server 2005 9.00.852
-- This example sends one message from a service
-- on one database to another service in another
-- database.
USE DotNetFun;
DECLARE @DialogHandle uniqueidentifier,
@BrokerGUID uniqueidentifier;
-- The Target service on another database:
SELECT @BrokerGUID = service_broker_guid
FROM sys.databases
WHERE database_id = DB_ID('DotNetFun2');
BEGIN DIALOG CONVERSATION @DialogHandle
FROM SERVICE DotNetFunService
TO SERVICE 'DotNetFun2Service', @BrokerGUID
ON CONTRACT DotNetFunContractSentByAny;
SEND ON CONVERSATION @DialogHandle
MESSAGE TYPE DotNetFunMessageTypeValidateNone
('HELLO!');
/*
NOTE: MAKE SURE that the MESSAGE TYPE and CONTRACT for
the initiating service also exist in the target service.
If their names are different, the message will NOT be
delivered and will be stored in the transmission queue:
sys.transmission_queue
*/
-- End the conversation if you don't want any more
-- messages to be added to this dialog conversation:
END CONVERSATION @DialogHandle;
As you can see, each message that's sent belongs to a dialog conversation, and each dialog conversation must use a
predefined Contract. Contracts
are rules that define who (the initiator, the target or both) can send messages to another service.
Each contract must have at least one
Message Type associated
with it. Message Types define what type of data the body of a message can contain (or not contain).
Message Types can be either well-formed XML, empty, valid XML with schema or any type.
When messages are sent to a target service, they're validated for matching and compatible Contracts and Message Types.
Not only must the Initiating Contracts and Message Type names be the same as that of the Target, but they must also be
compatible. A message sent using a message type in one service must have the same name and type in the target service.
Contracts and Message Types make up the rules that two services must adhere to when they send and receive
messages to each another.