#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 이라는 언어의 범주에서는 꽤 많은 양의 코딩을 해야 한다. 하지만 비동기 통신, 안정적인 메시지 처리, 보내진 순서대로 처리하는 등의 내부적인 처리에 대한 구현하려면 어떤 프로그래머도 꽤 골치가 아플 것이라 생각된다. 아마도 이러한 부분을 감안할 때 비교적 간단히 비동기 메시지 처리 시스템을 구현할 수 있다는 말이 될 것이다.