본문 바로가기
개발/Spring

스프링 properties 변경 감지하여 동적으로 로딩하기!

by Kingbbode 2016. 9. 29.

 스프링에서는 변경될 여지가 있는, 민감하고 다소 정적인 설정 값들을 외부 설정 파일로 관리하는 경우가 많습니다. 외부 파일로 설정 값을 관리하는 경우 어플리케이션의 소스수정, 리페키징없이 비교적 간단하게 설정 값을 바꿀 수 있습니다.


 그러나! 아무런 설정없이 properties를 사용한다면 WAS의 재기동은 불가피합니다. WAS의 재기동없이 properties를 동적으로 로딩하는 2가지 방법을 소개하려고 합니다.

 


 첫번째는 Spring 내장 컴포넌트인 ResourceBundleMessageSource이며

 두번째는 Apache Commons 프로젝트 컴포넌트인 PropertiesConfiguration입니다!



첫번째. Spring ResourceBundleMessageSource


 Spring에서 공식지원하는 MessageSource 중 ReloadableResourceBundleMessageSource를 사용합니다. 

 

1
2
3
4
5
6
7
8
9
10
11
private static final String SERVER_FILE = "file:/data/dynamic";
private static final String CLASSPATH_FILE = "classpath:dynamic";
 
@Bean
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource(){
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasenames(CLASSPATH_FILE, SERVER_FILE);
    messageSource.setCacheMillis(5000);
 
    return messageSource;
}
cs


파일 등록

 setBasenames를 통해 여러 개의 파일을 등록할 수 있습니다. 내부에서 XML, properties Suffix를 검사하는 로직이 따로 있으므로 파일이름은 확장자를 제외하여 적어주도록 합니다.


ReloadableResourceBundleMessageSource 사용시 몇가지 주의할 점

  • ReloadableResourceBundleMessageSource는 클래스패스 내에 존재하는 message resource에 대해서는 Reload 작업을 수행하지 않습니다. 따라서, ReloadableResourceBundleMessageSource이 관리하는 message resource는 클래스패스가 아닌 별도의 위치에 정의하여 사용하시되 해당 resource는 file:C:/temp/test와 같은 포맷으로 정의하시면 됩니다. 

  • 클래스패스 내에 존재하는 message resource 정의시에는 classpath: 프로토콜을 붙여 주는 것을 권장합니다. classpath: 프로토콜을 붙이지 않으면, 웹어플리케이션에서 해당 message resource를 읽어들이고자 할 경우, 일반 테스트케이스를 통해 해당 message resource를 읽어들일 때와는 다르게 ServletContextResource를 사용하면서 해당 message resource를 못찾는 현상이 발생합니다.

(출처:ANYFRAME(오픈소스커뮤니티))


CacheMillis는 캐시주기가 아닌 마지막 검사로부터 최소 유지시간

 CacheMillis는 마지막 검사로부터 파일검사를 수행하지 않고 유지하는 시간이라고 생각하면 됩니다. ChacheMillis가 스케줄링되어 배치로 돌아가는 주기처럼 보여질 수 있으나, 실제로는 getMessage 호출마다 현재시간이 마지막 검사시간 + CacheMillis보다 큰지 검사하고 있습니다. 작거나 같다면 아무것도 수행하지 않으며, 클 경우 마지막 검사시간을 갱신하고 파일이 수정되었는지를 검사를 하고 해당 파일이 수정이 되었다면 reload하게 됩니다. CacheMillis를 설정하지 않을 시 디폴트로 -1 값을 갖게되며, reload 기능이 작동하지 않습니다.


Properties로써의 기능은 부실

 Spring 자체적으로 제공되는 컴포넌트로 별도의 디펜던시 추가가 없으며, 동적으로 파일을 가져오는 기능이 간단하게 설정이 가능합니다. 그러나 properties를 key,String value 메시지로만 보기때문에 properties로써의 기능적인 문제가 단점입니다. properties에 리스트 형태로 입력해둔 데이터도 직접 파싱을 해서 사용해야합니다.

 

1
2
3
model.addAttribute("SpringString", messageSource.getMessage("dynamic.string"null , Locale.US));
model.addAttribute("SpringInt", messageSource.getMessage("dynamic.int"null , Locale.US));
model.addAttribute("SpringList", messageSource.getMessage("dynamic.list"null , Locale.US).split(","));
cs




두번째. Apache Commons Configuration


 아파치 Commons 프로젝트의 컴포넌트인 PropertiesConfiguration을 사용합니다. Commons Configuration은 일반화된 설정파일 인터페이스를 갖추고, properties로써 갖추어야할 기능을 기존 properties보다 더 넓게 확장하여 제공하고 있습니다. 


 제가 소개할 버전은 commons-configuration 1.10입니다. 이미 오래전 2.x 버전이 출시되었지만, reload 기능에 있어서는 기존 1.10 버전이 훨씬 간단하고 쉽게 구현될 수 있기때문에 1.10 버전을 소개합니다. 


dependency

  • commons-configuration

  • commons-loggin

  • commons-collections


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static final String SERVER_FILE = "file:/data/dynamic.properties";
private static final String CLASSPATH_FILE = "classpath:dynamic.properties";
 
@Bean
PropertiesConfiguration propertiesConfiguration() throws Exception {
    PropertiesConfiguration configuration;
    try {
        configuration = new PropertiesConfiguration(SERVER_FILE + SUFFIX);
    } catch (ConfigurationException e) {
        configuration = new PropertiesConfiguration(CLASSPATH_FILE + SUFFIX);
    }
    configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
 
    return configuration;
}
cs


Configuration 1 : 1 File

 하나의 Configuration에 여러 개의 파일이 등록될 수 없는 구조이기때문에 여러 파일을 동적 로딩으로 사용하신다면 별도의 Factory를 구성해야 합니다. 이러한 구조때문에 로드순서로 중요도 높은 설정으로 덮어씌우는 것이 불가능합니다. 그래서 실제 서버에서는 외부 설정 파일을 사용하며, 개발시에는 classpath의 설정파일을 사용하기 위하여 try-catch로 외부 설정 파일을 먼저 로드하였고, 파일이 존재하지 않을 시 classpath의 설정파일로 configuration을 구성하도록 했습니다. 


RefreshDelay는 refresh주기가 아닌 마지막 검사로부터 최소 유지시간

 위에 설명한 ResourceBunddleMessageSource와  마찬가지로 호출마다 현재시간이 마지막 검사시간 + reFreshDelay보다 큰지를 검사하고 있습니다. 작거나 같다면 아무것도 수행하지 않으며, 클 경우 마지막 검사시간을 갱신하고 파일이 수정되었는지를 검사를 하고 해당 파일이 수정이 되었다면 reload하게 됩니다. RefreshDelay는 5초입니다.


Properties로써의 기능 충실

 save, update, refresh, getString, getInt, getList 등 properties 설정을 보다 쉽게 사용 가능한 다양한 인터페이스를 제공해줍니다.


1
2
3
model.addAttribute("ApacheString", dynamicProperties.getString("dynamic.string"));
model.addAttribute("ApacheInt", dynamicProperties.getInt("dynamic.int"));
model.addAttribute("ApacheList", dynamicProperties.getList("dynamic.list"));
cs




Demo(github)



 같은 동적 프로퍼티 로딩 기능이지만, 각 특성에 따라 골라서 사용하시면 될 것 같습니다. 더불어 이 기능은 사용자에게 오픈되는 서비스에서 거의 실시간으로 프로퍼티를 로딩하고 싶다면 부적합하다는 의견을 많이 보았습니다. 갱신 검사 주기 시간을 조절하여 꽤 긴 시간 단위마다 한번만 갱신되게 하는 것이 바람직할 것 입니다. 



GitHub : https://github.com/kingbbode/spring-dynamic-properties

댓글