Dynamic Routing Service
July 31, 2007So how does one deploy Service Broker services in a large enterprise? Hundreds of services that change location every now and then (just enough to create a major outage exactly at the wrong moment!), and each database requiring a route to any service it wishes to interact with. Creating hundreds and thousands of routes and maintaining them as each new service is deployed, is retired or is moved is a difficult task and obviously prone to operational mistakes, pretty much a disaster waiting to happen.
One solution to this problem is to use a ‘dynamic routing service’, as described in the Service Broker routing algorithm http://technet.microsoft.com/en-us/library/ms166052.aspx:
4. If a route to a dynamic routing service is present, and no request for a route to the service is pending, mark the conversation delayed and request routing information from that service.
What is a ‘dynamic routing service’? Is just an ordinary Service Broker service, implemented by your enterprise/environment and responsible to provide routing information for all the services in the environment is deployed in. Whenever the Service Broker needs to route message to a service with an unknown location it will request the ‘dynamic routing service’ to find the location of the unknown service and provide the appropriate routing information. The request for routing information is, obviously, a Service Broker message delivered to the ‘dynamic routing service’ queue. The ‘dynamic routing service’ is expected to actually create a route in the database and then ‘respond’ to the request by ending the dialog.
Example
Let’s see how the ‘dynamic routing’ behaves in practice. So we’ll create a ‘dynamic routing service’ and watch it in action. For this example we’ll consider that a newly deployed ‘MyDepartament/MyService’ application/service needs to exchange messages with the ‘MyEnterprise/Accounting’ service. So go ahead and create this ‘application’:
<p __designer:dtid="281474976710668" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create queue [MyDepartament/MyService/Queue];
</span></span></p> <p __designer:dtid="281474976710674" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create service [MyDepartament/MyService]</p> <p __designer:dtid="281474976710679" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p>
on queue [MyDepartament/MyService/Queue];<p __designer:dtid=”281474976710686”>When this application initiates a dialog with the ‘MyEnterprise/Accounting’ service, messages will just sit in sys.transmission_queue because Service Broker needs to know the location of this service, it needs a route. Let’s have the ‘application’ send a message (for simplicity will use the implicit [DEFAULT] contract and message type):</p>
<p __designer:dtid="281474976710688" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">begin transaction;
</span></span></p> <p __designer:dtid="281474976710694" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">declare @h uniqueidentifier;</p> <p __designer:dtid="281474976710700" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">begin dialog conversation @h</p> <p __designer:dtid="281474976710706" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> from service [MyDepartament/MyService]</p> <p __designer:dtid="281474976710712" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> to service N‘MyEnterprise/Accounting’</p> <p __designer:dtid="281474976710719" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with encryption = off;</p> <p __designer:dtid="281474976710728" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">send on conversation @h (N‘<request account=”FOO” information=”credit limit”></request>’);</p> <p __designer:dtid="281474976710737" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">commit;</p> <p __designer:dtid="281474976710741" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
go<p __designer:dtid=”281474976710743”>If we check the sys.transmission_queue, we found the message there delayed because there is no routing information about the location of the service:</p>
<p __designer:dtid="281474976710745" style="margin: 0in 0in 10pt" class="MsoNormal">select<span __designer:dtid=”281474976710747” style=”font-size: 10pt; line-height: 115%; font-family: ‘Courier New’”> transmission_status from <span __designer:dtid=”281474976710749” style=”color: green”>sys.transmission_queue</span>;</span>
</span></p> <p __designer:dtid="281474976710753" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"><span __designer:dtid=”281474976710754” style=”font-size: 8pt; font-family: ‘Courier New’“>transmission_status</span></p> <p __designer:dtid="281474976710756" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">—————————————</p> <p __designer:dtid="281474976710758" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">The target service name could not be found. Ensure that the service name is specified correctly and/or the routing information has been supplied.</p> <p __designer:dtid="281474976710761" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p> <p __designer:dtid="281474976710764" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
(1 row(s) affected)<p __designer:dtid="281474976710766">Let’s see how things change if we introduce a ‘dynamic routing service’. Note that the service name must be SQL/ServiceBroker/BrokerConfiguration:</p>
<p __designer:dtid="281474976710768" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create queue [SQL/ServiceBroker/BrokerConfiguration/Queue];
</span></span></p> <p __designer:dtid="281474976710774" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create service [SQL/ServiceBroker/BrokerConfiguration]</p> <p __designer:dtid="281474976710779" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> on queue [SQL/ServiceBroker/BrokerConfiguration/Queue]</p> <p __designer:dtid="281474976710785" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> ([http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice]);</p> <p __designer:dtid="281474976710791" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p>
go<p __designer:dtid="281474976710794">This doesn’t change anything so far, because the routing algorithm described at http://technet.microsoft.com/en-us/library/ms166052.aspx says that a route to this service has to exist in order that ‘dynamic routing’ functionality is used. So let’s go ahead and create this route:</p>
<p __designer:dtid="281474976710796" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [SQL/ServiceBroker/BrokerConfiguration]
</span></p> <p __designer:dtid="281474976710801" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with <span __designer:dtid=”281474976710805” style=”color: blue”>service_name = N‘SQL/ServiceBroker/BrokerConfiguration’,</span></p> <p __designer:dtid="281474976710810" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p>
address = N‘LOCAL’;<p __designer:dtid="281474976710818">As soon as we add this route, two messages appear in the ‘dynamic routing service’ queue:</p>
<p __designer:dtid="281474976710820" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">select cast(message_body as xml), message_type_name
</span></p> <p __designer:dtid="281474976710829" style="margin: 0in 0in 10pt" class="MsoNormal"> from [SQL/ServiceBroker/BrokerConfiguration/Queue]</p> <p __designer:dtid="281474976710835" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> message_type_name</p> <p __designer:dtid="281474976710840" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">—————————————————————————————————————————————————————————————————————————————————————- ——————————————————————————————————————————–</p> <p __designer:dtid="281474976710843" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"><span __designer:dtid=”281474976710844” style=”font-size: 8pt; font-family: ‘Courier New’”><MissingRemoteServiceBinding xmlns=”http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRemoteServiceBinding”><SERVICE_NAME>MyEnterprise/Accounting</SERVICE_NAME></MissingRemoteServiceBinding> http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRemoteServiceBinding</span></p> <p __designer:dtid="281474976710847" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"><span __designer:dtid=”281474976710848” style=”font-size: 8pt; font-family: ‘Courier New’”><MissingRoute xmlns=”http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRoute”><SERVICE_NAME>MyEnterprise/Accounting</SERVICE_NAME></MissingRoute> http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRoute</span></p> <p __designer:dtid="281474976710852" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p> <p __designer:dtid="281474976710855" style="margin: 0in 0in 10pt" class="MsoNormal">(2 row(s) affected)</p> <p __designer:dtid="281474976710857">We’ll ignore the message type [http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRemoteServiceBinding] for the moment, so let’s focus on the ‘MissingRoute’ one:</p>
<p __designer:dtid="281474976710860" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">«/span>MissingRoute xmlns=“http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice/MissingRoute“>
</span></span></p> <p __designer:dtid="281474976710870" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> «/span><span __designer:dtid=”281474976710873” style=”font-size: 10pt; color: maroon; font-family: ‘Courier New’“>SERVICE_NAME>MyEnterprise/Accounting</<span __designer:dtid=”281474976710877” style=”color: maroon”>SERVICE_NAME></span></p> <p __designer:dtid="281474976710880" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
</MissingRoute><p __designer:dtid="281474976710884">So the ‘dynamic routing service’ was requested to provide routing information about the service ‘MyEnterprise/Accounting’. The ‘dynamic routing service’ has to find the location of this service, create a route to it and end the conversation on which this request was sent. We’ll cheat for the moment and create route manually, then end the conversation manually:</p>
<p __designer:dtid="281474976710886" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [MyEnterprise/Accounting]
</span></p> <p __designer:dtid="281474976710891" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with </p> <p __designer:dtid="281474976710896" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> <span __designer:dtid=”281474976710899” style=”color: blue”>service_name = N‘MyEnterprise/Accounting’,</span></p> <p __designer:dtid="281474976710904" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> address = N‘tcp://accountingserver.myenterprisedomain.org:4022’;</p> <p __designer:dtid="281474976710912" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">go</p> <p __designer:dtid="281474976710915" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p> <p __designer:dtid="281474976710918" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">begin transaction</p> <p __designer:dtid="281474976710923" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">declare @h uniqueidentifier;</p> <p __designer:dtid="281474976710929" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">receive </p> <p __designer:dtid="281474976710933" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> @h = <span __designer:dtid=”281474976710937” style=”color: blue”>conversation_handle</span></p> <p __designer:dtid="281474976710939" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> from [SQL/ServiceBroker/BrokerConfiguration/Queue];</p> <p __designer:dtid="281474976710945" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">if @h is not null</p> <p __designer:dtid="281474976710952" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">begin</p> <p __designer:dtid="281474976710955" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> end conversation @h;</p> <p __designer:dtid="281474976710962" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">end</p> <p __designer:dtid="281474976710965" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">commit;</p> <p __designer:dtid="281474976710969" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
go<p __designer:dtid="281474976710971">This example shows how the ‘dynamic routing service’ functionality works and how the Service Broker routing algorithm will use this service whenever it needs to find the location of an unknown service. Of course, the difficult part is actually implementing this service correctly 🙂</p> <h2 __designer:dtid="281474976710972">Dynamic Routing Service requirements</h2> <ul __designer:dtid="281474976710973"> <li __designer:dtid="281474976710974">Has to be bound to the ([http://schemas.microsoft.com/SQL/ServiceBroker/BrokerConfigurationNotice] contract in order to accept the MissingRoute messages</li> <li __designer:dtid="281474976710976">Has to be able to actually find the information about the location of any service in the enterprise</li> <li __designer:dtid="281474976710977">Has to create the ‘missing route’ with the information retrieved at the above step</li> <li __designer:dtid="281474976710978">Needs to end incoming request conversations</li> <li __designer:dtid="281474976710979">Has to adapt to any changes in the location of service as services are moved, retired or new services are added</li> </ul> <p __designer:dtid="281474976710980">Although the Service Broker algorithm only looks up a route to the ‘dynamic routing service’ (meaning the service could be located anywhere), the requirement to create the actual ROUTE in the database means that the normal implementation of this service is in the same database where the application is. That is, every deployed database in the enterprise contains a copy of this service. One can think of this service much like an automation script to provide ‘dynamic routing’. This leaves the question on how is the ‘dynamic routing service’ capable to find the location of any service? Usually this implies a central repository where all services are registered and the ‘dynamic routing service’ interrogates this central repository.</p> <p __designer:dtid="281474976710983">As for the requirement to adapt to services being moved and decommissioned, the best approach is simply to rely on a expiration time associated with each request. In fact, the ‘dynamic routing service’ doesn’t even have to implement and track this expiration time; it can simply use the CREATE ROUTE statement itself and provide a lifetime after which the route expires:</p>
<p __designer:dtid="281474976710987" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [MyEnterprise/Accounting]
</span></p> <p __designer:dtid="281474976710992" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with </p> <p __designer:dtid="281474976710997" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> <span __designer:dtid=”281474976711000” style=”color: blue”>service_name = N‘MyEnterprise/Accounting’,</span></p> <p __designer:dtid="281474976711005" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> address = N‘tcp://accountingserver.myenterprisedomain.org:4022’,</p> <p __designer:dtid="281474976711013" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
LIFETIME = 3600;<p __designer:dtid="281474976711019">When the newly created route expires, a new request will be sent to the ‘dynamic routing service’. The actual expiration lifetime value is dependent on how fast is desired that the system reacts to a service being relocated and how often are such relocation expected to occur. Reasonable lifetimes are in the range of 5-10 minutes up to hours. Days would probably be too long and cause long times of unavailability if an unexpired route keeps sending messages to a service at the old location, sorter times will result in too many lookups on the central repository and cause unnecessary contention.</p> <h2 __designer:dtid="281474976711020">The Central Repository</h2> <p __designer:dtid="281474976711021">So how is the ‘dynamic routing service’ supposed to interrogate the central repository about the location of the ‘unknown’ services? Here are some alternatives:</p> <ul __designer:dtid="281474976711022"> <li __designer:dtid="281474976711023">Use Service Broker. Although it seems like a chicken and egg problem, it is not J If Service Broker is used to interrogate the central repository, then the necessary routes to reach the ‘central repository have to be pre-deployed with each database, because otherwise the ‘dynamic routing service’ will not be able to find the location … of the central repository service.</li> <li __designer:dtid="281474976711024">Use Active Directory. The ‘dynamic routing service’ could use an ADSI linked server to query the Active Directory using LDAP. Of course, this means that your Active Directory environment has to contain new schema objects for Service Broker services. The Windows 2003 Active Directory schema contains objects like ‘SQL Server Instance’ and ‘SQL Server Database’, but it does not contain ‘SQL Server Service Broker Service’. Another approach is to use the Active Directory ‘Service-Connection-Point’ objects and provide the service address as the ‘Service-Binding-Information’ property.</li> <li __designer:dtid="281474976711025">Use a file share, simply lookup a well known share where a each known service contains a describing its location (perhaps a ‘service listing’ created with the Service Listing Manager tool)</li> <li __designer:dtid="281474976711026">Use a Web Service</li> </ul> <p __designer:dtid="281474976711027">If I would do such a Central Repository, of course, I would choose option 1 :). The Active Directory option is also interesting, but I have to recon that I could not implement it when I tried. But my knowledge and understanding of AD is quite minimal, and I had to deal with an environment where I was restricted severely in what I could do/modify in the AD (the actual Redmond MS domain). If you implement this successfully, drop me a note about it, I’m actually interested how it’s done.</p> <p __designer:dtid="281474976711028">Another subject of interest is how to keep this Central Repository up to date. One could use Event Notifications for this! Even more, the ‘dynamic routing service’ itself can be extended to accept the [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification] contract and subscribe to the CREATE/ALTER/DROP SERVICE events:</p>
<p __designer:dtid="281474976711031" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create event notification [SQL/ServiceBroker/BrokerConfiguration/Services]
</span></p> <p __designer:dtid="281474976711037" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> on database</p> <p __designer:dtid="281474976711043" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> for create_service, alter_service, drop_service</p> <p __designer:dtid="281474976711050" style="margin: 0in 0in 10pt" class="MsoNormal"> </p>
to service N‘SQL/ServiceBroker/BrokerConfiguration’, ‘current database’;<p __designer:dtid="281474976711060">This event notification subscription will send a message to our service each time a service is created, altered or dropped in the database. Since the ‘dynamic routing service’ has the means to communicate with the Central Repository in the first place, it can use these means to add, modify and drop service information in the repository. The service would also have to deal with services being moved around, either by databases that are detached/attached to a different instance, or by SQL Server host machines being renamed, but I will not drill further into this topic.</p> <h2 __designer:dtid="281474976711061">Gotcha!</h2> <p __designer:dtid="281474976711062">Whenever a route to the ‘dynamic routing service’ is added, a pretty unexpected thing happens: all of the services in the database become all of the sudden unreachable! This is because, according to the routing algorithm described at http://technet.microsoft.com/en-us/library/ms166052.aspx they are usually reached via the default ‘AutoCreatedLocal’ route at step 5 in the algorithm:</p> <p __designer:dtid="281474976711063">5. Find a route that does not specify either the service name or the service broker identifier</p> <p __designer:dtid="281474976711064">But when a ‘dynamic routing service’ is added, the step 4 takes precedence and thus all local services are suddenly ‘delayed’ expecting an answer from the ‘dynamic routing service’! To prevent this from happening, whenever one adds the route to the ‘dynamic routing service’ it should also add an explicit ‘LOCAL’ route to the built-in services:</p>
<p __designer:dtid="281474976711066" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">— Because we enabled dynamic routing, all of the existing
</span></p> <p __designer:dtid="281474976711069" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">— services in the database are sudenly unreacheable, unless an</p> <p __designer:dtid="281474976711072" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">— explicit route is created for them. this includes the built-in services</p> <p __designer:dtid="281474976711075" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">— like Event Notifications and Query Notifications, as well as the </p> <p __designer:dtid="281474976711078" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">— ‘ServiceBroker’ service itself.</p> <p __designer:dtid="281474976711081" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">—</p> <p __designer:dtid="281474976711084" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [http://schemas.microsoft.com/SQL/Notifications/EventNotificationService]</p> <p __designer:dtid="281474976711089" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with <span __designer:dtid=”281474976711093” style=”color: blue”>service_name = N‘http://schemas.microsoft.com/SQL/Notifications/EventNotificationService’,</span></p> <p __designer:dtid="281474976711098" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> address = N‘LOCAL’;</p> <p __designer:dtid="281474976711106" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p> <p __designer:dtid="281474976711109" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [http://schemas.microsoft.com/SQL/Notifications/QueryNotificationService]</p> <p __designer:dtid="281474976711114" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with <span __designer:dtid=”281474976711118” style=”color: blue”>service_name = N‘http://schemas.microsoft.com/SQL/Notifications/QueryNotificationService’,</span></p> <p __designer:dtid="281474976711123" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> address = N‘LOCAL’;</p> <p __designer:dtid="281474976711131" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> </p> <p __designer:dtid="281474976711134" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">create route [http://schemas.microsoft.com/SQL/ServiceBroker/ServiceBroker]</p> <p __designer:dtid="281474976711139" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> with <span __designer:dtid=”281474976711143” style=”color: blue”>service_name = N‘http://schemas.microsoft.com/SQL/ServiceBroker/ServiceBroker’,</span></p> <p __designer:dtid="281474976711148" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal"> address = N‘LOCAL’;</p> <p __designer:dtid="281474976711156" style="margin: 0in 0in 0pt; line-height: normal" class="MsoNormal">go</p>