#title 간단한 메시지 송/수신 예제 [[TableOfContents]] ==== 개요 ==== 도움말의 Service Broker의 역할을 보면 다음과 같은 그림을 볼 수 있다. Service Broker에 대한 개념을 설명한 그림으로 우편물을 보내는 것에 비유하고 있다. 이것을 구현해 보자. 편의상 그림 왼쪽의 보내는 사람을 ‘Sender’라 하고, 오른쪽의 받는 사람을 ‘Receiver’라 하겠다. attachment:sssb01.jpg ==== 응용 프로그램 기능 ==== 1. Person 테이블에 존재하는 사람들끼리 쪽지를 주고 받는다. 2. 만약 받는 사람이 존재하지 않으면 ‘수취인불명’ 메시지를 Sender에게 전달한다. ==== 시나리오 ==== 1. ‘이재학’이 ‘윤주형’에게 메시지를 보낸다. 2. Person 테이블에 받는 사람인 ‘윤주형’이 존재하면 Msg 테이블에 메시지를 저장 3. 만약 받는 사람인 ‘윤주형’이 Person 테이블에 존재하지 않으면 Sender인 ‘이재학’에게 ‘수취인 불명’ 메시지를 보낸다. 4. ‘이재학’이 메시지를 수신하게 되면 ‘시스템관리자’ 로부터 ‘수취인불명’ 메시지를 받게 된다. ==== 테스트 환경 ==== attachment:sssb02.jpg {{{ IF OBJECT_ID('Msg') IS NOT NULL DROP TABLE Msg; IF OBJECT_ID('Person') IS NOT NULL DROP TABLE Person; CREATE TABLE Person( PersonName varchar(20) PRIMARY KEY ); INSERT Person VALUES('시스템관리자'); --처음부터기본적으로테이블에들어있어야한다. INSERT Person VALUES('이재학'); INSERT Person VALUES('한로사'); INSERT Person VALUES('윤주형'); INSERT Person VALUES('김태헌'); CREATE TABLE Msg( Sender varchar(20) , Receiver varchar(20) , Msg nvarchar(max) , ReceiveDT datetime ); GO --관계설정 ALTER TABLE dbo.Msg WITH CHECK ADD CONSTRAINT FK_Msg_Person FOREIGN KEY(Sender) REFERENCES dbo.Person (PersonName) GO ALTER TABLE dbo.Msg CHECK CONSTRAINT FK_Msg_Person GO ALTER TABLE dbo.Msg WITH CHECK ADD CONSTRAINT FK_Msg_Person1 FOREIGN KEY(Receiver) REFERENCES dbo.Person (PersonName) GO ALTER TABLE dbo.Msg CHECK CONSTRAINT FK_Msg_Person1 GO }}} <소스1> ==== Contract 및 Service, Message Queue 생성 ==== 1. Message Type 생성 2. Contract 생성 3. Queue 생성 4. Service 생성 {{{ CREATE MESSAGE TYPE SenderMsg VALIDATION = NONE; CREATE MESSAGE TYPE ReceiverMsg VALIDATION = NONE; CREATE CONTRACT DeliveryMsgContract ( SenderMsg SENT BY initiator , ReceiverMsg SENT BY target ); CREATE QUEUE SendedMsgQueue; CREATE QUEUE ReceivedMsgQueue; CREATE SERVICE SendMsgService ON QUEUE ReceivedMsgQueue(DeliveryMsgContract); CREATE SERVICE ReceiveMsgService ON QUEUE SendedMsgQueue(DeliveryMsgContract); GO }}} <소스2> ==== Message 보내는 응용 프로그램(이재학이 윤주형에게 메시지를 보낸다) ==== {{{ --메세지보내기 DECLARE @handle uniqueidentifier , @Sender varchar(20) , @Receiver varchar(20) , @Msg nvarchar(max); SET @Sender = '이재학'; SET @Receiver = '윤주형'; SET @Msg = N'하이~ 방가방가(^^)/'; BEGIN TRAN; BEGIN DIALOG CONVERSATION @handle FROM SERVICE SendMsgService TO SERVICE 'ReceiveMsgService' ON CONTRACT DeliveryMsgContract WITH ENCRYPTION = OFF , LIFETIME = 600; SEND ON CONVERSATION @handle MESSAGE TYPE SenderMsg (N' ' + @Sender + '' + ' ' + @Receiver + ' '); COMMIT; GO }}} <소스3> ==== 메시지를 받아서 Msg 테이블에 저장하는 응용 프로그램 ==== {{{ --일반메세지받기 DECLARE @handle uniqueidentifier , @message_body xml , @Sender varchar(20) , @Receiver varchar(20) , @Msg nvarchar(max) , @PersonName varchar(20); BEGIN TRY BEGIN TRAN; WHILE(1=1) BEGIN --보낸편지를받는다. RECEIVE TOP(1) @handle = conversation_handle , @message_body = CONVERT(nvarchar(max), message_body) FROM SendedMsgQueue; IF @@ROWCOUNT = 1 BEGIN --DECLARE @message_body xml; --SET @message_body = ' -- -- yasi -- mkex -- -- '; SELECT @Sender = A.Sender , @Receiver = A.Receiver , @Msg = A.Msg , @PersonName = B.PersonName FROM ( SELECT x.item.value('Sender[1]', 'varchar(20)') AS Sender , x.item.value('Receiver[1]', 'varchar(20)') AS Receiver , x.item.value('Message[1]', 'nvarchar(max)') AS Msg FROM @message_body.nodes('/Letter') AS x(item)) A LEFT OUTER JOIN Person B ON A.Receiver = B.PersonName; IF @PersonName IS NOT NULL BEGIN INSERT Msg(Sender, Receiver, Msg, ReceiveDT) VALUES(@Sender, @Receiver, @Msg, GETDATE()); END ELSE BEGIN SEND ON CONVERSATION @handle MESSAGE TYPE ReceiverMsg (N' ' + ' ' + @Sender + ' '); END CONVERSATION @handle; END END ELSE BEGIN END CONVERSATION @handle; BREAK; END END COMMIT; END TRY BEGIN CATCH IF (XACT_STATE()) = -1 ROLLBACK; ELSE IF (XACT_STATE()) = 1 COMMIT; /* SELECT ERROR_NUMBER() AS ErrorNumber , ERROR_SEVERITY() AS ErrorSeverity , ERROR_STATE() AS ErrorState , ERROR_PROCEDURE() AS ErrorProcedure , ERROR_LINE() AS ErrorLine , ERROR_MESSAGE() AS ErrorMessage , GETDATE() AS ErrorDT */ END CATCH; GO --메시지 도착 확인 SELECT * FROM Msg; }}} attachment:sssb03.jpg <소스4> ==== 수취인 불명’ 테스트 ==== 1. <소스3>부분의 @Receiver를 다음과 같이 수정 2. <소스4>를 수행하여 메시지를 받고, 결과를 확인 3. <소스5>를 수행하여 수취인 불명 메시지를 수신하고, 결과 확인 {{{ --메세지보내기 DECLARE @handle uniqueidentifier , @Sender varchar(20) , @Receiver varchar(20) , @Msg nvarchar(max); SET @Sender = '이재학'; SET @Receiver = '없는놈'; --고친부분 SET @Msg = N'하이~ 방가방가(^^)/'; }}} <소스5> '''수취인 불명 메시지 처리''' {{{ --수취인 불명 메세지 DECLARE @handle uniqueidentifier , @message_body xml , @Sender varchar(20) , @Receiver varchar(20) , @Msg nvarchar(max) , @PersonName varchar(20) , @message_type sysname; BEGIN TRY BEGIN TRAN; WHILE(1=1) BEGIN --보낸편지를받는다. RECEIVE TOP(1) @handle = conversation_handle , @message_body = CONVERT(nvarchar(max), message_body) , @message_type = message_type_name FROM ReceivedMsgQueue; IF @@ROWCOUNT = 1 BEGIN SELECT @Sender = A.Sender , @Receiver = A.Receiver , @Msg = A.Msg FROM ( SELECT x.item.value('Sender[1]', 'varchar(20)') AS Sender , x.item.value('Receiver[1]', 'varchar(20)') AS Receiver , x.item.value('Message[1]', 'nvarchar(max)') AS Msg FROM @message_body.nodes('/Letter') AS x(item)) A LEFT OUTER JOIN Person B ON A.Receiver = B.PersonName; IF @message_type = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' BEGIN END CONVERSATION @handle; END ELSE IF @message_type = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error' BEGIN END CONVERSATION @handle; END ELSE IF @Sender IS NOT NULL BEGIN INSERT Msg(Sender, Receiver, Msg, ReceiveDT) VALUES(@Sender, @Receiver, @Msg, GETDATE()); END ELSE BEGIN END CONVERSATION @handle; END END ELSE BEGIN END CONVERSATION @handle; BREAK; END END COMMIT; END TRY BEGIN CATCH IF (XACT_STATE()) = -1 ROLLBACK; ELSE IF (XACT_STATE()) = 1 COMMIT; /* SELECT ERROR_NUMBER() AS ErrorNumber , ERROR_SEVERITY() AS ErrorSeverity , ERROR_STATE() AS ErrorState , ERROR_PROCEDURE() AS ErrorProcedure , ERROR_LINE() AS ErrorLine , ERROR_MESSAGE() AS ErrorMessage , GETDATE() AS ErrorDT */ END CATCH; GO --메시지 도착 확인 SELECT * FROM Msg; }}} attachment:sssb04.jpg <소스6> ==== 결론 ==== 간단히 SQL Server 2005의 Service Broker의 비동기 메시지 송/수신 과정을 구현해 보았다. 실제로 다른 SQL Script보다는 복잡한 것이 사실이다. 많은 문서에서는 ‘간단히 구현’ 할 수 있다고 떠들고 있지만 사실 SQL 이라는 언어의 범주에서는 꽤 많은 양의 코딩을 해야 한다. 하지만 비동기 통신, 안정적인 메시지 처리, 보내진 순서대로 처리하는 등의 내부적인 처리에 대한 구현하려면 어떤 프로그래머도 꽤 골치가 아플 것이라 생각된다. 아마도 이러한 부분을 감안할 때 비교적 간단히 비동기 메시지 처리 시스템을 구현할 수 있다는 말이 될 것이다.