반응형

스프링부트 사용하면서부터 RestTemplate을 많이 사용하여 API개발을 해왔었다.

하지만 최근에 알게된 사실은 블로킹 API로 리액티브 기반의 애플리케이션에서의 성능을 떨어트리는 원인이 될 수 있다는 걸 알게 되었다. 또한 Spring5.0버전부터는 RestTemplate은 유지모드로 변경되고 향우 deprecated될 예정이라고 한다.

따라서 대안으로 Spring에서는 WebClient사용을 권고하고 있으며 다음과 같은 장점이 있다.

  • Non-blocking I/O
  • Reactive Streams back pressure
  • High concurrency with fewer hardware resources
  • Functional-style, fluent API that takes advantage of Java 8 lambdas
  • Synchronous and asynchronous interactions
  • Streaming up to or streaming down from a server

WebClient에 대한 자세한 사용법에 대해서 알고 싶다면 아래의 블로그 글을 참고하자.

medium.com/@odysseymoon/spring-webclient-%EC%82%AC%EC%9A%A9%EB%B2%95-5f92d295edc0

 

Spring WebClient 사용법

Spring 어플리케이션에서 HTTP 요청을 할 땐 주로 RestTemplate 을 사용했었습니다. 하지만 Spring 5.0 버전부터는 RestTemplate 은 유지 모드로 변경되고 향후 deprecated 될 예정입니다.

medium.com

 

스프링공식문서

www.baeldung.com/spring-webclient-resttemplate

 

Spring WebClient vs. RestTemplate | Baeldung

Learn how to make server-side HTTP calls using WebClient and RestTemplate.

www.baeldung.com

 

반응형
반응형


Java8 Stream을 사용하다 보면 자주 발생할 수 있는 미묘한 실수 중에 하나는


이미 사용했던 Stream을 다시 사용하려고 하는 것이다. Stream은 오직 한 번만 사용할 수 있다.


Stream should be operated on (invoking an intermediate or terminal stream operation) only once. 


A Stream implementation may throw IllegalStateException if it detects that the Stream is being reused.


이미 사용했던 Stream을 또 사용하게 될 경우 다음과 같은 에러 로그를 발견할 수 있을 것이다.


java.lang.IllegalStateException: stream has already been operated upon or closed

at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)

at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)

at com.payco.cm.service.CacheService.cachingPartnerUrlMap(CacheService.java:31)

at com.payco.cm.service.CacheService$$FastClassBySpringCGLIB$$d06ada09.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)

at org.springframework.cache.interceptor.CacheInterceptor$1.invoke(CacheInterceptor.java:52)

at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:344)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:407)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:326)

at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)



[ 문제가 발생했던 코드 ]



코드를 보면 Stream을 두 번 사용하고 있는 것을 볼 수있다.


로깅기능으로 잠깐 보려고 했던 코드가 문제가 되었다.


정리하자면

Stream shoud be used only Once!!!


반응형
반응형

[ Spring ] 문제해결 no suitable constructor found, can not deserialize from Object value 


API로 JSON 데이터 받아와 모델에 매핑시키는 부분에서 다음과 같은 에러가 발생하였다.


org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not construct instance of beomcess.coin.contractor.entity.Bittrex$BittrexCoin: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

 at [Source: java.io.PushbackInputStream@423b2b62; line: 1, column: 41] (through reference chain: beomcess.coin.contractor.entity.Bittrex["result"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of beomcess.coin.contractor.entity.Bittrex$BittrexCoin: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

 at [Source: java.io.PushbackInputStream@423b2b62; line: 1, column: 41] (through reference chain: beomcess.coin.contractor.entity.Bittrex["result"]->java.util.ArrayList[0])

at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:240)

at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:225)

at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)

at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)


쉽게 말해 API 응답으로 받아와 JSON 데이터가 내가 매핑하고자 하는 Model에 알맞지 않다는 것이다...


API호출 결과 날아오는 JSON의 형태는 다음과 같았다.



내가 받고자 했던 JAVA MODEL의 형태는 다음과 같았다.


문제는 Result Array 내부에 있는 데이터들을 매핑해 오지 못해생겼던 것 같다.



일단 해결은 내부의 BittrexCoin 클래스에 static을 선언해 해결하였다.



이렇게 inner class를 static으로 변경하고 나니 정상적으로 데이터를 받아 올 수 있었다.


static 지시사의 역할은 메소드나 변수를 메모리에 로딩해서 다른 클래스가 이 클래스의 인스턴스를


생성하지 않고서도 사용할 수 있게 해주는 목적이다. 


RestTemplate으로 요청을 날리면서 동시에 내가 원하는 모델에 매핑해서 데이터를 가져오는데 static을 선언해주지 않았을 경우에


inner class인스턴스를 못만들어내서 매핑이 되지 않는 것 같다. 더 정확한 원인까지는 잘 모르겠다...


나중에 시간되면 더 파고들어볼만한 이슈인 것 같다.


혹시 원인에 대해 아시는분이 계시다면 댓글남겨주시면 감사하겠습니다. 







반응형
반응형


웹 작업을 할 때 jsp내의 문구라던지 구조를 변경하고 확인해야하는데 실시간으로 반영이 안되면 무척이나 성가시지요...


빌드다시해줘야하고.....시간은 시간대로 잡아먹고...


SpringBoot를 쓰고 계신다면 application.properties에 다음과 같이 추가해 주세요.


server.jsp-servlet.init-parameters.development=true


추가 이후에는 어플리케이션 재시작 없이 바로 웹에 반영되는 것을 확인할수 있다.

반응형
반응형


다른 도메인 내부에 쿠키 생성하기 (이게 가능해???)


최근 쿠키매칭시스템 설계를 위해 광고 프로세스 전반에 대한 내용을 학습중 우리 도메인 쿠키 내부에 


다른 도메인 쿠키값들이 박혀있는 걸 발견하였다.


뭐야 이거 어떻게 박는거야? 크로스도메인 이슈 때문에 당연히 안될거라고 생각했었는데 심지어 여러 도메인들 쿠키값들이 박혀있었다....


위의 캡쳐화면을 보면 cloud.toast.com 페이지에 들어갔는데 toast.com쿠키 내부를 보면 .toast.com말고도 여러 도메인들의 


쿠키값들이 설정되어있는 것을 보았다.


어떻게 이게 가능하단 말인가????


근데 가능하다는거....실제로 구현을 해보고서도 좀 신기하긴했다...




방법은 다음과 같다. 


해당 페이지의 html에 다른 도메인을 호출하는 호출하는 태그를 심는다. 이 때 태그는 <img>, <a> 태그가 가능하다.


다음을 보자.


local.media.com(내가 호스트에 등록해 띄운 페이지) 도메인의 페이지 내부에는 다음과 같은 태그를 박았다.


이렇게 박았을 경우 페이지가 렌더링되는 시점에 해당 도메인의 서버에 요청이 가게 된다.


요청을 받은 해당 도메인의 서버에서 요청을 받아 내부에서 쿠키를 생성하면 다른 도메인 쿠키 내부에 본인들의 쿠키값을 설정할 수 있는 것이다.


local.toast.com:8082/dsp/request 컨트롤러에서 요청을 쿠키를 만들어주게 되면




내가 생성한 쿠키 값이 박히는걸 볼 수 있다.


만약 다른 도메인의 iframe을 박아 렌더링 하게 된다면 해당 도메인의 쿠키가 하나 더 생기게 된다.


iframe으로 local.doubleclick.com:8080/adx/request를 요청해보자


이번에는 local.media.com쿠키 내부에 값이 생성되는게 아니고 별도의 local.doubleclick.com쿠키가 생성된 것을 볼 수 있다.


물론 어떻게 쿠키를 심든간에 httpServletRequest로 request를 받아 cookie를 얻어오게되도 본인 


서브도메인에 일치하는 쿠키값만 받아올 수 있다!!!(당연당연)


반응형
반응형


스프링프로젝트에서 디버깅 모드를 실행하려고 했는데 다음과 같은 에러가 발생했다.


ERROR: transport error 202: gethostbyname: unknown host

ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)

JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [debugInit.c:750]



일단 이런문제가 발생한다면 host파일을 체크해보도록 하자.



host파일에 로컬호스트가 주석처리되어 있다면 다음과 같은 문제가 발생할 수 있다.


확인해본 결과 역시나 '127.0.0.1 localhost' 에 주석처리가 되어있었다.


주석을 풀고 다시 실행시키면 정상작동한다!


반응형
반응형

 

 안녕하세요. 오늘은 간단하게 자바 EXCEPTION처리시  EXCEPTION이 처리되는 우선순위에 대한 궁금증이 생겨 확인한 내용 간단히 공유해봅니다. 포스팅을 너무 장황하게 하려하니 글쓰는 수도 줄어들고 해서 앞으로는 간단히라도 자주 기록을 남기는 습관을 들이려합니다:)


상 황 ]

 2개의 exceptionHandler 클래스가 있고 범위는 다음과 같다.  

 

1번 - @ControllerAdvice("com.nhnent.webcc")       (프로젝트 전체 패키지)          

  -   (2번이 1번에 완전 귀속)   - 

2번 - @ControllerAdvice("com.nhnent.webcc.controller.api")  (API 관련 패키지) 


 

[ 궁금증 ]

2번 패키지 범위의 클래스에서 1번 범위의 메서드 실행하다가 에러가 발생할 경우 1번 handler에서 처리가 될지 2번 handler에서 처리가 될지?


예측이 되시나요? ㅎㅎ

 

동작확인  ]

실제로 1번 범위의 클래스에서 exception이 발생했을지라도 ExceptionHandlerExceptionResolver에 의해 메서드를 호출한 상위

클래스를 찾아가게 되고 그 클래스내에서 exception이 발생하게 된다. 따라서 2번 handler에 의해서 exception이 처리되게 된다.

 

궁금하시거나 잘못된 부분이 있다면 코멘트 부탁드립니다.




오늘도 좋은하루보내세요:)

 



반응형
반응형

스프링부트(springboot)로 카산드라 데이터 모니터링 하는 프로젝트 작업 중  java.lang.NoClassDefFoundError:org/springframework/data/cassandra/mapping/UserTypeResolver 과 같은 에러가 발생해 한참을 해맸다.



Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/data/cassandra/mapping/UserTypeResolver

at java.lang.Class.getDeclaredMethods0(Native Method)

at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)

.

.

생략

Caused by: java.lang.ClassNotFoundException: org.springframework.data.cassandra.mapping.UserTypeResolver

at java.net.URLClassLoader.findClass(URLClassLoader.java:381)

at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)

at java.lang.ClassLoader.loadClass(ClassLoader.java:357)


일단 해결은 pom.xml 의 <parent>부분의 버전을 수정하여 해결하였다.

<parent>

         <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-parent</artifactId>

      <version>1.5.6.RELEASE</version>

        <!-- <version>1.2.3.RELEASE</version> -->

              <relativePath/> <!-- lookup parent from repository -->

</parent>


버전의 1.5.6버전을 하위 dependency에서 사용하는 스프링부트 버전과 맞춰주니 해결되었다. (1.5.6-> 1.2.3)


<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

<spring-boot.version>1.2.3.RELEASE</spring-boot.version>

<spring-data-cassandra.version>1.2.0.RELEASE</spring-data-cassandra.version>

<spring-cql.version>1.2.0.RELEASE</spring-cql.version>

<datastax.cassandra.version>2.1.7</datastax.cassandra.version>

</properties>


버전이 맞지않으면 예상치 못한 예외가 발생한다. 디텍팅하기도 너무 힘들뿐도로 원인파악이 너무 힘들기 때문에 메이븐 설정을 다른 프로젝트 등에서 가져다 


사용 할때는 버전을 꼭 참고하여 사용하도록 하자. 무작정 사용하는 ctrl+c, ctrl+v는 재앙을 가져올지니...

반응형
반응형


 안녕하세요. 오늘은 프로세스와 스레드에 대해서 알아보도록 하겠습니다. 많은 분들이 실제로 프로세스와 스레드를 알고 계시면서도 막상 물어보면 대답하기 막막해하시는 경우도 많이 있고 저도 실제로 프로세스&스레드에 대해서 명확히 설명하기 힘들어 했던 경험이 있어 이번 기회에 한 번 간단히 개념적인 부분에 대해서 정리하고 넘어가려고 합니다. 이제 막 2년차에 접어드는 현업 개발자지만 요즘 느끼는 것은 새로운 기술들에 관심을 가지는 것도 중요하지만 개발을 함에 필요한 '기본적인 내용들과 지식을 좀 더 단단히 쌓아야 겠다'는 생각이 많이 들곤 합니다. 포스팅 시작하도록 하겠습니다.



[ PROCESS ]

 먼저 프로세스란 간단히 설명해서 실행중인 프로그램에 대한 인스턴스를 프로세스라고 합니다. 실제로 프로세스가 생성되게 되면 해당 프로세스는 운영체제로부터 주소공간, 파일, 메모리 등을 할당받게 됩니다. 그리고 메모리 공간은 CODE, DATA, HEAP, STACK영역으로 나뉘어지게 됩니다. 실제로 프로세스가 처리해야 될 일을 각각의 메모리 영역들 위에서 처리하게 되게 됩니다.

프로세스는 다음과 같은 독립된 메모리공간을 프로세스별로 가지게됩니다. (스레드 설명할 때 설명하겠지만 스레드는 STACK 부분만 독립적으로 가지게 됩니다.) 그렇기 때문에 특정 PROCESS가 다른 PROCESS 메모리에 직접 접근하기 힘듭니다.


[ THREAD ]

 스레드란 프로세스 안에서 동작되는 여러 실행의 흐름이라고 보시면 됩니다. 간단히 말해서 프로세스 내부에서 실제로 일을 하는 녀석들을 스레드라고 합니다. 우리 몸으로 치면 몸은 프로세스 손,발은 스레드 정도가 되지 않을까요?(단순한 제 생각) 기본적으로 하나의 프로세스가 생성되면 하나의 스레드가 같이 생성이됩니다. 이를 메인 스레드라고 부르며, 스레드를 추가로 생성하지 않는 한 모든 프로그램 코드는 메인 스레드에서 실행이 된다고 생각하시면 됩니다. 또한 프로세스는 메인 스레드 외에도 여러개의 스레드를 가질 수 있는데요. 이를 멀티스레드라고 합니다.

 스레드의 경우에도 프로세스와 같이 메모리 공간을 할당 받게 되는데요. 프로세스가 각각의 독립적인 메모리 공간을 할당받는 반면에 스레드의 경우는 Stack영역만을 독립적으로 할당 받게 됩니다. 실제로 Code, Data, Heap영역은 프로세스의 공간을 공유받아 사용하게 됩니다. 이렇듯 프로세스 내에서 생겨나는 모든 스레드의 경우 Stack이외의 영역은 프로세스의 영역을 공유받아 사용하게 되고 이러한 이유로 인해 스레드간 Context Switching(아래에 간단히 설명)이 발생했을 경우 Stack 영역의 데이터들만 switching되면 되므로 프로세스 스위칭보다 훨씬 빠르게 진행이 됩니다. 스레드의 장점으로는 시스템의 Throughput(처리량)이 향상 되며, 자원 소모가 줄어들고 스레드간의 스위칭 시간이 줄어들면서 일을 처리할 때 응답 시간이 단축되는 점이 있습니다. 반면에 여러 개의 스레드를 사용할 때는 프로세스가 가지고 있는 메모리 영역의 자원 공유의 문제가 발생할 수 있고 디버깅이 힘들다는 점이 있습니다. 마지막으로 프로세스의 메모리 영역(Data, heap)을 공유함으로써 전역 변수와 동적 할당 딘 메모리 공간을 공유하게 되고 이를 통해 쓰레드간 통신이 쉽게 가능하게 됩니다.


[ THREAD가 스택을 독립적으로 할당하는 이유 ]

 스택은 함수 호출 시 전달되는 인자, 되돌아갈 주소 값 및 함수 내에서 선언하는 변수 등을 저장하기 위해 사용되는 메모리 공간입니다. 따라서 스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고, 이는 독립적인 실행흐름을 가질 수 있다는 것입니다. 결과적으로 실행 흐름의 추가를 위한 최소 조건이 독립된 스택을 제공하는 것이라고 볼 수 있습니다.


[ Context  Switching 이란? ]

 프로세스를 이것저것 우선 순위에 따라 변경하기 위해서는 실제로 작업에 사용되는 프로세스 데이터를 레지스터와 메모리 사이를 왕복하며 값을 복사해야 합니다. 보통 cpu에 의해 프로세스가 일을 처리하게 되는데 스케줄링 방식에 따라 특정 프로세스가 일을 진행하고 있다가 다른 프로세스에게 cpu사용권을 넘겨주게 될 때 어디까지 작업을 했고 다음부터는 어디서부터 작업을 해야하는지에 대한 정보를 보관할 수 있는 곳이 필요하고 이러한 정보들을 프로세스에 맞게 변경해주고 처리하는 것을 말합니다. 실제로 CPU는 동시에 한 개씩만 스레드를 실행시킬 수 있는데 스레드가 여러개가 생성되게 되면 CPU는 각각의 스레드를 번갈아가며 실항하게 되는데, 이 때 이전 스레드의 문맥 정보(레지스터 값, 실행중인 스택 정보 등)을 백업받고 백업 받아놓았던 다음 스레드의 문맥정보를 로딩하는 과정을 거치게 됩니다. 이 과정을 Context Switchig이라고 합니다. 이러한 스레드가 많아질 수록 Context Switching에 많은 부하가 걸리기 때문(메모리와 레지스터 사이의 데이터 이동도I/O이다)에 잘 고려해서 사용해야 되겠습니다.  


오늘 포스팅은 여기까지 하도록 하겠습니다. 잘못된 부분이나 설명이 부족한 부분은 댓글로 남겨주시면 참고하도록 하겠습니다. 감사합니다.




반응형

+ Recent posts