비동기적으로 작업을 수행하는 것은 실행 결과를 기다리지 않고 실행하는 것입니다. 이러한 유형의 처리는 많은 시간을 요하는 작업과 실행이 끝날 때까지 기다리지 않고 다른 작업을 계속 해나갈 수 있도록 해줍니다.
사용자들의 요청이 긴 시간의 처리를 필요로 하지만 사용자에게 즉각적인 응답 처리를 제공하길 원하는 웹의 경우와 같이, 비동기 처리는 많은 상황에서 사용하기에 이상적입니다. 사용자의 요청을 비동기적으로 처리함으로써, 시스템은 요청이 실제로 실행하기에 얼마나 긴 시간이 소요되는지에 상관없이 응답할 수 있게 됩니다.
Microsoft® .NET 응용프로그램에 비동기 처리를 추가하기 위한 다양한 방법이 존재합니다. 예를 들어, Microsoft Exchange 2000 또는 Microsoft BizTalk 서버의 워크플로우 기능의 장점을 이용할 수 있을 것입니다. COM Interop을 이용해 COM+ 대기열을 사용하는 구성요소(Queued Component)를 이용할 수 있습니다. 또는 MSMQ 메시지 대기열을 이용해 직접 처리 작업을 수행할 수도 있습니다. 향후 진행될 아키텍처 중심의 주제 기사에서는 깊이 있게 이들 대안들을 비교할 것입니다.
BizTalk 과 COM+ 대기열을 사용하는 구성요소(Queued Component)에서 사용되는 MSMQ는 마이크로소프트 윈도우 NT 4.0 옵션 팩과 마이크로소프트 윈도우 2000 서버 버전의 일부분입니다. 윈도우 NT 4.0 워크스테이션, 윈도우 2000 프로페셔널, 윈도우 XP 프로페셔널 운영체제에서, 이 기사에서 다뤄지게 될 지역 개인 대기열(local private queue)를 액세스하기 위해 MSMQ를 이용할 수 있습니다. MSMQ를 이용해 응용프로그램은 데이터를 대기열에 위치시키도록 할 수 있습니다. 그리고 응용프로그램(다른 응용프로그램이던지 대기열에 데이터를 적재한 응용프로그램이던지 간에)이 데이터를 액세스할 때까지, 데이터는 대기열에 계속해서 유지될 것입니다. 실제로 작업을 동기적으로 처리하는 것에 비해 작업을 나타내는 메시지(예를 들자면, 처리할 주문정보 따위)를 대기열에 위치시킴으로써, 주 시스템은 단지 대기열에 게시할 때 요구되는 시간만큼만 지연시간이 발생합니다. 여러 다른 종류의 시스템은 메시지를 대기열에 게시할 수 있으며, 같은 메시지를 검색하도록 사용될 수 있습니다. 그래서 응용프로그램에 확장성을 제공할 수 있는 것입니다.
이 기사에서는, MSMQ를 이용한 작업을 하기 위해 .NET 프레임워크에 의해 제공되는 System.Messaging 클래스를 어떻게 사용하고, 응용프로그램에 비동기 워크플로우를 어떻게 추가할 수 있는지에 대해 설명할 것입니다.
이미 언급한 바와 같이, MSMQ는 윈도우 NT 4.0, 윈도우 2000, 윈도우 XP 프로페셔널에서 모두 사용 가능합니다. 그러나 이 예제에서는 MSMQ가 설치된 윈도우 2000 서버를 보유하고 있다고 가정할 것입니다. 이 기사에서의 예제들은 이 서버가 .NET 코드가 실행되는 장비와 같다고 가정할 것입니다. 그러나, 실제 시스템에서는 이 코드는 분리된 별도 서버에 위치한 메시지 대기열들에 연결해야 할 지 모릅니다.
대기열을 이용한 작업
메시지 대기열을 사용하기 전에 메시지 대기열을 설정할 수 있어야만 합니다. 이를 위해서, 응용프로그램에서 유일무이하고 일관성 있게 대기열을 묘사하는 방법이 필요합니다. .NET은 특정 대기열을 액세스 하기 위한 세가지 다른 방법을 제공합니다.
경로를 이용하여 대기열을 설정하기
첫 번째 방법은 대기열의 경로(형식 : <servername>\private$\<queuename>예 : liquidsoap\private$\Orders)를 이용하는 것입니다. 실제 서버명 또는 로컬 서버를 표현하는 "."과 대기열에 대한 전체 경로를 지정해야 합니다.
포맷명을 이용해 대기열을 설정하기
두 번째 옵션은 포맷명(Format Name) 입니다. 포맷명은 연결 상세정보와 대기열의 경로를 사용하거나 메시지 대기열을 고유하게 구별하는 GUID(globally unique identifier)를 사용해 대기열을 표현하는 문자열입니다.
라벨을 이용해 대기열을 설정하기 Specify a queue by label
마지막으로, 대기열을 설정하는 세 번째 방법은 대기열의 라벨(예:"My Orders")을 사용하는 것입니다. 라벨은 소스 코드 또는 MSMQ 관리용 인터페이스를 이용해 직접 지정할 수 있는 값입니다.
라벨이나 경로를 이용하는 방법을 사용하는 것은 추가적인 부하를 야기시킵니다. 왜냐하면, MSMQ는 경로나 라벨 같은 설명정보를 내부적으로 개인 대기열(private queue)을 설명하도록 사용되는 포맷명(Format Name)으로 분석, 변환해야만 하기 때문입니다. 이름 확인 작업을 피하기 위해 포맷명을 직접 사용하는 것은 더 효율적인 방법이며, 대기열이 오프라인일 때 클라이언트 응용프로그램이 기능에 접근할 수 있도록 원한다면, 실제로 대기열을 참조할 수 있는 유일한 방법입니다.
참고 메시지 대기열의 오프라인 사용은 이 기사에서 다뤄지지 않습니다. 메시지 대기열에 대한 폭넓은 범위에 대한 이해를 위해서 플랫폼 SDK의 MSMQ 섹션을 참조하십시오.
이름을 이용해 대기열을 설정한 후에, System.Messaging.MessageQueue 개체의 인스턴스를 생성해야만 합니다. 코드의 첫 부분에 "Imports System.Messaging"을 작성했다고 가정한다고 하면, 개체를 구분하기 위해 대기열의 경로를 사용해 MessageQueue 개체의 새로운 인스턴스를 생성합니다.
Private Const QUEUE_NAME As String = ".\private$\Orders"
Dim msgQ As New MessageQueue(QUEUE_NAME)
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 1을 참조합니다. (다운로드는 기사의 첫부분에서)
실제 서버명을 사용하는 대신 대기열의 경로에서 로컬 머신을 나타내는 "."을 사용했음에 주목하십시오. 장비에서 MSMQ에 정의된 "Orders"라는 이름의 개인 대기열(private queue)을 가지고 있는 한, 이 코드는 성공적으로 그 대기열에 대한 개체 참조를 획득할 것입니다. 더욱 튼튼한 응용프로그램을 위해서, 확인 작업이 비교적 많은 시간을 소요하는 작업이라 할지라도, 대기열이 존재하는지 확인하고 대기열이 존재하지 않으면 자동적으로 생성하도록 할 수 있습니다.
프로그래밍을 통한 대기열 생성하기
MSMQ 인터페이스는 메시지 대기열을 생성, 삭제, 탐색하는 것을 가능하게 하지만, System.Messaging 네임스페이스를 이용해서 대기열에 관한 작업을 할 수 있습니다. 몇 가지 공유 메서드(static method - 클래스의 인스턴스를 생성하지 않고도 메서드를 호출할 수 있다는 의미) 는 MessageQueue 클래스에서 대기열이 존재하는지 확인하기 위해 Exist 메서드, 새로운 대기열을 생성하기 위해 Create 메서드, 대기열을 삭제하기 위해 Delete 메서드와 같은 기능을 제공하고 있습니다. 이러한 메서드를 사용해, 응용프로그램은 대기열의 존재 여부를 확인해 대기열이 존재하지 않으면 자동으로 대기열을 생성하도록 할 수 있습니다. 다음에 제시된 GetQ라는 함수는 방금 설명한 기능을 정확하게 제공합니다.
Private Function GetQ(ByVal queueName As String) As MessageQueue
Dim msgQ As MessageQueue
' 대기열이 존재하지 않으면 대기열을 생성합니다.
If Not MessageQueue.Exists(queueName) Then
Try
' MessageQueue 개체를 이용해 메시지 대기열을 생성합니다.
msgQ = MessageQueue.Create(queueName)
Catch CreateException As Exception
' 코드가 대기열을 생성하기에 충분치 않은 권한을 가지고 있지 않을 때,
' 대기열을 생성 중에 예외를 발생시킵니다.
Throw New Exception("Error Creating Queue", CreateException)
End Try
Else
Try
msgQ = New MessageQueue(queueName)
Catch GetException As Exception
Throw New Exception("Error Getting Queue", GetException)
End Try
End If
참고 BDAdotNetAsync1.vb 샘플 코드에서 GetQ를 참조합니다. (다운로드는 기사의 첫부분에서)
이 유형의 코드는 배치를 간단하게 합니다. 왜냐하면 이런 유형의 코드는 미리 대기열을 만들어 놓을 필요성이 없기 때문입니다. 그러나 Exists 메서드의 호출에 따른 관련 대가(trade-off)는 대기열이 설치 과정에 의해 생성되고 응용프로그램이 대기열이 이미 존재한다고 추정할 수 있다면, 더 좋은 성능을 보장할 수 있다는 것을 의미합니다. 또한 적어도 하나의 메시지가 대기열에 위치하게 될 때까지 대기열이 존재하지 않을 수 있기 때문에, 수신 코드가 필요한 대기열의 존재를 확인해야만 한다는 점을 주목할 필요가 있습니다.
참고 대기열을 생성하고 삭제하는 것은 특정 보안 권한을 필요로 합니다. 그리고 기본 값으로 대기열의 생성자/소유자 또는 관리자에 의해서만 대기열을 생성,삭제하는 작업이 가능합니다. 만일 충분한 권한을 가지고 있지 않다면, "액세스가 거부되었습니다"라는 메시지와 함께 예외가 발생될 것입니다.
대기열 삭제하기
사용하기에 유용한 공유 메서드(static method)는 Delete 메서드입니다. 그리고 Create 메서드와 Exists 메서드와 마찬가지로 경로를 이용해 대기열을 액세스합니다. 이 경우에 서버에서 대기열을 찾고 대기열을 제거하기 위해 Create나 Exist 메서드에서 사용한 경로를 사용합니다. 물론 Delete 메서드가 현재 대기열에 저장된 메시지까지 모두 삭제하여 중요한 데이터의 손실을 야기시킬 수 있기 때문에, 보다 철저한 작업이 요구됩니다. 대기열이 성공적으로 삭제되는 것을 보장하기 위해, 아래 보여지는 코드와 유사한 에러 처리 구조 안에 Delete 메서드를 호출하는 코드를 배치하십시오.
Try
' 지정된 대기열을 삭제합니다.
MessageQueue.Delete(queuePath)
Catch e As Exception
' 호출자에게 예외 정보를 전달합니다.
Throw New Exception("Deletion Failed", e)
End Try
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 2와 DeleteQ를 참조합니다. (다운로드는 기사의 첫부분에서)
메시지의 전송
System.Messaging 네임스페이스는 메시지를 전송하기 위해 간단한 방법과 복합적인 방법을 모두 제공합니다. 두 방법간의 차이점은 메시지 그 자체가 아니라, 메시지 포맷과 전달방법을 제공하는 각 방법의 제어 정도에 있습니다. 두 가지 중 한 방법을 사용해, 원하는 유형의 개체를 전송할 수 있습니다. 이 기사에서의 예제는 String, DataSet 개체와 예제 코드 내에 선언된 클래스 타입의 개체를 전송할 것입니다.
간단한 방법
간단한 방법을 이용해 대기열에 메시지를 전송하는 것은 단지 몇가지 단계를 필요로 합니다. 첫째, (대기열의 경로, 포맷명 또는 라벨을 사용하여) 적절한 메시지 대기열에 대한 참조를 획득합니다. 그리고 그 후, 전송하고자 하는 개체와 메시지를 위한 라벨을 공급하기 위해 MessageQueue 개체의 Send 메서드를 사용합니다. 메시지가 전달되는 방법을 제어하는 모든 옵션은 MessageQueue 개체의 기본값을 사용할 것입니다. 따라서, 개발자가 어떤 옵션도 지정할 필요가 없습니다. 아래 코드는 이미 이 기사 앞부분에서 설명한 바 있는 GetQ 프로시져를 사용하여, 지역 개인 대기열(local private queue)에 간단한 문자열 메시지를 전송하는 방법을 보여줍니다.
Private Const QUEUE_NAME As String = ".\private$\Orders"
Dim msqQ As MessageQueue
Dim msgText As String
' 텍스트 메시지를 준비합니다.
msgText = String.Format("Sample Message Sent At {0}", DateTime.Now())
Try
' 대기열을 위한 MessageQueue 개체를 획득합니다.
msqQ = GetQ(QUEUE_NAME)
' 대기열에 메시지를 전송합니다.
msqQ.Send(msgText)
Catch e As Exception
' 에러를 처리합니다.
…
End Try
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 3을 참조합니다. (다운로드는 기사의 첫 부분에서)
또한 메시지에 라벨을 지정하기 위해, Send 메서드를 호출할 때 두 번째 파라미터가 포함될 수 있습니다.
msgQ.Send(sMessage, "Sample Message")
윈도우 NT 에서의 MSMQ 탐색기 또는 윈도우 2000, 윈도우 XP의 "컴퓨터 관리" 도구를 사용해 서버에 존재하는 대기열을 살펴보고, 각 대기열에서 개인적인 메시지를 조사할 수 있습니다. 메시지에 라벨을 지정하였다면, 그 라벨은 메시지 뷰에 나타날 것입니다. 그렇지 않으면, 라벨 영역은 비어 있는 형태로 나타납니다.
그림 1. 윈도우 XP 컴퓨터 관리에서의 MSMQ 창
개인 메시지의 내용을 보기 위해서, 메시지를 마우스 오른쪽 클릭 후 속성을 클릭하고, 그 후 본문 탭을 클릭합니다. 기본값으로 설정된 모든 옵션을 사용한 간단한 방법의 경우에, 전송한 어떤 개체라도 모두 아래 예와 그림2와 유사한 형태의 XML로 직렬화됩니다.
<?xml version="1.0"?>
<string>Sample Message Sent At 5/25/2001 3:51:48 PM</string>
그림 2. XML로 직렬화된 메시지 본문
참고 XML은 메시지를 전송하는 기본 포맷입니다. 그러나 다른 포맷을 선택하는 것이 가능합니다. 메시지를 직렬화하기 위해 사용하는 포맷을 변경하는 것은 "복합적인 방법" 섹션에서 다뤄집니다.
어떤 개체라도 Send 메서드를 이용해 개체를 전송함으로써 간단히 전송됩니다. 인수로서 제공되는 어떠한 것도 앞서 기술한 대로 XML로 직렬화될 것입니다. 그리고 메시지 본문에 위치하게 될 것입니다. String, Integer, Double 과 같은 기본적인 데이터 타입 이외에도, DataSet과 사용자에 의해 생성된 응용프로그램 내부에서 사용되는 특정한 개체와 같은 개체들도 일반적으로 MSMQ를 통해 전송될 수 있습니다. 이들 개체 모두를 전송하는 예제는 다음 내용에서 제공됩니다. 그리고 System.Messaging 클래스를 사용해 어떤 유형의 정보를 보내는 것이 얼마나 쉬운 일인지 보여주게 됩니다.
DataSet을 전송하기 위해, 정말로 달리 해주어야 할 필요성이 있는 작업은 단지 DataSet 개체 인스턴스를 만들고, MessageQueue 개체의 Send 메서드로 DataSet 개체를 전송하는 것입니다. 이전에 전송했던 String 데이터와 마찬가지로 DataSet 개체는 XML로 직렬화되고 메시지 본문에 위치하게 될 것입니다.
Dim msgQ As MessageQueue
Dim msgText As String
Dim dSet As DataSet
' DataSet 개체를 생성하는 외부 코드
dSet = GetDataSet()
' 대기열을 처리하기 위한 MessageQueue 개체를 획득합니다.
msgQ = GetQ(QUEUE_NAME)
' "Order" 라벨을 붙여 DataSet 개체를 포함한 메시지를 전송합니다.
msgQ.Send(dSet, "Order")
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 4를 참조합니다. (다운로드는 기사의 첫 부분에서)
만일 시스템이 Order 클래스와 같은 데이터를 보유할 수 있는 자체 클래스를 가지고 있다면, DataSet 개체를 전송할 때 작성된 코드와 마찬가지로 MSMQ를 통해 그 개체들도 전송할 수 있습니다. .NET 메시징 클래스들은 개체 인스턴스들을 XML로 직렬화하고 메시지에 직렬화된 XML을 추가하는 것을 돕습니다. 다음의 예제에서, Order 클래스의 인스턴스가 생성되고 개체의 속성이 지정됩니다. 그 후 MessageQueue 개체의 Send 메서드는 이 개체를 메시지로 바꾸어 대기열에 추가하기 위해 사용됩니다. 메시지를 전송하는 복합적인 방법을 논의한 후에는, 대기열로부터 전송된 입력 메시지를 처리할 때 또 다른 유형의 개체를 액세스하는 방법을 다룰 것입니다.
Dim msgQ As MessageQueue
Dim myOrder As New Order()
' Order 개체의 정보를 채웁니다.
myOrder.CustomerID = "ALKI"
myOrder.ID = 34
myOrder.ShipDate = DateTime.Now()
'대기열을 액세스하기 위한 MessageQueue 개체를 획득합니다.
msgQ = GetQ(QUEUE_NAME)
' 대기열에 Order 개체 정보를 포함한 메시지를 전송합니다.
msgQ.Send(myOrder, "Order")
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 5를 참조합니다. (다운로드는 기사의 첫 부분에서)
복합적인 방법(Complex Method)
대부분의 목적상 Send 메서드가 적합하긴 하지만, 전송 과정을 좀더 세밀하게 제어하는 것이 때때로 필요합니다. 개체를 직접 전송하기 위해, Send 메서드를 사용하는 것을 대신할 대안은 System.Messaging.Message 클래스의 인스턴스를 이용해 작업을 수행하는 것입니다. 단일 MSMQ 메시지를 나타내는 Message 개체를 생성함으로써, 포맷팅, 암호화, Time-out 등에 대한 옵션을 설정할 수 있으며, 간단히 Send 메서드를 이용하여 얻을 수 있는 유용함 이상의 다양한 추가적인 속성을 액세스할 수 있도록 합니다.
설정하길 원할지도 모르는 주요한 메시지 옵션 중의 하나는 전송을 위해 메시지가 어떻게 직렬화되는지를 제어합니다. 기본적으로, 이것은 개체를 XML 표현식으로 직렬화하고 대부분의 목적에 잘 부합되는 XmlMessageFormatter를 사용하도록 설정되어 있습니다. 이 밖에도 BinaryMessageFormatter와 ActiveXMessageFormatter의 두 가지 포맷터(Formatter)가 사용가능 합니다. 그러나 두 가지 모두 개체를 XML처럼 사람이 알아볼 수 있는 형태가 아닌 binary포맷으로 직렬화합니다. Microsoft ActiveX® formatter 는 기본 형식 또는 COM 개체를 마이크로소프트 Visual Basic 6.0 응용프로그램과 같이 NET 시스템이 아닌 곳으로 전달할 때 사용됩니다. 그리고 그런 기능이 요구되지 않을 때는 사용하지 않는 편이 좋습니다. BinaryMessageFormatter는 기본값인 XmlMessageformatter보다 대체로 처리속도가 빠릅니다. 따라서, MSMQ 작업의 성능이 가장 중요한 문제라면 고려해보는 것이 좋습니다. XML 대신에 binary 포맷을 사용함으로써 치르는 대가를 고려하는 것은 중요한 일입니다. 성능은 향상되는 반면, 메시지 내용을 직접 읽어서 파싱하는 것은 어려워집니다.
참고 메시지 전송을 위해 어떤 포맷터를 선택하더라도, 대기열에서 메시지를 수신하기 위해서는 전송할 때 사용했던 것과 같은 포맷터를 사용해야만 합니다.
다음의 코드 예제는 메시지를 전송하기 위해 미리 설정된 Message 개체와 함께 MessageQueue 개체의 Send 메서드를 이용하기 전에 포맷팅과 암호화 작업을 포함하는 몇 가지 옵션을 설정하는 "복합적인 방법"을 어떻게 사용하는지를 보여줍니다.
Dim msgQ As MessageQueue
' 전송을 위한 새로운 Message 개체를 생성합니다.
Dim myMsg As New Message()
' Order 클래스의 새로운 인스턴스를 생성합니다.
Dim myOrder As New Order()
' Order 개체의 데이터를 채웁니다.
myOrder.CustomerID = "ALKI"
myOrder.ID = 34
myOrder.ShipDate = DateTime.Now()
msgQ = GetQ(QUEUE_NAME)
With myMsg
.Label = "Order"
.Formatter = New BinaryMessageFormatter()
.AppSpecific = 34
.Priority = MessagePriority.VeryHigh
.Body = myOrder
End With
' Order 클래스의 인스턴스를 이미 포함한 메시지 개체를 전송합니다.
msgQ.Send(myMsg)
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 6을 참조합니다. (다운로드는 기사의 첫 부분에서)
이 예제에서는, BinaryMessageFormatter가 기본으로 설정된 XmlMessageFormatter 대신에 사용되고 있기 때문에, 전송되는 클래스인 Order 클래스는 ISerializable 인터페이스를 지원하거나 <Serializable()> 특성으로 작성될 필요가 있습니다. 완성된 코드 샘플에서는 특성(Attribute)이 사용되었습니다.
메시지의 수신
대기열에 메시지가 적재되자마자, 누군가가 그 메시지를 검색하여 추가된 주문에서 대기열에서 데이터를 꺼내길 원할지도 모른다. 대기열에서 메시지를 획득하고, 메시지가 포함하고 있는 데이터를 액세스하는 것은 몇 가지 중요한 단계를 필요로 합니다.
첫 번째, 메시지를 전송할 때와 마찬가지로, 적절한 대기열을 가리키고 있는 MessageQueue 개체를 획득합니다.
Dim msgQ As New MessageQueue(QUEUE_NAME)
다음으로, 대기열에서 첫 번째 메시지를 획득하기 위해 MessageQueue 개체의 Receive 메서드를 사용합니다.
Dim myMessage As Message
myMessage = msgQ.Receive()
그런 후, 만일 기본값인 XML 포맷터를 사용하고 있다면, 메시지에 포함된 사용 가능한 데이터 타입의 목록을 이용해 Message 개체의 포맷터를 설정합니다. 이번 경우에서는 하나의 사용 가능한 데이터 타입으로 String 타입을 사용합니다.
Dim targetTypes(0) As Type
targetTypes(0) = GetType(String)
myMessage.Formatter = New XmlMessageFormatter(targetTypes)
마지막으로 데이터를 검색하기 위해 Body 속성을 사용합니다. 얻어낸 데이터는 적절한 형식으로 캐스팅합니다.
Dim msgText As String
msgText = CStr(myMessage.Body)
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 7을 참조합니다. (다운로드는 기사의 첫 부분에서)
Time-out 값 설정하기
앞서 단지 나열된 단계들은 모두 메시지를 액세스하기 위해 필요한 것들입니다. 그러나 지금 현재 이전 코드로서는 메시지가 사용 가능한 상태가 될 때까지 msgQ.Receive 를 호출한 상태로 다른 작업을 차단하거나 작업이 정체된 상태로 있게 됩니다. 만일, 대기열이 아무런 메시지를 포함하고 있지 않다면, 프로그램은 정해지지 않은 시간 동안 그 코드의 라인에서 기다리는 상태가 될 것입니다. Receive 메서드는 파라미터로 System.TimeSpan 개체를 제공함으로써, Time-out 값을 설정하는 것을 가능하게 합니다. 몇 가지 각기 다른 생성자중의 하나를 사용하여, Receive 메서드를 호출할 때 필요한 TimeSpan 개체를 생성할 수 있습니다.
objMessage = objQ.Receive(New TimeSpan(0, 0, 30))
앞의 코드 라인은 TimeSpan 생성자의 3가지 파라미터를 사용하여 30초로 Time-out 값을 설정한 것입니다. 그러나 어떤 시간 양이라도 설정할 수 있습니다. 이 기능을 사용하고 Receive 메서드를 호출할 때 Time-out 값을 설정한다면, 에러 처리 루틴에서 이 메서드 호출 코드를 try 영역으로 감쌀 수 있습니다. 왜냐하면, 만일 정해진 시간이 다 되기 전에 아무런 메시지가 수신되지 않는다면, 에러가 발생될 것이기 때문입니다.
Dim myMessage As Message
Try
myMessage = msgQ.Receive(New TimeSpan(0, 0, 30))
Catch eReceive As MessageQueueException
Console.WriteLine("{0} ({1})", _
eReceive.Message, _
eReceive.MessageQueueErrorCode.ToString)
End Try
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 8을 참조합니다. (다운로드는 기사의 첫 부분에서)
특정 포맷터 사용하기
이 과정은 XML 포맷터가 사용되는 한 전송한 데이터 타입과는 관계없이 동일합니다. 만일 BinaryMessageFormatter 와 같은 다른 포맷터가 사용된다면, 메시지 본문을 액세스하기 전에, 메시지가 올바른 포맷터를 사용하도록 설정해야 합니다. 다음 코드는 바이너리 포맷터를 사용해 Order 클래스의 인스턴스를 액세스하는 것을 보여줍니다.
Dim msgQ As MessageQueue
Dim myOrder As Order
' 대기열을 액세스하기 위한 MessageQueue 개체를 획득합니다.
msgQ = GetQ(QUEUE_NAME)
' BinaryMessageFormatter를 설정합니다.
msgQ.Formatter = New BinaryMessageFormatter()
' 대기열에서 메시지를 읽어내서 Order 개체로 형변환합니다.
myOrder = CType(msgQ.Receive().Body, Order)
' Order 개체를 이용한 작업을 수행합니다.
Console.WriteLine(myOrder.CustomerID)
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 9를 참조합니다. (다운로드는 기사의 첫 부분에서)
SendMessage 프로시져는 메시지를 전송하는데 또 다른 다양한 방법을 사용합니다. MessageQueue 개체의 Formatter 속성을 설정함으로써, 간단한 전송을 위해 사용되는 직렬화를 제어할 수 있습니다.
이 전체 기사는, 프로세스가 완료되는 동안 아무것도 기다리지 않는 것을 확신시키는 비동기적 작업 처리에 관한 내용을 다룹니다. 그러나 퇴근 몇몇 예제에서 사용된 Receive 메서드는 완전히 동기적인 것입니다. 이전의 코드는 메시지 대기열로부터 전체 메시지가 액세스되는 동안 기다려야만 합니다. 이러한 지연 현상은 작성한 코드 어디에서도 눈에 띌만한 것은 아니었습니다. 그러나 메시지의 사이즈가 4MB에 이를 수 있으며(DataSet을 전송한다고 가정하면4MB에 이르는 것은 불가능하지 않습니다..), 프로그램의 메모리 공간에 모든 데이터가 채워지는 동안, 기다리길 원하지 않을 것입니다. 큰 사이즈의 메시지와 느린 네트워크 연결을 고려하여, MessageQueue 개체는 Receive 메서드 이외에도 메시지를 수신하기 위해 비동기적인 방법을 제공합니다. 메시지를 비동기적으로 수신하기 위해, 적적한 대기열에 대한 개체 참조를 획득하고 그 후 Receive 메서드를 호출하는 대신, (필요하다면 Time-out 속성을 설정하여) BeginReceive 메서드를 호출할 것입니다. 이것은 수신 과정을 시작하고 다음 코드와 같이 즉시 실행을 진행시킵니다.
Private Const QUEUE_NAME As String = "liquidsoap\private$\Orders"
Private WithEvents msgQ As MessageQueue
'대기열을 액세스하기 위한 MessageQueue 개체를 획득합니다.
msgQ = GetQ(QUEUE_NAME)
' 메시지를 읽기 시작합니다.
msgQ.BeginReceive()
메시지가 성공적으로 액세스되었을 때, MessageQueue 개체의 ReceiveCompleted 이벤트가 발생되고 이벤트 핸들러가 호출될 것입니다. 액세스된 메시지는 파라미터 형태로 이벤트 핸들러에 전송되고, 실제 데이터를 액세스하기 위해 그 개체를 사용할 수 있습니다.
Public Sub msgQ_ReceiveCompleted(ByVal sender As Object, _
ByVal e As ReceiveCompletedEventArgs) _
Handles msgQ.ReceiveCompleted
Dim msgText As String
Dim eMsg As Message
' 이벤트 아규먼트(e)에서 메시지를 획득합니다.
eMsg = e.Message
' 미리 지정된 형식(String)으로 포맷터를 설정합니다.
eMsg.Formatter = _
New XmlMessageFormatter(New System.Type() {GetType(String)})
' 메시지 본문을 포착하여 String 형식으로 캐스팅합니다.
msgText = CStr(eMsg.Body)
Console.WriteLine(msgText)
' 비동기 수신의 루프에서 빠져나갈 수 있도록 플래그를 설정합니다.
msgReceived = True
End Sub
참고 BDAdotNetAsync1.vb 샘플 코드에서 Example 10을 참조합니다. (다운로드는 기사의 첫 부분에서)
결론
응용프로그램에서 비동기 처리를 구현하기 위한 우선적인 방법 중 한가지는 MSMQ의 메시지 대기열을 이용하는 것입니다. System.Messaging 클래스들은 .NET 혹은 COM 데이터 타입을 포함하는 대기열 메시지를 읽고 전송하는 기능을 제공합니다. 비동기 메시지 처리를 구현하는 다른 방법은 향후의 아키텍처 주제 기사에서 비교될 것입니다