세션 클러스터링(Session Clustering)과 JVM Memory

개요

얼마전 유지보수 하는 곳의 권한 체크하는 부분의 소스를 보다가 신기한(?) 코드를 발견했다.

1
2
3
4
5
String author = (String)session.getAttribute("author");
if(author == "admin") {
//관리자
...
}

오잉? 세션으로 권한 체크를 하는데 String 비교에 indentity(==) 연산을 사용하고 있다.

내가 이상하게 생각한 이유는 해당 시스템의 환경때문이다.
java servlet engine이 분산 클러스터로 구성되어서 각각의 container는 독립된 JVM을 가지고 있었기 때문이다.
그런데 또 기능은 잘 동작된다. ㅎㅎ
만약 두개의 웹엔진 A,B가 있다고 했을 때 처음 요청은 A로 다음 요청은 B로 보낸다면 session.getAttribute(“author”)로 session을 가져올 때마다 각각의 다른 JVM 메모리에서 가져올텐데 어떻게 독립된 JVM에서 같은 레퍼런스를 참조할 수 있는걸까??

question뭐지..?

의문점

  1. 2개의 웹 엔진 A, B가 있을 때 독립된 세션 메모리를 어떻게 같이 공유할 수 있는가?
  2. 같은 세션 메모리를 참조한다고 했을 때 String indentity 연산은 왜 잘 작동하는가?

먼저 의문점 1번을 해결하기 위해서 해당 서버인 JEUS의 session tracking 방식에 대해 알아보았다.

세션 트래킹(Session Tracking) 동작방식

세션 트래킹(Session Tracking) 동작방식에는 세션라우팅, 세션서버, 혼합방식이 있다.

  1. 세션 라우팅(Session Routing)
    세션라우팅은 해당 세션에 자신이 생성 또는 저장된 엔진의 ID를 부여한다.
    다음 요청이 올 때 쿠키에 해당 엔진의 아이디가 포함되어 있어서 항상 같은 엔진으로 요청을 보내게 된다.

세션라우팅첫번째 요청

세션라우팅두번째 요청

첫번째 요청을했을 때 세션오브젝트와 쿠키에 엔진 이름을 붙여서 넣는다.
두번째 요청을했을 때 세션쿠키의 이름에 따라 해당 엔진으로 요청을 보낸다.

  1. 세선 서버(Session Server)
    세션서버출처:https://technet.tmaxsoft.com

궁금증이 해결되었다. 세션라우팅방식으로 되어있어서 세션쿠키 아이디에 엔진 이름을 담아서 하나의 세션스코프 동안에는 항상 세션이 생성된 엔진으로만 요청이 가게된다.

Session Routing Test

정말 그런지 테스트를 해봤다. 먼저 세션을 확인 할수 있는 jsp페이지를 만든다

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
<%
HttpSession sess = request.getSession();
if(request.getParameter("key") != null) {
sess.setAttribute(request.getParameter("key"),request.getParameter("val"));
}
boolean isNew = session.isNew();
String sessionId = session.getId();
long creationTime = session.getCreationTime();
long lastAccessedTime = session.getLastAccessedTime();
int maxInactiveInterval = session.getMaxInactiveInterval();
%>
<table border=1>
<tr bgcolor="gray"><td colspan=2 align="center"><b>Session Info</b></td></tr>
<tr>
<td width="25%">Session ID</td><td width="75%"><%=sessionId%></td>
</tr>
<tr>
<td>isNew?</td><td><%=isNew%></td>
</tr>
<tr>
<td>Creation Time</td><td><%=new Date(creationTime)%></td>
</tr>
<tr>
<td>Last Accessed Time</td><td><%=new Date(lastAccessedTime)%></td>
</tr>
<tr>
<td>Max Inactive Interval</td><td><%=maxInactiveInterval%> sec</td>
</tr>
</table>

화면 결과는 다음과 같이 나왔다
세션캡쳐세션캡쳐

Session ID값 맨뒤를 보면 “.AP1_servlet_engine1” 처럼 뒤에 컨테이너 엔진이름이 붙어서 나온다
여러번 요청해보니 세션이 유지된 상태에서는 계속 같은 1번엔진으로 요청이 가는걸 확인할 수 있었다.

HttpSession과 JVM Constant Pool

의문점 2번.
같은 JVM 메모리를 참조하는건 알았다. 그런데 author == “admin” 연산은 어떻게 일관성있는 결과가 나오는걸까?

session.getAttribute(“author”) 메소드를 호출하면 HttpSession은 내부적으로 가지고 있는 ConcurrentHashMap.get(“author”)을 호출한다.
결과적으로 ConcurrentHashMap이 가지고있는 value값이 항상 같은 참조를 리턴해야 일관성있는 비교가 가능하다는 결론이 나온다.
서버에 세션을 저장하기 위해서 session.setAttribute(“author”, “amdin”) 하면 내부적으로 다음 코드가 실행될 것이다.

test.java

1
2
ConcurrentHashMap<String, String> sessionMap = new ConcurrentHashMap<>();
sessionMap.put("auth", "admin")

컴파일 후에 javap를 이용하여 바이트코드를 확인해보자

1
javap -v test.class

그럼 다음과 같은 결과가 나온다.
javapcontant pool 확인

#21을 보면 contant pool에 “admin”이 저장되는걸 알 수 있다.

JVM은 String 같은 상수를 담아놓는 contant pool이란 공간이 따로있다.
여기서는 contant pool에 대해서 따로 다루지 않겠다. 검색해보면 많이 나온다 ^^;

다른 세션객체에서 같은 contant pool의 참조를 가지고 있기 때문에 같은 결과가 나올 수 있었던 것이다.

jvm-memory