티스토리 뷰

반응형

서론

최근 하나의 회사가 제공하는 서비스의 규모가 커지고 복잡해지면서 이 서비스를 지탱하기 위해서 큰 서비스를 여러 개의 작은 서비스로 나누고 개발, 배포 하는 Micro Service Architectur(이하: MSA) 가 유행하고 있다. 이렇게 MSA에서는 각 서비스들은 독립적이며 자신을 Restful API로 자신을 노출 시킨다. 

MSA에서 각 서비스들은 서로 독립적 이기는 하나 그렇지 못한 부분들이 존재 할 수 있다. 예를 들어 사용자의 인증 및 권한 부여 같은 경우 서비스들의 독립성을 위해 각 서비스 마다 구현 또는 라이브러리를 넣고 사용 할 수 있지만 이렇게 되면 개발 언어마다 사용자 인증, 권한부여 로직을 구현을 해야 하며 관리 또한 복잡하게 된다. 

그래서 인증 및 권한 부여 서비스를 따로 만들고 Restful API명세를 통해 다른 서비스에서 사용자에 대한 권한을 체크 하는 방법을 사용 할 수 있다. 

이 방법을 사용하기 위해서 Spring에서는 RestTemplate을 사용할 수 있는데 이 RestTemplate을 사용시 주의해야 할 점 그리고 해결책에 대해서 설명한다. 


Connection Issue

RestTemplate은 Spring 으로 구현된  A서버에서 Spring으로 구현된 B서버로 요청을 보낼때 사용하는 객체로서 상당히 무거운 객체이다. 그만큼 내부적으로 하는일이 많다는 말인데 이 RestTemplate은 요청을 할 때마다 Connection을 만들게 된다. 

이렇게 만들어진 Connection들은 서버의 자원을 점차 차지하면서 서비스 배포 초기에는 멀쩡하던 서버가 아무 이유 없이 죽거나 속도가 급격히 느려 질 수 있거나 A서버가 B서버를 호출 하지 못하고 계속 대기하는 상태가 될 수 있다... 실제 서비스 되는 상태에서 이 상황이 발생하면... 생각만해도 끔찍하다!!!

그래서 RestTemplate을 사용하기 위해서는 Connection Pool을 직접 설정을 해주어야 한다. 여기서는 Connection Pool을 사용하기 위해서 Apache의 HttpClient를 사용할 것이다. 

public abstract class AbstractCommonInterface implements HandlerInterceptor {
	private final String authServerUrl;
	RestTemplate rt = null;

	public AbstractCommonInterface(String authServerUrl) {
		this.authServerUrl = authServerUrl;
		rt = new RestTemplate(getHttpRequestFactory());
	}

	
	private HttpComponentsClientHttpRequestFactory getHttpRequestFactory() {
		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(200);
		cm.setDefaultMaxPerRoute(20);
		HttpHost localhost = new HttpHost(authServerUrl);
		
		cm.setMaxPerRoute(new HttpRoute(localhost), 50);
		
		RequestConfig config = RequestConfig.custom()
				.setConnectTimeout(5000)
				.setConnectionRequestTimeout(5000)
				.setSocketTimeout(5000).build();
		
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(config).build();
		
		factory.setHttpClient(client);
		
		return factory;
	}
	
}


먼저 전역변수로 RestTemplate을 사용한 것을 볼 수 있는데 RestTemplate은 Thread Safe하기 때문에 Condition Race가 발생하지 않기 때문에 전역변수로 사용해도 무방하다. 외부 라이브러리의 클래스를 전역 변수로 사용하기 전에 꼭 공식 문서에서 Thread Safe한지 확인하는 습관을 가지도록 하자. 

약간 말이 중간으로 셋다. 꼭 저 부분을 나 스스로도 다시 한번 다짐하고 가고 싶어서 적어보았다. 여기서 중요한것은 getHttpRequestFactory()라는 메소드이다. 
PoolingHttpClientConnectionManager 객체를 생성해서 setMaxTotal 메소드에 총 conneciton수를 지정할 수 있는데 여기서는 200개를 할당한 모습을 볼 수 있다. 예시라서 200개라는 커넥션 수를 무작정 부여 하였지만 무작정 갯수를 정하는 것이 아니라 서버의 자원 상황, 요청 수를 계산하여 주기를 바란다. 

다음으로는 setDefaultMaxPerRoute라는 메소드를 볼 수 있는데 처음에는 이 메소드가 무엇을 의미하는지 몰라서 한참을 찾았다. 이 메소드는 어떤 경로든 최대 연결수를 나타낸다. 예를 들어 http://localhost:8080/testA, http://localhost:8080/testB 두개의 경로에 대해서 별도로 설정이 없다면 최대 20개의 연결이 생성된다는 뜻이다. 

마지막으로 setMaxPerRoute라는 메소드에 대해서 설명하면 전달되는 경로에 대해서 최대 연결 갯수를 지정할 수 있다. 여기서는 50개를 지정해주었는데 이렇게 되면 이전에 설정한 20개의 연결이 아니라 저 해당 경로만 50개의 연결을 가질 수 있게 된다. 

Tomcat Heap Size Issue

위와 같이 설정하고 서비스를 배포하고 테스트를 하는데 서버들간의 호출이 일어나면서 갑자기 heap 메모리 관련 오류가 발생하는 것을 확인 할 수 있다. 이것은 Connection이 생성되면서 허용 가능한 heap 메모리 크기를 넘어 섯기 때문이다. 

이렇게 되면 커넥션 수를 줄여주거나 Tomcat의 heap 메모리 크기를 늘려 주면 해결 할 수 있다.


결론

RestTemplate이 요청마다 Connection을 만들어 낸다는 것을 알고 바로 Connection Pool을 만들어야지 생각했고 쉽게 적용가능했다. 하지만 문제는 두번째 Tomcat Heap Size Issue였는데 내가 메모리 설정 관련해서 잘못 알고 있어서 그랬다. 

항상 이클립스 heap을 늘려주면 늘어나는 줄 알고 있었는데.... Tomcat설정을 따로 해줘야 했던것.... 참 부끄러웠던 순간.... 그래도 하나 깨달아서 행복!!! 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/12   »
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
글 보관함