1. Trang chủ
  2. » Công Nghệ Thông Tin

Microsoft SQL Server 2008 R2 Unleashed- P199 doc

10 105 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 195,87 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

NOTE When a new conversation is created, in addition to being assigned a new conversation or dialog handle, that conversation is also joined to a new conversation group behind the scenes

Trang 1

Planning Conversations Between Services

A conversation is a dialog between two services The purpose of this dialog is, of course, the

sending and receiving of messages, which ultimately leads to the completion of a task

A powerful feature of Service Broker messaging is that it guarantees exactly-once-in-order

(EOIO) messaging This means that messages are sent exactly once; there’s no chance that

a message can be sent twice because of a system issue, so the receiver doesn’t have to

check whether a message has already been processed It also means that messages are

always ordered in their queue in the same order in which they were sent (The

queuing_ordercolumn of the queue indicates this order.) Service Broker makes sure of

this, even in cases in which the send order somehow gets out of sync

Transactions are an integral part of Service Broker conversations When a message is sent

within the scope of a transaction, it is not actually moved to the destination queue unless

the transaction commits This has to do with the fact that before being placed in a queue,

messages are stored in internal tables called transmission queues (which are viewable via the

catalog view sys.transmission_queue) Similarly, a message is not deleted from a queue

after it is received unless the transaction commits (except in cases in which the RETENTION

flag for the queue is set to ON) This point is very important because it means that any

database operations as well as any messaging operations belong to the same transaction,

and they are controlled by the same transactional system This is a unique feature of

messaging with Service Broker and is part of the rationale for having messaging built in to

the database

TheBEGIN CONVERSATION DIALOGstatement is the cornerstone of the process of creating

conversations It specifies the services participating (TO SERVICEandFROM SERVICE) and

the contract to which they will be adhering during the dialog (ON CONTRACT) It also

enables the correlation of messages because it is the thread that relates them to each other

This relationship is achieved through the use of a conversation, or dialog, handle A dialog

handle is a variable of type uniqueidentifierthat identifies the dialog

You use the following syntax to start a dialog:

BEGIN DIALOG [ CONVERSATION ] @DialogHandle

FROM SERVICE InitiatingServiceName

TO SERVICE ‘TargetServiceName’

[ , { ‘service_broker_guid’ | ‘CURRENT DATABASE’ } ]

[ ON CONTRACT ContractName ]

[ WITH

[ { RELATED_CONVERSATION = RelatedDialogHandle |

RELATED_CONVERSATION_GROUP = RelatedConversationGroupId } ]

[ [ , ] LIFETIME = DialogLifetimeInSeconds ]

[ [ , ] ENCRYPTION = { ON | OFF } ] ]

[ ; ]

The items in the syntax are as follows:

Trang 2

@DialogHandle—This is an output parameter of typeuniqueidentifierthat is

returned by the statement You use this option later in this chapter to relate

conversations

InitiatingServiceName—This is the name of the (local) service acting as the initiator

’TargetServiceName’—This is the name of the service acting as the target Note that

this is a case-sensitive string (technically of type nvarchar(256)), for purposes of

name resolution against non–SQL Server services (for later extensions); a byte-level

comparison is made for name resolution If this value is incorrectly provided,

messages remain in the transmission queue Note that

sys.transmission_queue.to_service_nameholds this value

A Service Broker globally unique identifier (GUID) may be optionally specified after

’TargetServiceName’, and it is required when you are doing inter-database

messag-ing (as a later example in this chapter illustrates) The ’CURRENT_DATABASE’string

indicates the current Service Broker GUID

ContractName—This is the name of the contract that the services use

WITH—This clause allows you to specify a related conversation group to which the

current conversation is related, either via a conversation handle or a conversation

group ID

NOTE

When a new conversation is created, in addition to being assigned a new conversation

(or dialog) handle, that conversation is also joined to a new conversation group behind

the scenes, unless the group ID of an existing conversation group is specified

Conversation groups are incredibly important because queues are locked at the

conver-sation group level A queue used by any services in a group of related converconver-sations is

locked on that group during receives, ensuring that messages are always received

seri-ally by all the services in the group BEGIN CONVERSATION DIALOGimplicitly locks the

conversation group it specifies (or the implied group it creates)

If locking did not work this way, a service program could receive a message lower in the

queue order before a second instance of the same service program finished receiving a

message higher in the order If that lower message needed data that was dependent

on the other uncommitted receive, you would end up with a referential integrity issue

It is thus a rather questionable practice to spread related and/or dependent data

across multiple messages or to do so without doing the appropriate checks in the code

The following options are available for the WITHclause:

RELATED_CONVERSATION—This option relates the current conversation to the

conversation group created for the specified conversation handle

Trang 3

RELATED_CONVERSATION_GROUP—This option relates the current conversation to

the conversation group created for the specified conversation group ID (This

has the same effect as the RELATED_CONVERSATIONkeyword, with a different

parameter.) If the value provided for RelatedConversationGroupIdis invalid, a

new conversation group is created to which the dialog is related

LIFETIME—This option specifies the number of seconds for which the dialog

will remain open; it defaults to the maximum value of int, which is

approxi-mately 68 years If this option is specified, both services must call END DIALOG

CONVERSATIONbefore this time is up, or an error is raised

ENCRYPTION—This option specifies whether messages transmitted beyond the

current SQL Server instance (within the conversation) are encrypted It defaults

toON, meaning that message transmissions between databases on different

instances are encrypted by default Encryption requires the use of certificates,

discussed later in this chapter, in the section “Using Certificates for

Conversation Encryption.”

Creating the Conversation Initiator

It’s finally time to create the stored procedure that initiates the dialog between the

services Listing 49.4 contains the code to do this

CREATE PROCEDURE Production.ProductModelUpdate

GO

USE AdventureWorks2008

GO

DROP PROC Production.ProductModelUpdate

GO

CREATE PROCEDURE Production.ProductModelUpdate

(

@ProductId int,

@NewName Name

)

AS

DECLARE @DialogHandle UNIQUEIDENTIFIER

DECLARE @CatalogChangeXml xml (DOCUMENT dbo.CatalogChangeSchema)

DECLARE @RemoteSSBGuid uniqueidentifier

Get the SSB guid for the target service’s db

SELECT @RemoteSSBGuid = service_broker_guid

FROM sys.databases

WHERE name = ‘XCatMgmt’;

BEGIN TRAN;

Trang 4

UPDATE Production.ProductModel

SET Name = pm.Name change this to @NewName to actually modify the data

FROM Production.ProductModel pm

JOIN Production.Product p on

p.ProductModelId = pm.ProductModelId

WHERE p.ProductId = @ProductId;

if @@ERROR != 0

BEGIN

ROLLBACK TRAN

RAISERROR(‘(Initiator) Error during table update’, 16, 1)

RETURN

END;

BEGIN TRY;

WITH XMLNAMESPACES

(

‘http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription’ as p1,

‘http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain’ as wm,

‘http://www.adventure-works.com/schemas/OtherFeatures’ as wf,

‘http://www.w3.org/1999/xhtml’ as html

)

SELECT

@CatalogChangeXml = CatalogDescription.query(‘

for $ContextNode in //p1:ProductDescription,

$SpecNode in $ContextNode/p1:Specifications,

$FeatureNode in $ContextNode/p1:Features

return

<CatalogChangeMessage

xmlns=”urn:www-samspublishing-com:examples:ssb:catalogchange”>

<CatalogChange

ChangeType=”2”

Price=”{sql:column(“p.ListPrice”)}”

ManufacturerId=”1”

Name=”{sql:column(“pm.Name”)}”

SourceProductId=”{sql:column(“p.ProductId”)}”>

<Summary>{$ContextNode/p1:Summary/html:p/text()}</Summary>

<Features>

Handlebars: {$FeatureNode/wf:handlebar/text()}

Wheels: {$FeatureNode/wf:wheel/text()}

BikeFrame: {$FeatureNode/wf:BikeFrame/html:i/text()}

</Features>

<Specifications>

Trang 5

Material: {$SpecNode/Material/text()}

Color: {$SpecNode/Color/text()}

ProductLine: {$SpecNode/ProductLine/text()}

Style: {$SpecNode/Style/text()}

</Specifications>

</CatalogChange>

</CatalogChangeMessage>

‘)

FROM Production.ProductModel pm

JOIN Production.Product p

ON pm.ProductModelId = p.ProductModelId

WHERE p.ProductId = @ProductId

END TRY

BEGIN CATCH

ROLLBACK TRAN

RAISERROR(‘(Initiator) Error during XML production.’, 16, 1)

RETURN;

END CATCH

BEGIN DIALOG CONVERSATION @DialogHandle

FROM SERVICE

[//samspublishing.com/SS2008/SSB/Services/CatalogChangeInitiatorService]

TO SERVICE

‘//samspublishing.com/SS2008/SSB/Services/CatalogMaintenanceService’,

@RemoteSSBGuid

ON CONTRACT

[//samspublishing.com/SS2008/SSB/Contracts/BasicCatalogChangeContract]

WITH ENCRYPTION = OFF;

SEND ON CONVERSATION @DialogHandle

MESSAGE TYPE

[//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeMessage]

(@CatalogChangeXml)

PRINT ‘(Initiator) Message Sent Successfully.’

COMMIT TRAN

ThisUPDATEprocedure exemplifies several key concepts A variable for the dialog handle is

declared for later storage during the call toBEGIN DIALOG After the actual database update,

a typed XML variable—that matches the same XML schema collection as the message type

of which it will become an instance—is populated, using anXQuerystatement

The call to CatalogDescription.query()transforms the original XML into XML that

matches the schema in CatalogChangeSchema This way, if there are any validation errors,

Trang 6

you find out about them before the message is sent (implicitly terminating the open

trans-action) This makes it virtually impossible to send an invalid message

The new value forProductModel.Nameis inserted into the XML via the attribute constructor

Name=”{sql:column(“pm.Name”)}” The value of the attribute ChangeType=”2”corresponds

to the enumeration in the schema whereid=”Update” Because you use this value (as you’ll

soon see), the service forXCatMgmtknows what the sender intended by the message

In the new SENDstatement, the saved GUID for the Service Broker instance on XCatMgmtis

used to locate the target service when sending the message SENDhas the following syntax:

SEND ON CONVERSATION ConversationHandle

[ MESSAGE TYPE MessageTypeName ]

[ ( MessageBody ) ][ ; ]

As you can see, the SENDstatement requires ConversationHandlefor message correlation

The type specified inMessageTypeNamemust match the appropriate type specified in the

contract for the sending service.MessageBodymust be of a data type that can be converted

tovarbinary(max), such as xml

If any issues arise during the sending of a message, you can find the text of the reason

for the problems insys.transmission_queue.transmission_status This is a great place

to look for transmission-related information because the messages in it are reasonably

user-friendly

You also need to consider the use of the END CONVERSATIONstatement, which, predictably,

ends the conversation This is its syntax:

END CONVERSATION ConversationHandle

[

[ WITH ERROR = ErrorPositiveInt DESCRIPTION = ‘ErrorMsg’ ]

|

[ WITH CLEANUP ]

][ ; ]

If desired, you can specify an error message value in ErrorPositiveIntand an error

message of your choosing when ending the conversation Ending a conversation with an

error drops all the unsent messages currently in the transmission queue, and Service

Broker sends a message to the target service of typeError.

You can specify the WITH CLEANUPclause to clean up the transmission queue’s unsent

messages related to the conversation and to clear the queue owned by this service (in this

case,Production.CatalogChangeAckQueue).

Note that until both services in the conversation call END CONVERSATION, the conversation

is not complete When only one side calls END CONVERSATIONor when the LIFETIME

setting of the conversation has been met, the other endpoint can continue to use the

Trang 7

invalid conversation handle until the two sides receive the EndDialogmessage (if messages

are sent after the EndDialogmessage has been received, a runtime error is raised)

Creating the Conversation Target

After you create the initiator, the next step is to create the service program in XCatMgmt

that is activated when messages arrive This step involves a bit more work because this

program needs to receive messages of at least three types (Error,EndDialog, and

CatalogChangeMessage), create and send acknowledgment messages, and perform local

database DML

The example in Listing 49.5 contains a stored procedure that receives correlated messages

from the initiator Note that this procedure is the same (empty) one you specified in

Listing 49.3

CONVERSA-TION in an Service Broker–Activated Stored Procedure

use XCatMgmt

GO

DROP PROC Publication.CatalogChangeQueueReader

GO

CREATE PROCEDURE Publication.CatalogChangeQueueReader

AS

DECLARE @e int, @r int, @MsgTypeName nvarchar(128), @desc varchar(255),

@MsgXml xml, @AckXml xml (DOCUMENT dbo.GenericAcknowledgementSchema),

@RemoteSSBGuid uniqueidentifier, @ErrNS varchar(150), @EndDlgNS varchar(150),

@CatChangeNS varchar(150), @TempXml xml, @NewName varchar(100),

@SourceProductId int, @ChangeType int, @ConversationGroupId uniqueidentifier,

@DialogHandle uniqueidentifier

SET @ErrNS = ‘http://schemas.microsoft.com/SQL/ServiceBroker/Error’

SET @EndDlgNS = ‘http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog’

SET @CatChangeNS = ‘//samspublishing.com/SS2008/SSB/MessageTypes/

CatalogChangeMessage’

Get the SSB guid for the initiator’s db

SELECT @RemoteSSBGuid = service_broker_guid

FROM sys.databases

WHERE name = ‘AdventureWorks2008’;

BEGIN TRAN;

WAITFOR(

GET CONVERSATION GROUP @ConversationGroupId

FROM Publication.CatalogChangeQueue

), TIMEOUT 1000

IF @ConversationGroupId IS NULL

Trang 8

BEGIN

ROLLBACK TRAN

PRINT ‘(Target) ConversationGroupId not acquired in time.’

RETURN

END

ELSE

PRINT ‘(Target) ConversationGroupId acquired successfully.’;

RECEIVE TOP(1)

@MsgXml = CAST(message_body as xml),

@MsgTypeName = message_type_name,

@DialogHandle = conversation_handle

FROM Publication.CatalogChangeQueue

WHERE conversation_group_id = @ConversationGroupId

SELECT @e = @@ERROR, @r = @@ROWCOUNT

IF @r = 0

BEGIN

ROLLBACK TRAN

RETURN

END

IF @e != 0

BEGIN

ROLLBACK TRAN

PRINT ‘(Target) Error during receive.’

RETURN

END

ELSE

PRINT ‘(Target) Message received.’

if the msg is of type Error, end the conversation, stating the error

IF @MsgTypeName = @ErrNS

BEGIN

SELECT @desc = @MsgXml.value(‘

declare default element namespace

“http://schemas.microsoft.com/SQL/ServiceBroker/Error”;

(/Error/Description/text())[1]’, ‘varchar(255)’)

ROLLBACK TRAN

PRINT ‘(Target) Error message received.’

END CONVERSATION @DialogHandle

WITH ERROR = 808 DESCRIPTION = @desc

RETURN

END

Trang 9

if the msg is of type EndDialog, end the conversation without error

IF @MsgTypeName = @EndDlgNS

BEGIN

PRINT ‘(Target) EndDialog message received.’;

END CONVERSATION @DialogHandle

RETURN

END

if the msg is of type CatalogChangeMessage, update appropriately

IF @MsgTypeName = @CatChangeNS

BEGIN

BEGIN TRY

what kind of change is requested?

(here we only deal with product name and xml changes)

;WITH XMLNAMESPACES

(

DEFAULT ‘urn:www-samspublishing-com:examples:ssb:catalogchange’

)

SELECT

@ChangeType = @MsgXml.value(‘

(/CatalogChangeMessage/CatalogChange/@ChangeType)[1]’, ‘int’),

@NewName = @MsgXml.value(‘

(/CatalogChangeMessage/CatalogChange/@Name)[1]’, ‘varchar(100)’),

@SourceProductId = @MsgXml.value(‘

(/CatalogChangeMessage/CatalogChange/@SourceProductId)[1]’, ‘int’)

IF @ChangeType IS NULL OR @NewName IS NULL OR @SourceProductId IS NULL

BEGIN

ROLLBACK TRAN

PRINT ‘(Target) An xml-selected value is NULL.’

RETURN

END

IF @ChangeType = 2 “Update”

BEGIN

UPDATE Publication.Product

SET

ProductName = @NewName,

ProductDetailXml = @MsgXml

WHERE @SourceProductId = SourceProductId

IF @@ERROR != 0 OR @@ROWCOUNT = 0

BEGIN

ROLLBACK TRAN

PRINT ‘(Target) Failure during table update.’

RETURN

END

Trang 10

SET @AckXml = ‘

<Ack

xmlns=”urn:www-samspublishing-com:examples:ssb:genericack”

ResultCode=”1”>

<ResultMessage ContentId=”’ + CAST(@SourceProductId as varchar(10)) + ‘“

MsgType=”1”>Success!</ResultMessage></Ack>’;

SEND ON CONVERSATION @DialogHandle

MESSAGE TYPE

[//samspublishing.com/SS2008/SSB/MessageTypes/GenericAck]

(@AckXml)

PRINT ‘(Target) Message Sent Successfully.’

END

END TRY

BEGIN CATCH

ROLLBACK TRAN

SELECT @desc = ERROR_MESSAGE()

INSERT dbo.TargetErrs SELECT @desc simple error storage table

PRINT ‘(Target) Caught error:’ + @desc

END CONVERSATION @DialogHandle

WITH ERROR = 808 DESCRIPTION = @desc

RETURN

END CATCH

END

COMMIT TRAN

One issue you might notice when testing the code in Listing 49.5 is that the PRINT

state-ments in the activated procedure do not show up in the SSMS query window You need to

use SQL Profiler (which is discussed in Chapter 6, “SQL Server Profiler”) to aid in

debug-ging activated code because it always runs on background threads To help you out, a

Service Broker event group is available in SQL Profiler for tracing all the new Service

Broker events’ message transmission, activation, conversation beginning and ending, and

so on You can use this new event group along with the T-SQL and stored procedure event

groups to trace the code path Print statements are included in the code in Listing 49.5 to

make debugging easier

The code in Listing 49.5 introduces three new SQL statements: GET CONVERSATION DIALOG,

RECEIVE, and GET CONVERSATION GROUP.

The purpose ofGET CONVERSATION DIALOGis to lock the next available conversation group

associated with the messages inPublication.CatalogChangeQueue Conventional use of

GET CONVERSATION DIALOGrequires that theWAITFORstatement be used to make the

initi-ated program wait a specified number of milliseconds (or infinitely, as specified in

TIMEOUT) before continuing on in the program logic If the conversation group ID has been

Ngày đăng: 05/07/2014, 02:20

TỪ KHÓA LIÊN QUAN