Service Broker を使用すると SQL Server のインスタンス間で順番保障されたメッセージの交換を行えます。単一DB内でのメッセージ交換は[SQL Server 2012] Service Brokerで単一データベース内でメッセージ交換してみたでやってみました。今回はSQL Server インスタンスが異なる DB 間でメッセージを交換する処理を下記チュートリアルに従ってやってみました。その覚書を記載します。ほぼチュートリアルと同じですが、ENDPOINT間の認証がうまくいかなかったので、CONNECT権の付与を追加しています。
インスタンス間のメッセージ交換の完了
http://msdn.microsoft.com/ja-jp/library/bb839483(v=sql.105).aspx
冒頭にも記載しましたが、基本的に チュートリアルのまんまです。
動作確認環境
- ADサーバー1 台, SQL Server 2012 がインストールされた メンバサーバー 2台
- OS はすべて SQL Server 2012
- SQL Server 2012 は既定のインスタンスとしてインストールしています
前提条件
配信側、配信先 SQL Server のマシン上で次の設定が行われていることとします。
-
ファイアウォール(FW) の受信設定 、具体的には検証時にはTCP 1433,1434,4022 ポート を受信許可しました。
(名前付きSQL Server インスタンスの場合は sqlserverのexe に受信許可設定をしてください。) - SQL Server の通信プロトコルで TCP/IP が有効になっている
本サンプルでは、発信先のDBは TargetDB, 発信側のDB を InitiatorDB として作成しています。そのほかマスターキーに使用するパスワードなど適宜 自分の環境に合うように置き換えて読んでください。
1. 発信先でService のエンドポイントの作成からサービスの作成まで
発信先のSQL Server のインスタンスで次のことをします。
-
master DB で Service Broker 用の エンドポイントを作成
ポートは 4022, WIndows 認証を使用するようにしています。(この場合 SQL Server インスタンスのユーザーになります。) - 発信先DB(TargetDB)を作成
- 発信先DBにマスターキーの作成
-
発信先サービス用のユーザー作成(ログインにマップしない)。そのユーザー(TargetUser)に証明書を関連付けます。
公開鍵証明書は 発信側で作成するTargetUserに関連付けるためにファイルに退避しています。 - メッセージ交換用に メッセージ型、コントラクト、受信キュー、サービスを定義します。サービスの所有者を TargetUser にしています。
サービスの所有者を TargetUser にし、 TargetUserに証明書を関連付けるのはなんでだろうとおもったのですが、CREATE REMOTE SERVICE BINDINGの説明で納得しました。CREATE REMOTE SERVICE BINDING は リモートサービスに接続するユーザーを必須でWITH USER 句で指定するようですが、その説明で次のように記載されています。
USER = user_name
TO SERVICE 句のリモート サービスに関連付けられた証明を所有しているデータベース プリンシパルを指定します。この証明は、リモート サービスと交換されるメッセージの暗号化と認証に使用されます。
-- 発信先SQL Serverでの作業 -- 前提:FW 1433,1434,4022 受信許可設定済み -- SQL Server で TCP接続プロトコルを有効化済み use master; GO -- エンドポイントの作成 IF EXISTS (SELECT * FROM master.sys.endpoints WHERE name = N'TestTargetEndpoint') DROP ENDPOINT TestTargetEndpoint; GO -- AUTHENTICATIONがWINDOWSなので SQL Server サービスインスタンスのユーザー CREATE ENDPOINT TestTargetEndpoint STATE = STARTED AS TCP ( LISTENER_PORT = 4022 ) FOR SERVICE_BROKER (AUTHENTICATION = WINDOWS ); GO -- DB の作成, Service Broker は既定で有効化されている IF EXISTS (SELECT * FROM sys.databases WHERE name = N'TargetDB') DROP DATABASE TargetDB; GO -- DB の作成 CREATE DATABASE TargetDB; GO USE TargetDB; GO -- TargetDBにマスターキーの作成 IF NOT EXISTS(SELECT * FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##') BEGIN CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'passwordXXX!'; END; GO -- 発信先サービス用のユーザー作成 CREATE USER TargetUser WITHOUT LOGIN; GO -- サービス用のユーザーに証明書を関連付ける CREATE CERTIFICATE TargetUserCertificate AUTHORIZATION TargetUser WITH SUBJECT = 'Target Certificate', EXPIRY_DATE = N'12/31/2099'; -- 発信側で参照するために公開鍵証明書を退避 BACKUP CERTIFICATE TargetUserCertificate TO FILE = N'C:\work\ServiceBrokerSample01\TargetUserCertificate.cer'; GO -- メッセージ型の作成 CREATE MESSAGE TYPE [//Test/Sample1/RequestMessage] VALIDATION = WELL_FORMED_XML; CREATE MESSAGE TYPE [//Test/Sample1/ReplyMessage] VALIDATION = WELL_FORMED_XML; GO -- コントラクトの作成 CREATE CONTRACT [//Test/Sample1/SimpleContract] ([//Test/Sample1/RequestMessage] SENT BY INITIATOR, [//Test/Sample1/ReplyMessage] SENT BY TARGET ); GO -- 発信先のキュー作成 CREATE QUEUE TestTargetQueue; GO -- 発信先のサービス作成(所有者を TargetUser)にする -- 後ほどの処理で発信側から発信先サービスへのRemote Binding -- を作成するが、このときに使用するユーザーとして指定するのが -- (退避した証明書を関連付けた)発信側で作成されたTargetUserとなる。 CREATE SERVICE [//TestTargetDB/Sample1/TargetService] AUTHORIZATION TargetUser ON QUEUE TestTargetQueue ([//Test/Sample1/SimpleContract]); GO
2. 発信側でエンドポイントの作成から サービスの作成まで
発信側のSQL Server のインスタンスで次のことをします。
-
master DB で Service Broker 用の エンドポイントを作成
ポートは 4022, WIndows 認証を使用するようにしています。(この場合 SQL Server インスタンスのユーザーになります。) - 発信側DB(InitiatorDB)を作成
- 発信側DBにマスターキーの作成
-
発信側サービス用のユーザー作成(ログインにマップしない)。そのユーザー(InitiatorUser)に証明書を関連付けます。
公開鍵証明書は 発信先で作成するInitiatorUserに関連付けるためにファイルに退避しています。 - メッセージ交換用に メッセージ型、コントラクト、受信キュー、サービスを定義します。サービスの所有者を InitiatorUser にしています。
メッセージ型、コントラクトは発信先,発信側で同じになるように定義してください。
-- 発信側SQL Serverでの作業 -- 前提:FW 1433,1434,4022 受信許可設定済み -- SQL Server で TCP接続プロトコルを有効化済み use master; GO -- エンドポイントの作成 IF EXISTS (SELECT * FROM master.sys.endpoints WHERE name = N'TestInitiatorEndpoint') DROP ENDPOINT TestInitiatorEndpoint; GO -- AUTHENTICATIONがWINDOWSなので SQL Server サービスインスタンスのユーザー -- ポートは4022 を使用する CREATE ENDPOINT TestInitiatorEndpoint STATE = STARTED AS TCP ( LISTENER_PORT = 4022 ) FOR SERVICE_BROKER (AUTHENTICATION = WINDOWS ); GO -- DB の作成, Service Broker は既定で有効化されている IF EXISTS (SELECT * FROM sys.databases WHERE name = N'InitiatorDB') DROP DATABASE InitiatorDB; GO CREATE DATABASE InitiatorDB; GO USE InitiatorDB; GO -- InitiatorDBにマスターキーの作成 IF NOT EXISTS(SELECT * FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##') BEGIN CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'passwordxxxx!'; END; GO -- 発信側サービス用のユーザー作成 CREATE USER InitiatorUser WITHOUT LOGIN; GO -- サービス用のユーザーに証明書を関連付ける CREATE CERTIFICATE InitiatorUserCertificate AUTHORIZATION InitiatorUser WITH SUBJECT = 'Initiator Certificate', EXPIRY_DATE = N'12/31/2099'; -- 発信先で参照するために公開鍵証明書を退避 BACKUP CERTIFICATE InitiatorUserCertificate TO FILE = N'C:\work\ServiceBrokerSample01\InitiatorUserCertificate.cer'; GO -- メッセージ型の作成(発信先と同じであること) CREATE MESSAGE TYPE [//Test/Sample1/RequestMessage] VALIDATION = WELL_FORMED_XML; CREATE MESSAGE TYPE [//Test/Sample1/ReplyMessage] VALIDATION = WELL_FORMED_XML; GO -- コントラクトの作成(発信先と同じであること) CREATE CONTRACT [//Test/Sample1/SimpleContract] ([//Test/Sample1/RequestMessage] SENT BY INITIATOR, [//Test/Sample1/ReplyMessage] SENT BY TARGET ); GO -- 発信側のキュー作成 CREATE QUEUE TestInitiatorQueue; GO -- 発信側のサービス作成(所有者をInitiatorUser)にする -- 後ほどの処理で発信先から発信側サービスへのRemote Binding -- を作成するが、このときに使用するユーザーとして指定するのが -- (退避した証明書を関連付けた)発信先で作成されたInitiatorUserとなる。 -- コントラクトを指定しないことで発信先としてサービスが使用される -- ことを明示的に指定している。 CREATE SERVICE [//TestInitiatorDB/Sample1/InitiatorService] AUTHORIZATION InitiatorUser ON QUEUE TestInitiatorQueue; GO
ここまでで サービスの定義まで終わりました。
3. 発信側で発信先サービスへの参照設定
インスタンスが異なる DB でメッセージを交換する場合は、 発信先、発信側でルーティングの設定が必要になります。発信先、発信側サービスで認証が各サービスのユーザーで認証されるようにリモートバインディングの設定も行います。
発信側DBで次のことを行います。
- 発信先サービス接続用のユーザー(TargetUser)を作成し、退避した公開鍵証明書と関連付ける
- 発信先、発信側サービスへのルーティング設定を登録する
- 発信先サービスへのリモートバインディングを定義しユーザーを 発信先サービス接続用のユーザー TargetUser にします。
ルーティングの設定は、 発信先サービスへは InitiatorDB, ローカルの配信側サービスのルーティングは msdb に作成するので注意してください。発信先のサービスのADDRESSのポートはService Broker のエンドポイント作成時に指定したポートになります。
-- 発信側での作業 USE InitiatorDB; GO -- 発信先Service接続用のユーザー作成 CREATE USER TargetUser WITHOUT LOGIN; -- 発信先で退避した公開先証明書を使用して証明書を作成し -- 発信側で作成した TargetUser に関連付ける CREATE CERTIFICATE TargetCertificate AUTHORIZATION TargetUser FROM FILE = N'C:\work\ServiceBrokerSample01\TargetUserCertificate.cer' GO -- ルートの作成 -- 発信先サービスへのRouteを作成。発信先DBのエンドポイントアドレスをADDRSSに指定する CREATE ROUTE TestTargetRoute WITH SERVICE_NAME = N'//TestTargetDB/Sample1/TargetService' , ADDRESS = 'TCP://SBW2012SQL02:4022'; use msdb; GO -- 発信側サービスへのRouteを作成。ADDRESSはLOCALを指定する CREATE ROUTE TestInitiatorRoute WITH SERVICE_NAME = N'//TestInitiatorDB/Sample1/InitiatorService' , ADDRESS = 'LOCAL'; use InitiatorDB; GO -- リモートサービスバインディングの作成 -- WITH USER で指定するのが 発信先サービスのユーザーとして -- 認証されるように作成した公開鍵証明書が関連付けられた -- InitiatorDBのTargetUser CREATE REMOTE SERVICE BINDING TestTargetBinding TO SERVICE N'//TestTargetDB/Sample1/TargetService' WITH USER = TargetUser;
4. 発信先で発信側サービスへの参照設定
発信側で実施したのと同じく、発信先からも発信側サービスへの参照、ルーティングの設定、リモートバインディングの作成を行います。
発信先DBで次のことを行います。
- 発信側サービスへの接続用のユーザー(InitiatorUser)を作成し、退避した公開鍵証明書と関連付ける
- 発信側、発信先サービスへのルーティング設定を登録する
- 発信側サービスへのリモートバインディングを定義しユーザーを 発信側サービス接続用のユーザー InitiatorUserにします。
ルーティングの設定は、 発信側サービスへの登録は TargetDB, ローカルの発信先サービスのルーティングは msdb に作成するので注意してください。発信側のサービスのADDRESSのポートは発信側でService Broker のエンドポイント作成時に指定したポートになります。
-- 発信先での作業 USE TargetDB; GO -- 発信側Service接続用のユーザー作成 CREATE USER InitiatorUser WITHOUT LOGIN; -- 発信側で退避した公開鍵証明書を使用して証明書を作成する -- 発信先で作成した InitiatorUser に関連付ける CREATE CERTIFICATE InitiatorCertificate AUTHORIZATION InitiatorUser FROM FILE = N'C:\work\ServiceBrokerSample01\InitiatorUserCertificate.cer' GO -- リモートサービスへのルートを作成 -- 発信側サービスへのRouteを作成。ADDRSSに発信側SQL Serverのエンドポイントを指定する CREATE ROUTE TestInitiatorRoute WITH SERVICE_NAME = N'//TestInitiatorDB/Sample1/InitiatorService' , ADDRESS = 'TCP://SBW2012SQL01:4022'; use msdb; GO -- 発信先サービスへのRouteを作成。ADDRESSはLOCALを指定する CREATE ROUTE TestTargetRoute WITH SERVICE_NAME = N'//TestTargetDB/Sample1/TargetService' , ADDRESS = 'LOCAL'; GO use TargetDB; GO -- 発信先サービスでInitiator に SEND 権限を付与する -- ないと以下のエラー -- Service Broker received an error message on this conversation. Service Broker will not transmit the message; -- it will be held until the application ends the conversation. -- D 29975 //TestTargetDB/Sample1/TargetService TargetDB -- ユーザー InitiatorUser は、サービス //TestTargetDB/Sample1/TargetService で SEND 権限がありません GRANT SEND ON SERVICE::[//TestTargetDB/Sample1/TargetService] TO InitiatorUser; -- リモートサービスバインディングの作成 -- WITH USER で指定するのが 発信側サービスのユーザーとして -- 認証されるように作成した公開鍵証明書が関連付けられた -- TargetDBのInitiatorUser CREATE REMOTE SERVICE BINDING TestInitiatorBinding TO SERVICE N'//TestInitiatorDB/Sample1/InitiatorService' WITH USER = InitiatorUser;
以上でチュートリアルまでの準備完了。
このまま実行すると次のようなエラーが発生しました。
An error occurred while receiving data: '10054(既存の接続はリモート ホストに強制的に切断されました。)'.
とか
Connection handshake failed. The login 'DomainName\SQLServiceインスタンスの実行ユーザー' does not have CONNECT permission on the endpoint. State 84.
なので、発信先、発信側でSQL Server インスタンスの実行ユーザーのログインを作成し、各エンドポイントへの接続許可を設定しました。
5. CONNECT 権限の付与
今回は、発信先、発信側 の各SQL Server で次のコマンドを実行します。
-- 発信先での操作 CREATE LOGIN [DomainName\SQLServiceのユーザー] FROM WINDOWS; GO GRANT CONNECT ON ENDPOINT::[TestTargetEndpoint] TO [DomainName\SQLServiceのユーザー]; GO
-- 発信先での操作 CREATE LOGIN [DomainName\SQLServiceのユーザー] FROM WINDOWS; GO GRANT CONNECT ON ENDPOINT::[TestInitiatorEndpoint] TO [DomainName\SQLServiceのユーザー]; GO
ここまででメッセージ交換が行えるようになりました。
6. メッセージ交換のテスト
あとはチュートリアルに従って 発信側でメッセージ交換の開始、発信先でメッセージの受信と応答メッセージの送信とメッセージ交換終了処理、発信側で応答メッセージの受信とメッセージ交換終了処理を実施します。
6.1 発信側からメッセージ交換開始
USE InitiatorDB; GO DECLARE @InitDlgHandle UNIQUEIDENTIFIER; DECLARE @RequestMsg NVARCHAR(100); BEGIN TRANSACTION; BEGIN DIALOG @InitDlgHandle FROM SERVICE [//TestInitiatorDB/Sample1/InitiatorService] TO SERVICE N'//TestTargetDB/Sample1/TargetService' ON CONTRACT [//Test/Sample1/SimpleContract] WITH ENCRYPTION = ON; SELECT @RequestMsg = N'<RequestMsg>Message for Target service.</RequestMsg>'; SEND ON CONVERSATION @InitDlgHandle MESSAGE TYPE [//Test/Sample1/RequestMessage] (@RequestMsg); SELECT @RequestMsg AS SentRequestMsg; COMMIT TRANSACTION; GO
6.2 発信先でのメッセージ受信と応答メッセージ送信とメッセージ交換終了処理
-- 発信先処理 -- メッセージを受信し応答メッセージ送信 USE TargetDB; GO DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER; DECLARE @RecvReqMsg NVARCHAR(100); DECLARE @RecvReqMsgName sysname; BEGIN TRANSACTION; WAITFOR ( RECEIVE TOP(1) @RecvReqDlgHandle = conversation_handle, @RecvReqMsg = message_body, @RecvReqMsgName = message_type_name FROM TestTargetQueue ), TIMEOUT 1000; SELECT @RecvReqMsg AS ReceivedRequestMsg; IF @RecvReqMsgName = N'//Test/Sample1/RequestMessage' BEGIN DECLARE @ReplyMsg NVARCHAR(100); SELECT @ReplyMsg = N'<ReplyMsg>Message for Initiator service.</ReplyMsg>'; SEND ON CONVERSATION @RecvReqDlgHandle MESSAGE TYPE [//Test/Sample1/ReplyMessage] (@ReplyMsg); END CONVERSATION @RecvReqDlgHandle; END SELECT @ReplyMsg AS SentReplyMsg; COMMIT TRANSACTION; GO
6.3 発信側での応答メッセージ受信とメッセージ交換終了処理
USE InitiatorDB; GO DECLARE @RecvReplyMsg NVARCHAR(100); DECLARE @RecvReplyDlgHandle UNIQUEIDENTIFIER; BEGIN TRANSACTION; WAITFOR ( RECEIVE TOP(1) @RecvReplyDlgHandle = conversation_handle, @RecvReplyMsg = message_body FROM TestInitiatorQueue ), TIMEOUT 1000; If @RecvReplyDlgHandle is not null END CONVERSATION @RecvReplyDlgHandle; -- Display recieved request. SELECT @RecvReplyMsg AS ReceivedReplyMsg; COMMIT TRANSACTION; GO
メッセージはXML形式で送信しています。xmlに対する処理は[SQL Server 2012] Service Brokerで単一データベース内でメッセージ交換してみたの発信先のメッセージ応答処理や次のMSDNドキュメントを参考にして下さい。
xml データ型のメソッド
http://msdn.microsoft.com/ja-jp/library/ms190798.aspx
7 まとめ
説明は以上です。私の環境ではメッセージ交換が行えるようになりました。 エンドポイント間の認証は Windows 認証を使用していますが証明書を使うパターンも時間があればやってみようと思います。
もしうまくいかない場合は、マスターキーや証明書の作成先やルートの作成先などが間違っていないことなどを確認してください。またそのほか転送キュー(sys.transmission_queue) や各受信キューの確認、SQL Service Broker や 受信キューが無効になっていないことを確認してください。
そのほか、 SQL Service Profier で Broker 関連のイベントと Audito のBroker 関連のイベントをチェックしてメッセージを確認してください。ssbdiagnose ツールを使用して メッセージ交換中にエラーが発生しないことを確認してください。次のようなコマンドを実行してメッセージ交換処理を行うだけでも簡単な診断を行えます。
ssbdiagnose runtime connect to -S SQLServerインスタンスA connect to -S SQLServerインスタンスB
さんのコメント: さんのコメント: