티스토리 뷰

필자는 일전에 이와 관련되어 상당한 분량의 포스팅을 올린 적이 있다. 총 5회의 아티클 중 마지막 회를 모두 작성하지는 못했지만, 지금 이 내용이 그 마지막 회의 내용과 어느 정도의 내용과 유사하다고 보면 된다.


그 중, 4회 아티클 ‘[실전 ASP.NET Session [4] - 세션상태 마이그레이션]5’의 내용은 본 아티클의 내용에서 매우 중요한 기초 내용이 된다. 이 글을 읽고 있는 독자 중 잘 이해가 되지 않는다면 필자가 이전에 작성한 포스팅을 반드시 읽어보기를 바란다.

그리고 위의 필자가 작성한 링크의 아티클은 5년전에 작성된 글임을 인지해 주길 바란다. 그 때는 필자가 지금보다도 더 실력이 형편 없었을 뿐더러 현재 추구하는 웹 개발의 트랜드와 상이한 면이 있을 수도 있을 것이다. .NET Framework 버전과 개발툴의 버전도 지금 사용하는 버전과는 한참 예전 버전이었다. 하지만 5년이 지난 글임에도 기초적인 내용은 모두 탄탄하게 짚고 넘어가므로 한번씩 보는 것도 나쁘지 않다.

ASP.NET이 지원하는 분산 세션 상태(Session State)

일반적으로 1대 1의 물리적인 관계는 분명 분산환경이긴 하지만 분산 환경이라고 말하지 않는다. 어떤 물리적인 환경에 배치하든, 통계학의 분산에 대한 기대값이나 편차의 해는 변하지 않기 때문이다.

분산 환경에서 더 나은 성능을 내기 위한 알고리즘과 휘발성인 메모리 안에서 데이터가 유실되지 않도록 원자성(Atomicity)을 유지할 수 있는 방법을 제공해야 한다. 또 웹 서버의 세션이라는 특징은 매우 빈번하게 엑세스 되고, 업데이트와 삭제 작업도 매우 많이 발생한다.

하지만, 안타깝게도 memcached[1]는 ‘get’ 명령의 원자성(Atomicity)를 보장하지 않는다고 한다.

A series of commands is not atomic. If you issue a 'get' against an item, operate on the data, then wish to 'set' it back into memcached, you are not guaranteed to be the only process working on that value. In parallel, you could end up overwriting a value set by something else.


하지만 괜찮다. 웹 서버의 세션은 동시다발적인 병렬 작업으로 요청이 들어올 수 없는 구조이다. 웹 서버로 사용자의 요청이 들어올 때, 사용자의 웹 브라우저에 가진 쿠키 값이 신원보증을 하는 값, 또는 해시된 세션의 키 값이다. 그러므로 한 세션에 대해 동시에 여러 클라이언트(사용자 웹 브라우저)의 요청은 있을 수 없는 일이다.

하지만 불가능한 것도 아니다. 예전에는 자바스크립트가 허용되는 게시판이나 이와 유사한 환경에서 악성 스크립트를 심어 놓으면, 그 글을 보는 누군가는 브라우저의 쿠키 값을 취득하여 해커에게 보내고, 해커는 세션이 만료되는 20분(일반적인 세션 만료 시간) 전에 취득한 세션 값으로 재 요청을 하게 되면 다시 세션이 연장되고, 세션이 로그인된 상태인 경우 해커도 로그인된 상태로 입장하는 것이 가능하다

때문에 완전히 같은 해시된 세션의 키 값으로 여러 클라이언트(사용자의 웹 브라우저)의 요청이 온다는 것은 사용자의 세션 값이 털려서, 누군가 악용하고 있다고 보는 것이 맞다.

그러므로 아주 정상적이고 일반적인 환경에서 원자성을 제공하지 않는 ‘get’ 명령은 전혀 문제가 되지 않으며, 오히려 원자성을 보장할 수 없는 문제로 더 좋은 Read 성능을 낼 수 있기 때문에, memcached 가 세션 서버로 사용하기에 redis[2] 보다 더 좋을 수 있다.

ASP.NET이 제공하는 세션 관리 요약

아무런 설정이 없다면 In-Proc, 즉 로컬 머신의 메모리를 사용하게 된다. 만약 불행히도, Win32 DLL을 참조하는 웹 응용 프로그램이라면 웹 서버의 메모리의 크기 따윈 무시하고 최대 2GB 밖에 사용할 수 없게 된다.

이를 극복하는 방법으로 윈도우 서비스로 백그라운드로 실행되는 ASP.NET Session State Service 서비스를 사용하면 좋다. 여러 웹 응용 프로그램이 하나의 Session State Service에 연결되더라도 웹 응용 프로그램마다 고유의 Identity Key(Guid)가 할당되고, 이를 Primary Key로 구분하게 된다. 이 NT Services가 다운되거나 재시작하지 않는 이상 웹 서버의 세션은 항상 유지할 수 있다.

긴급한 패치로 웹 응용 프로그램을 업데이트 해야 하는 경우, IIS가 재시작 되는 경우가 발생할 수 있는데 이 때 Worker Process에 의해 구동되는 In-Proc 세션 정보는 모두 초기화된다. 웹사이트에서 뭔가를 구매하려는 사용자가 있었다면 큰 사고로 이어질 수 있지만, Session State Service에 세션 정보가 저장이 되므로 IIS 재시작 후에도 웹 응용 프로그램은 세션 정보를 모두 안전하게 유지할 수 있다.

보통 웹 서버와 Session State Service간에 통신을 하기 위해 방화벽의 특정 포트를 개방해야 하고, 내부적으로 서로 간에 소켓 통신에 사용되는 전용 프로토콜이 존재하므로 Session State Service를 확장하기란 쉽지 않을 것이다.

이런 경우, ASP.NET에서 MS SQL Server를 이용하여 세션 정보를 저장하는 방법을 제공해 준다. 세션 정보에 추가하고 싶은 정보나 DB상에 기록되어질 특정 필드를 추가하여 사용할 수 있고, Oracle 또는 MySQL을 사용하여 세션 상태를 저장할 수도 있다.

위의 링크 중 ‘[실전 ASP.NET Session [4] - 세션상태 마이그레이션]13’에 예제로 구현된 Oracle Session Store Provider 예제가 있으니 참고하길 바란다.

memcached를 이용하는 세션 저장소

memcached를 세션 저장소로 이용하는 것은 ASP.NET Session State Services의 역할과 구현만 다를 뿐이지 매우 유사하다. memcached도 메모리를 저장소로 이용하여 빠르게 캐시하고 요청에 빠르게 응답할 수 있는 간결한 구조로 구현된 오픈소스이다.

특히 memcached를 이용할 경우 ASP.NET Session State Services로 불가능한 클러스트링이 가능하기 때문에 수평적으로 세션 서버를 확장할 수 있다. 한 대의 세션 서버만 있는 경우보다 로드벨런싱의 자유도가 더 높고, 세션 서버의 장애에도 대처가 가능하다. 또 하나의 장점이라면 저가형 장비를 병렬로 구성이 가능하기 때문에 세션 서버를 구성하기 위한 금전적이거나 물리적인 많은 제약이 사라지게 된다.

memcached와 redis, 두 가지 오픈소스를 고려하고 있었는데, 세션 서버에 memcached가 더 적합하다고 생각하여 memcached만 다룬다. memcached의 소스 코드가 더 가볍고 취향이나 요구사항에 따라 코드를 변형하기에 더 적합하지 않을까 생각한다. 그리고 자칫 복잡해질 수 있는 구조적인 아키텍처를 좀 더 간결한 memcached로 표현하는 것이 글을 보는 입장이나 쓰는 입장에서 편하다고 믿는다.

memcached는 C언어로 구현이 되어있고, GNU C 표준 라이브러리를 사용하므로 여러 운영체제에서 사용이 가능하다. (ASP.NET Session State Services는 윈도우 환경에서만 사용이 가능하다) 물론 필자는 맥킨토시, 리눅스, 윈도우에서 memcached를 컴파일, 구동이 잘 됨을 확인하였다.

간단하게 사용자의 세션 정보를 저장할 클래스를 하나 간단하게 만들었다. 외부에서는 오직 참조만 하여 사용할 수 있도록 internal 생성 메서드를 하나 가지고 있다.


다음은 ASP.NET MVC 프로젝트로 만든 간단한 예제이다.

web.config
1
2
3
4
5
6
<sessionState mode="Custom" customProvider="MemcachedSessionStateStore">
    <providers>
        <add name="MemcachedSessionStateStore"
             type="Umc.Core.Web.SessionState.Memcached.MemcachedSessionStateStore, Web.SessionState.Memcached, Version=1.0.0.0, PublicKeyToken=eed8f2bc3bfc4c7a, Culture=neutral"/>
    </providers>
</sessionState>

  

HomeController.cs
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
public class HomeController : Controller   
{
   private static readonly string KEY_OF_USER_SESSION = "__USER_SESSION_KEY__";
   public UserIdentity SessionStore
    {
        get { return (Session[KEY_OF_USER_SESSION] as UserIdentity) ?? UserIdentity.Empty; }
        set { Session[KEY_OF_USER_SESSION] = value; }
    }
 
 
    public ActionResult Index()
    {
        Session[KEY_OF_USER_SESSION] = UserIdentity.New("POWERUMC"
                                                        , "Junil, Um"
                                                        , "powerumc at gmail.com");
 
 
         /*
          // 맴버쉽 사용자 인증 설정 생략...
        var ticket = new FormsAuthenticationTicket(...);
        var user = new GenericPrincipal(new FormsIdentity(ticket), ... )
         * */
 
 
        return View();
    }
}

 

위와 같이 클라이언트의 세션 키를 이용하여 memcached의 서버에 세션 키를 이용하여 세션 값을 저장한다.

telnet 'get' 명령 결과
MPOWERUMC:~ powerumc$ telnet 192.168.0.23 11211
Trying 192.168.0.23...
Connected to 192.168.0.23.
Escape character is '^]'.
get laaymm13uyu03bofhdydi4iq
VALUE laaymm13uyu03bofhdydi4iq 0 477
AAEAAAD/////AQAAAAAAAAAMAgAAAF1XZWIuU2Vzc2lvblN0YXRlLk1lbWNhY2hlZCwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWVlZDhmMmJjM2JmYzRjN2EFAQAAAEpVbWMuQ29yZS5XZWIuU2Vzc2lvblN0YXRlLk1lbWNhY2hlZC5NZW1jYWNoZWRTZXNzaW9uU3RhdGVTdG9yZStTZXNzaW9uRGF0YQQAAAATPElkPmtfX0JhY2tpbmdGaWVsZBg8TG9ja0FnZT5rX19CYWNraW5nRmllbGQYPEV4ZmlyZXM+a19fQmFja2luZ0ZpZWxkFzxMb2NrSWQ+a19fQmFja2luZ0ZpZWxkAQAAAgwNAgAAAAYDAAAAGGxhYXltbTEzdXl1MDNib2ZoZHlkaTRpcQC8oGUBAAAAAPAWAzAi0IgJAwAAAAs=
END

 

 

ASP.NET의 세션이 실제로 저장이 되었는지 확인해보자. telnet을 통해 memcached 포로토콜의 명령을 입력하여 확인하면 된다.

현재 브라우저에서 연 세션 값은 쿠기에 저장이 되어 있다. 쿠키에 저장된 세션의 키 값은 ‘laaymm13uyu03bofhdydi4iq’ 이다.


telnet에 접속하여 memcached에 저장된 세션 키의 값을 조회된다.

memcached 세션 저장소로써 활용

memcached 오픈 소스 솔루션을 이용하여 메모리 캐시를 이용하여 ASP.NET 세션 상태를 저장할 수 있도록 구성해 보았다. 이제 얼마나 좋은 성능을 낼 수 있는지 측정이 필요한데 본 아티클에서는 memcached를 세션 서버로 활용할 때에 대한 성능의 문제는 다음에 기회가 되면 더 심도있게 다루어 보도록 하겠다.

그리고 ASP.NET 뿐만 아니라, JSP 또는 Servlet, 그 외에 여러 웹 개발 프레임워크에서 쉽게 사용할 수 있다. memcached는 C#, Java, Python 등 여러가지 언어로 memcached서버에 연결할 수 있는 클라이언트 라이브러리를 어렵지 않게 찾을 수 있다.

이번 아티클에서는 MemcachedSessionStoreProvider 소스 코드를 제공하지 않았다. 물론, 필자의 위 코드를 실행하기 위해 MemcachedSessionStoreProvider 소스 코드가 있어야 한다. 이 코드는 다음 아티클에서 조금씩 작성해 나갈 예정이다.

memcached 에 대한 자세한 내용은 아래 링크의 구글 코드에 들어가면 컴파일 및 실행 방법과 유지관리 방법에 대한 위키가 있다.

memcached wiki, https://code.google.com/p/memcached/wiki/NewStart



[1]: C언어로 구현된 고성능 분산 메모리 객체를 캐시하는 서버. Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.

[2]: 키/값을 저장할 수 있는 분산 서버로 데이터의 구조체 뿐만 아니라 문자열, lists, 빠른 검색을 위한 hashes 등을 지원. Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain stringshasheslistssets and sorted sets.

댓글