반응형
반응형

WebApplicationInitializer에서 dispatcherServlet 설정에 InitParameter옵션을 아래와 같이 해주면된다.


dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");


위와 같이 설정해두면, 404 에러에 대해서 org.springframework.web.servlet.NoHandlerFoundException를 던져주게 된다.

반응형
,
반응형
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper);
converters.add(jackson2HttpMessageConverter);



서블릿 설정 중, MessageConverter를 추가해주는 부분에서 

위와 같이 세팅해주면, pretty하게 정렬이 된 JSON String을 볼 수 있게 된다.

반응형
,
반응형

비동기 처리 시 기본적으로 @Async를 사용한다.


JavaConfig기준 Spring 설정파일에서 @EnableAsync을 붙이기만 하면 바로 사용이 가능해진다.


하지만, 위 설정만으로 사용하게 되면, 기본적으로 단일 쓰레드로 동작을 하게 된다.

(ThreadPool설정을 보니 Default값이 1로 되어 있다.)


log를 @Async를 이용해서 보내게 되는 경우,

확실히 대규모 접속 서비스 같은 경우 단일쓰레드만으로는 사용이 어렵다.


로드러너를 이용한 성능 테스트에서도 목표 성능에 미치지 못했었던 적이 있다.


위 문제를 해결하기 위해, ThreadPooling을 해야 될 것 같아서,

스프링 사이트를 뒤져봤다.


참조 : http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html


AsyncConfigurer interface를 구현하겠다고 선언하면, 아래와 같이 두 가지 메소드에 대해서 구현을 하라고 나오는데,

getAsyncExecutor()가 내가 원하던 ThreadPooling역할을 해줄 것이다.

getAsyncUncaughtExceptionHandler()를 구현해서 이용할 수 있는 에러 핸들링은 필요에 따라 사용하는걸로...


@Override
public Executor getAsyncExecutor() {
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
     executor.setCorePoolSize(10);         // 최초에 생성할 Pool 개수
     executor.setMaxPoolSize(50);          // 몇개까지 Pool을 생성할 것인지
     executor.setQueueCapacity(100);      // Async 처리 시 Queue Size 
                                                      // (설정하지 않으면 Integer.MAX이기 때문에 성능에 문제가 발생함)
     executor.setThreadNamePrefix("LogAsync-");   // Thread 생성 시 가장 앞에 붙는 이름을 뭘로 할 것인지
     executor.initialize();
     return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
}



참고로 위 설정에서 사용하는, ThreadPoolTaskExecutor가 생각보다 목표 성능에 미치지 않고 불안정하다면, 

ConcurrentTaskExecutor를 이용해서 동시성에 대한 처리를 개선할 수 있다고 한다.

참조 : http://blog.outsider.ne.kr/1066 


전에 단일쓰레드 사용으로 문제가 있었으니, 

쓰레드풀링으로 다시 이용해보고 결과는 다시 포스팅해야 겠다.


[테스트 결과]

위 처럼 QueueCapacity를 100으로 잡고, 부하를 줘봤더니 일반적인 부하에서는 문제가 없었지만,

임계치까지 부하를 준 경우에 담아둘 수 있는 Queue Capacity가 작아서 Exception을 떨구며, 처리를 못하는 것들이 꽤 생겼다.


그래서, QueueCapacity를 CorePoolSize * MaxPoolSize * 2  = 1000 수치로 잡고 테스트를 해보았더니

임계치까지 부하를 줘도 모두 처리가 가능했고, CPU 사용률도 양호했다.


실무에서는, 로그를 DB에 insert하는 부분에서 사용하고 있다.

반응형
,
반응형

application.properties를 열어서 logging.path를 지정해주면 끝남.


아래 링크에 자세히 설명되어 있다.

http://www.slideshare.net/whiteship/ss-47273947

반응형
,
반응형

프로젝트 생성은 start.spring.io에 들어가서 만들던, sts에서 starter project로 만들던 똑같다.

freemarker랑 web만 체크를 하고 만들면됨.


내부적으로 뭔가만 해줄뿐, 다른 Spring Boot 프로젝트와 디렉토리 형식은 별반 다를게 없다.


내부적으로 해주는 뭔가에 대해 spring 공식 사이트에서 알아보니..


src/main/resources폴더에 있는 static 폴더를 internelResource로 알아서 세팅을 해주며,

templates 안에 있는 파일은 freemarkerViewResolver로 알아서 설정을 해준다고 한다.


그래서, 기존에 복잡하게 설정파일을 이리저리 고칠일이 없어졌다.


만약 ftl파일이 들어갈 경로를 templates 경로 말고 다른 경로를 사용하고 싶다면.. (웬만하면 그럴일은 없겠지만)

application.properties 파일을 열어서 필요한 옵션을 추가하여 override시켜주면 된다. (아래 링크 참고)

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-view-resolvers



sts가 이제 3.7버전이 되면서 application.properties파일에서도 code assistance를 해준다;;

spring boot도 그렇고 IDE도 그렇고, 점점 개발자가 뭔가를 설정할 일이 없어지는듯?


하나 주의할 점은, 추가로 WebMvc관련된 interface나 class(WebMvcConfigurerAdapter 등...)를 상속받아서

새롭게 설정파일을 만들 경우 spring boot가 자동으로 해줬던 것들이 제대로 동작을 하지 않는 것 같다.


Object를 Json으로 매핑하기 위해서 WebMvcConfigurer를 상속받아서 MappingJackSon2MessageConverter를 추가했더니

viewResolver건, static 폴더 접근이건 둘다 안됬었다.


하지만, 기본으로 MappingJackson2MessageConverter를 지원해주는 것 같아서, 설정파일에서 지워버리니 해결이 됨..


자동으로 해주는게 너무나 많아서, 뭔가를 추가할때 복잡하긴한데, 어느정도 타협을 해야될것 같음.

반응형
,
반응형

view가 전혀 필요없는, API만 지원하는 서비스에서는 그냥 @RestController를 쓰면 된다.


하지만, view가 필요한 곳에서는 @RestController를 사용해서 클래스를 매핑해버리면,

view로 접근을 못하는 문제가 있다.


@RestController가 @ResponseBody를 포함하고 있기 때문이다.


그래서 view가 필요하고, 해당 클래스에서 restfulAPI와 view를 함께 묶어버리고 싶은 경우에는 클래스에 @Controller로 지정해주고,

restfulAPI에는 @ResponseBody를 일일히 붙여주도록 사용을 해야 되는 것 같다.

반응형
,
반응형

새로운 회사로 오자마자, 세미나를 하라그러길래 (....)

이것저것 문서를 만들고, 데모 샘플을 만들기 위해 Spring Boot AMQP를 써보던중 삽질했던 부분이 몇 가지 있어서 정리하게 됨.


데모 샘플의 목표는 RabbitMQ 공식 홈에 있는 Tutorial 중 Worker, Publish/Subscribe에 대한 구현이었다.


Java소스를 제공은 하지만, Spring Boot가 어마어마하게 숨겨놓아서..

삽질이 필요하게 됨.


Spring Boot AMQP가 기본적으로 해주는건, RabbitTemplate에 대해서 기본설정을 해주기 때문에,

그냥 @Autowired만 붙여서 끄집어와서 사용만 하면된다.


하지만, Host가 localhost 기본으로 잡혀있기 때문에, remote 환경에 있는 경우에는 변경을 해주어야 할 것이고,

Connection관련된 세부적인 튜닝도 해주어야 할 것이다.


Host, Port등 바꾸는것은 매우 간단하다!

application.properties에서 Ctrl + Space눌러가며 입맛에 맞는 설정을 하면 되기 때문이다. sts기준..

intelliJ는 지원안할지도 모르겠다. 그래서 정리해봄.


spring.rabbitmq.host=192.168.33.10       // host (어차피 가상머신이라서 그냥 ip공개)
spring.rabbitmq.port=5672              // RabbitMQ 기본 포트 (굳이 안적어도 될듯 하지만 혹시나 실패 떨어지는걸 보기싫어서...)
spring.rabbitmq.username=jss              // localhost접근이 아니라서, 인증 정보가 필요하더군. admin페이지와 마찬가지
spring.rabbitmq.password=1234            // 비번도 유저네임과 같은 이유

저기 없는 값들은, 공식홈 가면 정말 자세하게 나와있음. 영어라는게 문제..


Host랑 인증정보는 성공적으로 커스터마이징 했다.

이제 커넥션 관련된 여러가지 옵션들을 튜닝을 해줘야 하는데.

org.springframework.amqp.rabbit.listener패키지에 있는 SimpleMessageListenerContainer가 옵션을 오버라이드 가능하도록 지원을 해준다.

그냥 아래처럼 Bean으로 만들어서 등록해버리면, 옵션 값 지정이 가능하다. 

RabbitMQ 공식홈에서 Java Client받아서 해본사람들은 알겠지만, Spring Boot AMQP가 설정까지도 여러가지 바꿔놨다.

비권장 설정들을 빼고, 좀 더 편하게 바꿔놓은 듯 하니 믿고 사용하자.


@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.setQueueNames("testQueue");
    container.setMessageListener(listenerAdapter);
    container.setMaxConcurrentConsumers(1);      // 최대 컨슈머 수 인데.. 어떤 기준인지 모르겠다.
    container.setReceiveTimeout(3000L);         // 메시지 받을 때 타임아웃 값 (ms)
    container.setRecoveryInterval(3000L);        // 연결이 끊어졌을 시 Recover 시도를 어느 주기로 할지에 대한 term (ms)
    return container;
}


여기까지 지정해주고, 바로 rabbitTemplate가지고 convertAndSend를 날리면,

지정한 queue이름에 해당하는 queue가 없다고 뭐라뭐라 에러가 떨어질 것이다.


이 문제는, Queue를 Bean으로 등록해주면 해결이 된다.


@Bean
Queue queue() {
     return new Queue("testQueue", false);      // 두번째 인자값은 디스크에 저장할지, Ram에 저장할지에 대한 flag (false : ram) 
}


위의 설정으로 인해 Queue가 존재하지 않으면, 알아서 생성을 하도록 구성이 되어, 정상적으로 Send가 될 수 있는 환경이 만들어졌다!


받는 부분은 RPC형식으로 구현을 하는 사람들은, rabbitTemplate으로 recv를 하면 되지만,

보통 Queue에 메시지가 있는지 없는지 체크하면서, 가져와서 뭔가를 하는 행위가 많기 때문에

MessageListenerAdapter라는 리스너를 지원한다. (org.springframework.amqp.rabbit.listener 패키지에 있음.)


@Bean MessageListenerAdapter listenerAdapter(Receiver receiver) { return new MessageListenerAdapter(receiver, "receiveMessage"); }



그냥 복붙하면, Receiver클래스가 없다고 나올 것인데, 만들어주면 되고

Bean생성 시 인자값에는 방금 만든 POJO를 넣어주면 되고,

MessageListenerAdapter생성자의 두번째 인자값에 있는 method를 만들어주고, 

Queue로부터, 메시지가 도착했을 경우에 할 일을 구현해주면 된다.


여기까지 구현하고, 받는 부분 2개이상 띄우고

보내는 부분으로 메시지를 쭉 보내보면, 보내는 부분에서 bind되어 있는 채널들에다가 나눠서 뿌려주게 되고,

받는 부분에서는 그에따라 나눠서 받을 수 있게 된다. 이렇게 쓸 일이 있을지는 잘 모르겠다.. -_-;;

(Worker 구현 완료)


Pub/Sub 패턴 같은 경우에는 비교적 간단한 Worker와는 다르게 Fanout Exchange라는 것을 추가로 이용해야 한다.


일단 받는 쪽에서는 고칠 게 Queue이름밖에 없다. (Queue가 여러개 있을 때 제대로 BroadCasting을 하는지 보기위해)


보내는 쪽에서는 FanoutExchange를 등록을 해주어야 하는데, FanoutExchange를 Bean으로 만들어주고, 

만들어진 Bean을 Binding해주는 작업을 해주면 된다. 

테스트를 위해 2개의 Queue를 바인딩한다.


@Bean
FanoutExchange exchange() {
      return new FanoutExchange("test-exchange");
}

@Bean
Binding binding() {  
      return BindingBuilder.bind(queue1()).to(exchange());
}

@Bean
Binding binding_2() {
      return BindingBuilder.bind(queue2()).to(exchange());
}



그리고 가장 중요한.. rabbitTemplate에서 setExchange("test-exchange")를 해줘야 정상적으로 동작을 한다.


서로 다른 Queue를 바라보고 있는 Receiver Application을 띄우고, (보내는 쪽에서 exchange로 바인딩 된 Queue여야 한다.)

메시지를 보내보면, 메시지가 각 Queue에 BroadCasting되는 것을 확인할 수 있다.

반응형
,
반응형

Spring Framework는 4.1.7, jQuery는 2.1.4 버전을 사용하여 테스트함.


[Front-End]


<head>
<script>
function checkboxArr() {
    var checkArr = [];     // 배열 초기화
    $("input[name='test_check']:checked").each(function(i)) {
        checkArr.push($(this).val());     // 체크된 것만 값을 뽑아서 배열에 push
    }

    $.ajax({
        url: 'test_check'
        , type: 'post'
        , dataType: 'text'
        , data: {
            valueArrTest: checkArr
        }
    });
}
</script>
</head>


    <input type="checkbox" name="test_check" value="1" />
    <input type="checkbox" name="test_check" value="2" />
    <input type="checkbox" name="test_check" value="3" />
    <input type="checkbox" name="test_check" value="4" />


[Back-End]


@RequestMapping(value = "/test_check", method = RequestMethod.POST)
@ResponserBody
public void testCheck(@RequestParam(value = "valueArrTest[]") List<String> valueArr) {
    // TODO
}


Back-End단에서 받을땐 String[] 형식으로 하면 안되고, List<String> 형태로 사용해야 된다.

 

예전에는 for loop돌면서 delimiter를 정해서 String하나로 만들어 넘기거나, checkbox의 이름을 각각 다르게 정해서 조합해서 억지로 넘겼던 기억이 있는데, 위 방법으로 하면 깔끔하게 정리가 된다.

반응형
,
반응형

jdbcTemplate을 이용해서 select쿼리를 날리는 경우 .query 메소드를 사용하게 된다.


그런데 .query 메소드의 경우 List형태로 리턴을 해주기 때문에,

.queryForObject 메소드를 사용하는 경우도 있다.


다만, .queryForObject 메소드의 경우에는, row가 정상적으로 1개 나왔을 경우에는 문제가 없는데,

row가 아예 나오지 않는 경우에 대해 exception을 떨구게 된다.


이곳저곳 구글링을 해보면, try catch 문으로 exception을 캐치하여 null을 리턴하도록 하라고는 하는데,

try catch 문의 경우 성능 저하의 원인이 될 수 있기 때문에, 왠만해선 사용을 권장하지 않는다.


해결방안은, .query메소드를 사용하되, 단일 행이 나올 것이라고 예상되는 구문에서는

DataAccessUtils.singleResult나 DataAccessUtils.uniqueResult를 이용하여 List형태를 Object형태로 리턴하게끔 하면 된다.


메소드 구현체 내부를 들어가보면 알겠지만, .query 메소드의 결과로 나온 List의 사이즈를 체크하여,

사이즈가 0이면 null을 리턴하고, 사이즈가 0이 아니면 첫번재 element를 끄집어와서 리턴을 해주는 방식으로 되어 있다.

반응형
,
반응형

Spring Boot의 경우 application.properties 파일로 온갖 설정을 다 할 수 있도록 되어 있다.


하지만, 개발환경, QA환경, 서비스 환경에 대해서 application.properties에 들어가는 내용이

달라지는 경우가 대부분이다.


Spring Boot는 application.properties 명을 아래와 같이 지정함으로써 profile기능을 제공한다.


application-{profile명}.properties


기존에 존재하는 application.properties는 기본적으로 모든 profile에 포함이 되게 된다. (super class 상속 개념이라고 보면 쉽다.)


저렇게 파일을 만들어주고, WAS설정에 JAVA_OPTS 에 아래와 같이 추가를 해준다.


아래는 Tomcat8 기준 Catalina.sh를 예시로 들어봤다.

ex) service profile에 대한 properties를 활성화 한다.

JAVA_OPTS="-Dspring.profiles.active=service"


Maven을 사용하는 경우, 플러으니 사용하고 profile별로 파일 복사하고 이것저것 해서 가능하지만,

어차피 JAVA_OPTS는 서비스 전에 튜닝을 해야하니, maven보다는 이게 개인적으로 더 단순해서 맘에든다.

반응형
,
반응형