Thinking Different








Pooling Pattern - 풀링 패턴


  • 자주 사용되는 리소스에 대해 Provider에서 사용할때마다 생성, 반환시 성능상의 문제가 있다.

  • 최초 정해진 갯수의 리소스를 초기화하여 Pool에 미리 만들어놓고 필요할때마다 사용하여 성능을 높일 수 있다.

  • 캐시와의 차이점은 같은 종류의 리소스를 사용하므로 Identified가 필요하지 않다.






문제 예시) Web-based e-commerece Solution 


클라이언트 유저들이 하나의 전자상거래 웹 서비스 카탈로그를 요청하게 되면, 서버에서는 서블릿(servlet)이 Database와 커넥션(할당 - 해제)을 맺어 데이터를 유저에게 다시 Response 해주는 부분을 보여주고 있다.

하지만 다수의 클라이언트가 동시 접속하여 서비스를 요청하게 된다면 서버와 데이터베이스의 커넥션은 수많은 할당, 해제가 일어나야 될 것이다. 여기에서 성능상의 문제와 함께 서비스가 제대로 이루어지기 힘들어질 것이다. 이를 해결하기 위해서 Pooling Pattern을 사용하게 된다.



풀링 패턴 (Pooling Pattern) 설계 시 필요 사항


  • 리소스 재사용이 가능해야한다
  • 성능을 보장해야 한다
  • 리소스의 빠른 재사용이 가능해야한다
  • 구조가 복잡하지 않아야 한다.
  • 메모리 할당 해제 반복시 단편화(flagment)가 발생하므로 최소화 필요


Pooling Pattern 사용 범위

데이터베이스 or 네트워크 커넥션, 오브젝트 인스턴스, 스레드, 메모리 및 기타 등등







Resource Pool 시나리오)

  1. Resource Pool에 미리 Resource들을 생성 및 관리해 놓는다.
  2. 유저의 요청으로 리소스를 요청(acquire)를 하게 되면 이미 만들어진 Resource Pool에서 Resource를 얻어와서 사용을 하고 사용이 끝나면 release를 통해 Resource Pool 로 반환한다.
  3. 만약 Resource Pool에 더이상 Resource가 없을 경우 Resource Environment를 통해서 새로운 Resource를 요청 생성하고, leasing 기법을 통해서 해제 한다. (Leasing Pattern)



<Connection Pool Design 예시>








예제 코드 다이어그램)




예제 코드)


Pool.h

1
2
3
4
5
6
7
8
9
10
#pragma once
 
class CResource;
 
class CPool
{
public:
    virtual CResource* aquire() = 0;
    virtual void release(CResource* res) = 0;
};


Resource.h

1
2
3
4
5
6
7
#pragma once
 
class CResource
{
public:
    virtual void* getConnection() = 0;
};


ConnectionPool.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once
#include <list>
 
class CConnectionPool : public CPool
{
public:
    CConnectionPool(unsigned int poolSize);
    virtual ~CConnectionPool();
 
public:
    CResource* aquire();
    void release(CResource* res);
 
protected:
    CResource* findNewConnection();
 
private:
    list<CResource*> m_pool;
};


ConnectionPool.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include "stdafx.h"
#include "ConnectionPool.h"
 
 
// 생성자
CConnectionPool::CConnectionPool(unsigned int poolSize)
{
    // 풀 사이즈만큼 커넥션 생성
    for (int i = 0; i<poolSize; i++)
    {
        CResource* resource = new CConnection;
        m_pool.push_back(resource);
    }
}
 
// 소멸자
CConnectionPool::~CConnectionPool()
{
    // 모든 커넥션 메모리 해제
    for each (CResource* entry in m_pool)
        delete entry;
 
    m_pool.clear();
}
 
// 커넥션 얻기, 없으면 생성 반환
CResource* CConnectionPool::aquire()
{
    CResource * resource = NULL;
    
    if (NULL == (resource = findNewConnection())) 
    {
        resource = new CConnection;
        m_pool.push_back(resource);
    }
 
    return resource;
}
 
// 커넥션 반환
void CConnectionPool::release(CResource* res)
{
    for each (CResource* entry in m_pool)
    {
        if (entry == res)
        {
            static_cast<CConnection*>(entry)->setInUse(true);
            break;
        }
    }
}
 
// 사용 가능 커넥션 찾기
CResource* CConnectionPool::findNewConnection()
{
    for each (CResource* entry in m_pool)
    {
        if (!static_cast<CConnection*>(entry)->getInUse())
        {
            static_cast<CConnection*>(entry)->setInUse(true);
            return entry;
        }
    }
 
    return NULL;
}


Connection.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#pragma once
 
#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" rename("EOF", "EndOfFile") no_namespace
 
typedef struct 
{
    long ticks;
    int hour;
    int minutes;
    int seconds;
    int month;
    int day;
    int year;
} DATETIME;
 
class CConnection : public CResource
{
public:
    CConnection();
    virtual ~CConnection();
 
public:
    void* getConnection();
    void setInUse(bool value);
    bool getInUse();
 
private:
    bool m_currentlyUsed;
    DATETIME mlastUsage;
    _ConnectionPtr m_connection;
};


Connection.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "stdafx.h"
#include <time.h>
 
 
CConnection::CConnection() : m_currentlyUsed(false)
{
    // 연결
    m_connection.CreateInstance(__uuidof(Connection));
    m_connection->CursorLocation = adUseServer;
    m_connection->Open(L"연결 프로바이더 설정", L"", L"", adConnectUnspecified);
}
 
CConnection::~CConnection()
{
    if (m_connection) 
    {
        m_connection->Close();
        m_connection = NULL;
    }
}
 
void* CConnection::getConnection()
{
    return m_connection;
}
 
void CConnection::setInUse(bool value)
{
    // 사용 날짜 시간설정(Leaseing Pattern 이용 목적)
    if (value)
    {
        time_t seconds = time(NULL);
        struct tm* timeinfo = localtime(&seconds);
 
        mlastUsage.ticks = seconds;
        mlastUsage.hour = timeinfo->tm_hour;
        mlastUsage.minutes = timeinfo->tm_min;
        mlastUsage.seconds = timeinfo->tm_sec;
        mlastUsage.month = timeinfo->tm_mon;
        mlastUsage.year = timeinfo->tm_year;
        mlastUsage.day = timeinfo->tm_mday;
    }
 
    m_currentlyUsed = value;
}
 
bool CConnection::getInUse()
{
    return m_currentlyUsed;
}


Main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
 
    // 풀 갯수 지정 풀생성
    CPool* mPool = new CConnectionPool(5);
 
    // 리소스 요청
    CResource* pResource = mPool->aquire();
 
    // 리소스 사용....  
 
    // 리소스 반환
    mPool->release(pResource);
 
    delete mPool;
 
    CoUninitialize();
    return 0;
}