메일 클라이언트의 멀티스레드 지원 및 연결 풀

다음과 같은 메일 클라이언트 ImapClient, Pop3Client, 그리고 SmtpClient 멀티스레드 환경에서 사용할 수 있습니다. 클라이언트는 서버와 하나 이상의 연결을 유지할 수 있습니다. 클라이언트 내부의 연결 집합을 관리하려면 연결 풀이 사용됩니다. 동시에 생성·사용 가능한 연결 수는 CredentialsByHostClient.MaxConnectionsPerServer 속성. 이 속성은 1 또는 그 이상의 값으로 설정할 수 있습니다. 기본값은 10입니다.

각 연결마다 멀티스레드 작업을 지원하기 위해 명령 대기열이 구현됩니다. 명령은 프로토콜에 정의된 가장 기본적인 작업을 구현합니다, 예를 들어 Noop, Authenticate, 등등. 사용자는 사용 가능한 연결보다 더 많은 명령을 시작할 수 있지만, 해당 작업에 대한 연결을 생성할 수 있을 때만 실행됩니다.

멀티스레드 환경에서 메일 클라이언트 동작 방식

이메일 클라이언트는 다음과 같은 동작을 합니다:

  1. 다음과 같은 경우 MaxConnectionsPerServer = 1, 클라이언트는 하나의 연결을 만들고 인증 및 권한 부여를 수행합니다. 이 연결은 클라이언트가 해제될 때까지 작업 상태를 유지합니다. 다른 스레드의 모든 작업은 주 연결에 배치된 하나의 명령 대기열로 향합니다.

  2. 다음과 같은 경우 MaxConnectionsPerServer > 1, 클라이언트는 필요한 수의 연결을 생성하고 각 연결에 대해 인증 및 권한 부여를 수행합니다. 하나의 연결은 주 연결로 예약됩니다. 이 연결은 클라이언트가 해제될 때까지 작업 상태를 유지합니다. 다른 모든 연결은 필요에 따라 생성·해제됩니다. 이러한 연결의 최대 수는 MaxConnectionsPerServer 속성. 예를 들어 MaxConnectionsPerServer = 2, 그러면 하나의 연결이 주 연결로 예약되고, 두 번째 연결은 다른 스레드에서 실행되는 작업을 위한 추가 연결로 사용됩니다. 따라서 MaxConnectionsPerServer = 3, 그러면 첫 번째 연결이 주 연결로 예약되고, 다른 두 연결은 다른 스레드에서 실행되는 작업을 위한 추가 연결로 사용됩니다. 새 스레드에서 연결 요청이 들어오고 모든 연결이 이미 사용 중이면, 클라이언트는 사용 중인 연결 수가 감소할 때까지 대기합니다. 이는 연결을 올바르게 해제하는 것이 왜 중요한지를 명확히 보여주는 매우 중요한 순간입니다.

멀티스레드 환경에서 메일 클라이언트 사용 예시

사용자는 여러 방법으로 다른 스레드에서 작업을 실행할 수 있습니다. 두 가지 유형으로 나눌 수 있습니다.

비동기(Begin/End) 메서드 사용

사용자는 클라이언트에 정의된 비동기(Begin/End) 메서드를 사용합니다. 이 경우 메일 클라이언트는 필요에 따라 새 스레드를 시작합니다. 클라이언트에는 작업 대기열이 구현되어 있으며(연결의 명령 대기열과 혼동하지 마세요), 연결이 사용 가능하면 작업이 실행됩니다. 사용된 연결 수가 제한값보다 적어지면 클라이언트는 새 연결을 만들고, 현재 작업을 위한 스레드를 생성하여 작업을 실행합니다. 비동기 작업 사용 예시:

// Create an imapclient with host, user and password
ImapClient client = new ImapClient();
client.Host = "domain.com";
client.Username = "username";
client.Password = "password";
client.SelectFolder("InBox");

ImapMessageInfoCollection messages = client.ListMessages();
IAsyncResult res1 = client.BeginFetchMessage(messages[0].UniqueId);
IAsyncResult res2 = client.BeginFetchMessage(messages[1].UniqueId);
MailMessage msg1 = client.EndFetchMessage(res1);
MailMessage msg2 = client.EndFetchMessage(res2);

사용자 생성 스레드 사용

사용자는 다음과 같은 객체를 사용해 스레드를 만들 수 있습니다 Thread, ThreadPool, Task, 또는 이 목적을 위한 기타 객체. 사용자는 타사 코드에서 만든 스레드도 사용할 수 있습니다. 이 경우 클라이언트는 두 가지 동작 모델을 가집니다.

a. 사용자가 해당 스레드에서 추가 연결을 만들지 않으면, 모든 작업이 주 연결의 명령 대기열로 전송됩니다. 다음은 새 연결을 만들지 않은 추가 스레드에서의 작업 예시이며, 모든 트랜잭션이 주 연결을 통해 이루어집니다:

List<MailMessage> List = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
    client.SelectFolder("folderName");
    ImapMessageInfoCollection messageInfoCol = client.ListMessages();
    foreach (ImapMessageInfo messageInfo in messageInfoCol)
    {
        List.Add(client.FetchMessage(messageInfo.UniqueId));
    }
});

b. 사용자가 추가 스레드용 새 연결을 만들기 위한 메서드를 실행하면, 새 연결 할당량이 변경될 때까지 해당 스레드는 차단됩니다. 그런 다음 새 연결이 생성되고, 이 연결은 해당 스레드의 모든 작업에 대한 기본 연결이 됩니다. 스레드의 모든 작업이 완료되면 연결을 해제해야 합니다. 새 연결을 만들려면 CredentialsByHostClient.CreateConnection 메서드를 사용합니다. 이 메서드는 IDisposable 인터페이스. 연결을 해제하려면 Dispose 메서드를 호출해야 합니다. 연결 생성 및 해제는 메일 작업이 실행되는 스레드 내에서 이루어져야 합니다. 메일 클라이언트를 만든 스레드에서 새 연결을 만들려고 하면 오류가 발생합니다. 해당 스레드는 현재 새 연결을 만들 수 없기 때문입니다. 또한 MaxConnectionsPerServer = 1. 추가 스레드에서 새 연결을 만드는 코드 예시:

List<MailMessage> List1 = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
    using (IDisposable connection = client.CreateConnection())
    {
        client.SelectFolder("FolderName");
        ImapMessageInfoCollection messageInfoCol = client.ListMessages();
        foreach (ImapMessageInfo messageInfo in messageInfoCol)
            List1.Add(client.FetchMessage(messageInfo.UniqueId));
    }
});

연결 풀

Aspose.Email 19.3부터 연결 풀이 재구성되었습니다. EmailClient 클래스가 도입되었으며, 이는 결국 CredentialsByHostClient 클래스. EmailClient 클래스는 ConnectionAsgmtMode 멀티스레드 환경에서 연결 할당 모드를 정의하는 속성. EmailClient.ConnectionAsgmtMode 다음으로 설정됩니다: ConnectionAsgmtType 열거형.

연결 유형

연결 유형은 세 가지가 있습니다:

  • 주 연결. 메일 클라이언트와 함께 생성 및 해제되는 연결이며, 수동으로 생성하거나 해제할 수 없습니다.
  • 기본 연결. 사용자는 CreateConnection 메서드로 수동으로 생성할 수 있습니다. 기본 연결이 존재하면, 해당 스레드에서 실행되는 이메일 클라이언트의 모든 메서드는 암시적으로 이 연결을 사용합니다. 스레드당 하나의 기본 연결만 존재할 수 있습니다. 이는 EmailClient.ConnectionAsgmtMode 속성. 이러한 연결은 EmailClient.CreateConnection(createAsDefaultConnection = true) 메서드로 수동 생성할 수 있습니다. 기본 연결을 사용하지 않을 경우(연결 할당 모드에 따라), 주 연결이 대신 암시적으로 사용됩니다.
  • 독립 연결. 스레드에 연결되지 않은 연결이며, 수동으로 생성하고 메서드 매개변수로 명시적으로 사용해야 합니다. 이러한 연결은 EmailClient.CreateConnection() 메서드 또는 EmailClient.CreateConnection(createAsDefaultConnection = false) 메서드.

연결 할당 유형

설정하려면 EmailClient.ConnectionAsgmtMode 속성, ConnectionAsgmtType 열거형이 사용됩니다. 제공되는 할당 유형은 아래에 나열됩니다.

  • ConnectionAsgmtType.UseMainOrDefault 이 모드는 이메일 클라이언트의 기본 설정입니다. 기본 연결이 생성되지 않았거나 메서드 매개변수로 명시적으로 전달된 연결이 없을 경우, 이메일 클라이언트는 여러 스레드의 모든 작업에 주 연결을 사용합니다. 주 연결은 이메일 클라이언트와 동시에 생성됩니다. 사용자는 CreateConnection 메서드를 사용할 경우, 스레드에 대한 기본 연결이 생성되면 해당 스레드에서 호출된 이메일 클라이언트 모든 메서드에 암시적으로 사용됩니다. 스레드에 대한 기본 연결이 생성되지 않은 경우, 주 연결이 해당 스레드에서 호출된 모든 메서드에 사용됩니다. 사용자는 또한 스레드와 연결되지 않은 연결(기본 연결이 아닌)을 CreateConnection 메서드. 다른 연결(주 연결도 기본 연결도 아님)을 사용하려면 사용자가 연결을 메서드 매개변수로 명시적으로 전달해야 합니다. 사용자는 추가로 원하는 만큼의 연결을 만들 수 있습니다. 스레드당 하나의 기본 연결만 존재할 수 있습니다. 기본 연결은 사용자가 Thread 멀티태스킹 프로그래밍용 객체입니다. 사용자가 연결 풀을 사용하거나 Task 멀티태스킹용 객체인 경우 이 모드에서는 올바르지 않은 동작이 발생할 수 있습니다. 이 문제를 방지하려면 사용자는(사용 중인 경우) 기본 연결을 코드 실행이 끝날 때 수동으로 해제해야 합니다.

  • ConnectionAsgmtType.UseMain 이메일 클라이언트는 여러 스레드의 모든 작업에 주 연결을 사용합니다. 주 연결은 이메일 클라이언트와 동시에 생성됩니다. 사용자는 기본 연결을 만들 수 없지만, CreateConnection 메서드로 스레드에 대한 기본 연결을 만들 수 없습니다. 다른 연결을 사용하려면 사용자가 이를 메서드 매개변수로 명시적으로 전달해야 합니다.

  • ConnectionAsgmtType.UseDefault 이메일 클라이언트는 여러 스레드의 모든 작업에 기본 연결만을 암시적으로 사용합니다. 이 모드에서는 주 연결이 사용되지 않습니다. 스레드에 대한 기본 연결이 아직 생성되지 않은 경우(이메일 클라이언트 메서드가 처음 호출될 때), 이메일 클라이언트는 첫 작업이 실행되기 전에 해당 스레드에 대한 기본 연결을 암시적으로 생성합니다. 사용자는 CreateConnection 메서드 때문에 자동으로 생성됩니다. 사용자는 또한 스레드와 연결되지 않은 연결을 CreateConnection 메서드. 다른 연결을 사용하려면 사용자가 이를 메서드 매개변수로 명시적으로 전달해야 합니다. 사용자는 추가로 원하는 만큼의 연결을 만들 수 있습니다. 스레드당 하나의 기본 연결만 사용할 수 있습니다. 기본 연결은 사용자가 Thread 멀티태스킹 프로그래밍용 객체입니다. 사용자가 연결 풀을 사용하거나 Task 멀티태스킹용 객체인 경우, 이 모드에서는 올바르지 않은 동작이 발생할 수 있습니다. 이 문제를 피하려면 사용자는 코드 실행이 끝날 때 기본 연결을 수동으로 해제해야 합니다.

권장 사항

사용자가 모든 명령을 주 연결에 보낼 경우, 서로 다른 스레드에서 온 명령이 혼합되는 상황이 발생할 수 있습니다. 사용자는 명령의 순서 의존성을 이해하고 이러한 명령을 동기화하기 위한 조치를 취해야 합니다. 또한 명령을 서로 다른 세션(IMAP/POP3)에서 실행할 가능성도 고려해야 합니다. 가장 시간이 많이 소요되는 작업은 FetchMessage, AppendMessage, 그리고 Send. 이러한 작업은 새 스레드와 새 연결로 수행하는 것이 합리적일 것입니다. 빠른 작업은 Delete 주 연결로 수행하는 것이 합리적일 수 있습니다. 새 연결 초기화는 상당히 시간이 많이 소요되는 작업임을 유의하십시오.