글쓴이 보관물: LeekyoungIl

Spring Boot Book 콘서트 참석 후기

도서 Boot Spring Boot! 의 저자 김지헌 님이 발표를 하셨던 북 콘서트에 다녀오고 정리해 봅니다.

 

Spring Boot 란?

  • Web Application 을 쉽고 빠르게 개발할 수 있다.
  • Auto configuration 으로 접근 장벽을 낮췄다.
  • 나온지 5년이 지났지만 아직도 한번도 써보지 않은 개발자들이 많다.

Spring Boot 가 쉽고 빠른 이유

  • 많은 Starter project 들이 있어 필요한 기능을 쉽게 사용할 수 있다.
  • 참고 : https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters

Jar vs War

  • Spring Boot 는 Embedded container 를 포함한 FAT jar 를 이용한 Application deploy 를 권장한다.
  • Cloud Native Java 참고 : http://www.yes24.com/24/goods/61788283?scode=032&OzSrank=1

Spring Boot 1.5.x vs 2.x.x

  • Java 8 이상을 완벽하게 지원하는 Spring Boot 2.x.x 이상을 사용하라
  • Spring Boot 2.x.x 에서부터는 Tomcat-JDBC 에서 HikariCP 가 기본으로 변경 되었다.
  • 참고 : https://github.com/brettwooldridge/HikariCP
  • 1.5.x 는 java 1.6, 1.7, 1.8 을 위해 만들어졌다. (하위 호환성을 위한 희생이 있음)
  • 2.0.x 는 java 1.8 이상을 위해 만들어졌다.
  • 1.5.x 와 2.x.x 는 actuator 사용시 endpint 가 달라졌다.
    • 1.5.x : /{endpoint}
    • 2.x.x : /actuator/{endpoint}
  • 1.5.x 와 2.x.x 의 container 는 명칭과 기능이 달라졌다.
    • 1.5.x : embedded container
    • 2.x.x : web server (reactive 지원)
  • 1.5.x 는 2019-08-01 에 지원이 끝난다. 따라서 가능하면 2.x.x로 갈아 타도록 하자

Spring Boot Auto Configure

  • Spring Boot 는 편리성을 위해서 Auto Configure 를 지원한다.
  • 본인이 사용하는 모듈이 어떤 설정으로 구동 되는지 확인하자 : https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure

Spring Boot Dependency

  • Spring Boot 는 dependency에 버전을 명시하지 않아도 동작을 하는게 기본 설정되어있는 버전이 있기 때문이다.
  • 본인이 사용하는 모듈의 라이브러리 버전을 확인하도록 하자
  • Spring Boot reference 참고 : https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#appendix-dependency-versions

Debug 모드 활용

  • Application.yml 이나 properties 에 debug : true 로 설정해 주면 (기본값은 false)
  • 라이브러리 간에 의존성이나 기타 디버깅을 볼 수 있다.

 

저자 싸인 받았음 히히

RabbitMQ client 에서 useNio 사용시 timeout 이 먹히지 않는현상…

개인적으로 개발하고 있는 OpenSource Project illuminati 는 rabbitMQ 를 사용중이다.

illuminati를 프로젝트에 적용을 하면 해당 프로젝트가 bootrun 할때 같이 올라가는데
rabbitMQ 에 문제가 생겨 컨넥션을 만들지 못할 경우 장시간 멈추는 (약 1분 20초정도) 현상이
로컬 테스트에서 발견 되었다.

분명 소스에는 timeout 이 설정되어있었지만 소용이 없었다.

Connection factory 의 useNio 옵션을 사용하지 않고 useBlockingIo 옵션을 사용할 경우
정상적으로 5초후에 timeout 이 나는것을 확인 하였지만 useNio 옵션을 사용할경우 1분이 넘게
대기를 하는 현상을 다시한번 확인하고 amqp-client 소스를 까보기 시작했다.

useNio 옵션을 사용하느냐 하지 않느냐에 따라서

FrameHandler 를 생성하기 위한 Factory 는 2가지의 구현체를 사용하는데

nio : SocketChannelFrameHandlerFactory
io : SocketFrameHandlerFactory

일단 io (blockingIo) 부터 보면

socketConnect 라는 native method 를 사용하는것을 볼 수 있으며 timeout 를 파람으로 받아서 사용할 수 있는것을 볼 수 있다.

다음 nio 를 보면

Net.class 에서 connect0 이라는 native method 를 사용하는 것을 볼 수 있고 이놈은 timeout 를 파람으로 받지를 않는것을 볼 수 있다.

헐…. 뭐지… 하고 찾아본 경과

이놈은 OS 의 tcp timeout 설정을 사용한다는것을 알 수 있었다.

sysctl 명령을 이용하여 TCP 설정들을 보면

이렇게 1분 15초가 설정된것을 볼 수 있다.

왜 timeout 을 임의로 어플리케이션 딴에서 설정을 할 수 없는지

설정할 수 있는 native method 가 있는지는 아직 찾지 못했다.

나만 몰랐던 이슈로

일단 정리한대까지 까먹기 전에 기록으로 남겨 둔다. (나의 스승님들이 답을 주실듯)

Elasticsearch 에서 Mapping 의 중요성을 느끼다.

안녕하세요.

제가 개인적으로 개발하고 있는 프로젝트가 있습니다.
https://github.com/LeeKyoungIl/illuminati Project illuminati 인데요.
요즘은 gatekeeper라는 서브 프로젝트를 열심히 개발 중이죠.
https://github.com/LeeKyoungIl/illuminati/tree/feature/gatekeeper_new

Project illuminati는 데이터를 결국 ES로 보내주어 그 데이터를 Kibana에서 사용하도록 만들었던 터라
ES로 save 만 할 뿐 Read 성 기능을 필요가 없었습니다. (어차피 Kibana에서 해주니…)

하지만 Kibana 도 제가 잘 다루지 못하고… 원하는 대로 데이터를 뽑지도 못해서 visualization 툴을 새로 만들고 있는데 그게 위의 Gatekeeper 프로젝트인데요.

여기서 직접 ES 데이터를 핸들링하려고 보니 ES에서 데이터를 읽어오는 기능이 필요하더라구요.
그래서 작업을 하는 중 일반적인 데이터의 검색은 별 문제가 없습니다. 아시다시피 간단한 Query로도 데이터를 쉽게 뽑아낼 수 있죠.

하지만 RDB에서의 Group By에 의한 값과 count를 구하는 Query와 동일한 결과를 뽑아야 하는데 쉽지 않더라구요.

(보통은 업무상에서 ES를 쓸 때 빠른 검색 결과를 뽑기 위해서 다양한 필드에 특정 키워드 검색 (날짜와 같이) 정도로 쓰지 ES를 이용해서 데이터 집계를 하는 용도로는 사용해 본 적이 없는 거 같네요.)

하지만 물론 집계 쿼리 자체는 어렵지 않습니다.

간단하게 aggregation query 만 날리면 뽑을 수 있죠… 하지만.. 저는 해보니 오류가…

저는 serverInfo 의 hostName field를 이용해 hostName 별로 호출된 수를 구하고 싶었는데 기본적으로 serverInfo.hostName의경우 text field로 설정이 되어있습니다. 이경우 집계 쿼리를 사용할 수 없나 봐요. (저도 공부 중이라..)

그래서 fielddata=true로 설정하라고 하네요. (메모리가 많이 사용될 수 있다고 합니다.)

이 방법 보다는 좀.. 찾아보니

이렇게.. 정렬이나 집계를 원하면 text 가 아닌 keyword로 설정을 하라고 되어있더라구요.

그래서 새롭게 index를 mapping 해주기 위해 쿼리를 날렸습니다.

결과는 오류…

오류가 난 이유는.. ES는 매핑을 update 할 수 없습니다. 만약 데이터가 있다면 update시 타입이 변경이 불가능하기 때문이죠.

그래서 index를 날리고 새롭게 맵핑을 해주었습니다.

다음 다시 집계 쿼리 실행!

제가 원하는 대로 데이터가 잘 나오는군요.

 

ES는 데이터를 입력 시에 자동으로 데이터를 기반으로 mapping을 생성합니다.

제가 이번에 테스트를 한 hostName는 제가 한것처럼 따로 mapping을 하지 않으면 데이터가 입력이 될때 text로 맵핑이 자동으로 된 걸 볼 수 있습니다.

그래서 ES에서는 맵핑을 직접 하는 것을 권장하는 것 같습니다.

그럼 하나하나 직접 이렇게 mapping query를 날려 줘야 하나?? 해서 만들어 보았는데요.

MappingType 이라는 enum을 만들었습니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/enums/MappingType.java#L3-L27

다음 해당 Mapping값을 사용할 GroupMapping 에노테이션을 하나 만들어 주었습니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/GroupMapping.java#L7-L13

디음은 제가 집계를 원하는 항목에 해당 에노테이션을 달아 주었습니다.
항목은 ServerInfo class의 hostName 과 serverIp입니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/ServerInfo.java#L25-L26

ES로 데이터를 전송할 class 를 시작으로 하위 클래스들을 전부 돌면서 해당 @GroupMapping 에노테이션과 GSON의 @Expose 에노테이션이 걸린 것들을 전부 찾아서 MappingBuilder 에 set 해주는 부분 입니다.

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/model/IlluminatiEsTemplateInterfaceModelImpl.java#L263-L286

이제 ES로 전송을 해야겠죠 이 부분에서 고민 많이 했습니다.

mapping 은 이미 생성되어있으면 수정이 불가능하기 때문에 ES로 데이터를 저장하기 전에 먼저 mapping 정보가 있는지를 먼저 체크를 한 다음 없다면 mapping을 만들어 주고 (@GroupMapping 에노 테이션을 걸은 것들만 생성이 먼저 날아가며 나머지는 최초 데이터 저장 시 자동으로 mapping 됨) 나서 다음 ES로 데이터를 저장하는 방식으로 일단 구현해 보았습니다.

이게 항상 ES에 데이터를 저장할 때 먼저 체크하는 호출이 날아가야 하기 때문에 정말 마음에 들지 않습니다만.. 일단 이렇게 한 뒤 차차 수정해 나가도록 할 생각입니다.

아무튼 로직은 아래와 같습니다.

ES 에 save 를 할때 먼저 index를 체크하고 없다면 만들어 준다. (원래 mapping 이지만 RDB에 친숙하게 index로 메서드 명을 만들었습니다.)

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/infra/ESclientImpl.java#L56-L57

ES에 해당 mapping 이 있는지 없는지 질의하고 없다면 index 를 생성해 주는 질의를 날리는 부분 입니다.

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/infra/ESclientImpl.java#L165-L173

제가 아직 ES를 잘 모르고 공부를 하는 중 이라.. 이렇게 만들어 보았는데요.

역시나… ES에 데이터를 저장 할때마다 mapping 을 체크 하는 부분이 걸리네요..

좋은 의견 있으면 부탁좀 드리겠습니다~

코드 작성 시 무엇을 우선시할 것인가? 성능? 가독성? 유지보수성?

제가 개인적으로 개발하고 있는 프로젝트가 있습니다.
https://github.com/LeeKyoungIl/illuminati Project illuminati 인데요.
요즘은 gatekeeper라는 서브 프로젝트를 열심히 개발 중이죠.
https://github.com/LeeKyoungIl/illuminati/tree/feature/gatekeeper_new

이 서브 프로젝트를 개발 하면서 코드가 전반적으로 구조나 내용이 개선이 많이 되고 있는데요.

요즘 들어 작업을 계속하면서 생각하는 게 우리가 코드를 짤 때 무엇을 우선시 하며 짤 것인가?

  1. 성능
  2. 가독성
  3. 유지 보수성

3가지 다 중요합니다.

하지만 요즘처럼 하드웨어 파워가 좋은 시절은 1번은 정말 심하지 않은 경우 (막 loop 몇 개씩 중첩) 크게 상관이 없지 않을까 싶네요.

제가 요즘 개발을 하면서 (Java 기준으로) 생각하는 부분이 있는데 가령 Boolean 관련 조건문 처리에서

많은 분들이 이런 식으로 코드를 짜고 있습니다.

가장 단순 하지만 폰트에 따라 false 체크의 경우 앞에 ! 표가 잘 안 보일 때도 있고.. (노안이..) 실수를 할 수도 있는 여지가 있다고 생각해요.

그래서 저는 요즘 이런 코드를

이렇게 작성하고 있는데요.

당연히 Boolean 은 bool 의 레퍼 타입 이기 때문에 성능적으로 떨어지기야 하겠죠..

하지만 가독성은 월등히 높져? 그리고 솔직히 성능도 뭐 크게 차이 안 날 듯 합니다.

(그리고 소스 보면 public static final 임)

그래도 성능이 얼마나 차이가 날까? 하고 테스트를 해보았는데 결과는 아래와 같습니다.
(단순 for문 1억번 조건문 비교)

nanos 기준
if (a) : 4090656
if (a == Boolean.TRUE) : 6280227

ms 기준
if (a) : 3
if (a == Boolean.TRUE) : 5

그리고 다른 사람들 의견을 구글링을 해서 좀 찾아보았는데 글 중에 마음에 와 닿은 글이

정리를 하면 성능보다는 단순성, 가독성, 유지보수성이 무엇보다도 중요하다 라는 소리죠.

위에 제가 개인적으로 하는 프로젝트의 경우는 저 혼자 하는 것이라 어떤 방법으로 짜던 상관없지만… (오픈 소스이긴 해도)
우리가 업무적으로 협업을 할 때는 가독성이 최우선되어 유지보수가 쉬운 코드를 짜야하지 않을까요?

지옥에서 온 CTO 님 (제가 존경하는 형님이 그분을 그렇게 부르더라구요?) 이 예전에 페이스북에 남기신 글이 있는데 주석이 필요 없이 코드만 보고도 이해가 가능한 코드를 짜야한다.

저도 업무를 하면서 지난날을 돌이켜 보며 많이 반성을 하고 (동료들에게 미안…) 글고 저 말을 가슴에 새기며.. 개발을 해야겠습니다.

저는 아직 실력이 부족해서 이렇게 까지 코드를 가독성 있고 주석이 필요 없을 정도로 짤 능력이 안되지만 목표가 생겨서 즐겁네요.

 

ps. 해당 주제로 시니어 분과 대화결과 프리미티브 타입만으로도 boolean이라는 것이 전달이 잘 되는데 굳이 레퍼런스 타입을 사용할 필요가 없고 레퍼런스 타입이 가치가 있는 경우가 있는 부분이 있긴 한데 이런 부분에서는 사용하는건 좀 오버엔지니어링 것 같다는 의견을 주셨습니다. 곰곰히 생각해본 결과 시니어 님이 의견 주신대로 true 는 if (a) false 는 if (false == a) 이정도로 사용해도 충분히 가독성을 높일 수 있을꺼 같습니다. (정상혁님 의견 감사 드립니다.)

대용량 Job 처리시 Hibernate Session Memory leak 경험

안녕하세요. 이경일입니다.

그동안 illuminati 개발을 하느라고 블로그에 정리를 하는 것을 소홀히 했네요.

Project illuminati git : https://github.com/LeeKyoungIl/illuminati <- 허접하지만..별 좀 주세요… (굽신굽신)

오늘은 hibernate를 사용하는데 대용량 Job 처리 시에 발생할 수 있는 OOM 에 관해서 정리를 해보려고 합니다.

제가 하는 업무에서 외부와 연동하는 Job을 돌릴 일이 있었는데요. 종종 오류가 발생을 하는 현상이 발견되었습니다.

해당 Job은 실행이 될 때마다 대상의 개수가 변하기 때문에 작게는 몇백 개 많게는 몇십만 개가 넘을 때도 있었죠.

현상은 몇백 개 돌 때는 괜찮은데 10만 개가 넘어가면 오류가 발생을 하더라구요…

오류부터 보겠습니다.

 

 

OOM 이 발생하고 있는 것을 볼 수 있습니다.

해당 잡은 하루에 한 번 돌고 한번 돌면 한 시간 이상을 그냥 돌아가는 대용량의 잡이죠…

Job 서버라 10만 개가 넘어갈 무렵 heap Dump를 떠 보았습니다.

보니 메모리 릭이 발생하고 있네요.

전체 Heap의 74%를 점유하고 있는 부분을 찾아보았습니다. StatefulPersistenceContext 에 메모리가 누적이 되어있어서 자세하게 어떤 Object 인지 찾아보았습니다.

 

헐… HashMap 노드 개수 보이시나요?

 

동일한 이슈가 있나 구글링을 한 결과 발견을 했고.

(구글신 없으면 개발 어떻게 할지 막막하네요 ㅜㅜ…)

대강 핵심만 정리해 보면 Hibernate session 이 열려서 객체를 영속화 하면 당연히 cache를 하는데요. job의 경우 보통은 jenkins를 이용해서 실행을 시키면 빌드를 하고 -> Job 실행 -> 종료 이런 라이프 사이클을 가지죠 그러면 ORM을 사용하면 Hibernate session 은 Job이 실행을 하고 종료를 할 때까지 살아 있다고 보면 됩니다. 따라서 해당 Job에서 100개던 20만 개던 데이터를 DB에서 영속화 시키는 object를 전부다 cache 해서 가지고 있겠죠. 이게 Heap 용량을 넘어가지 않는다면 잡이 종료되며 클리어가 되겠지만 넘어가면 OOM이 발생하며 Job이 중지가 되는 상황인 겁니다. 따라서 session을 flush 하고 clear를 해줘야 하는 이슈가 생기는 것이죠.

 


따라서 코드에 해당 부분을 추가해 주었습니다.

 

결과는 짠~? 해결 되었습니다.

오늘도 이렇게 하루를 넘기는군요.

 

 

나만 몰랐던 Http Delete Method payload body 문제

오늘 해당 문제로 좀 삽질을 했습니다.  (다 저의 무지함 때문에 발생한 일이지만… ㅜㅜ)

간단한 Delete 요청을 처리하기 위해 동료 개발자와 협업을 하고 있었죠.  서로 다른 도메인을 담당하기 때문에 직접 해당 DB에 붙어 CRUD를 날릴 수가 없었고
시스템 간의 의존도를 없애기 위해 동료 개발자는 API를 만들어 주고 저는 해당 API를 이용해서 특정 정보를 CRUD 하는 작업을 하고 있었죠.
CRU까진 완료된 상황 이제 D (Delete)가 남아서 Http의 Delete 메서드를 이용해서 Pqyload Body에는 간단한 ID 정보만 실어서 요청해주면 끝.

집에 가야지… 했는데 해당 API요청이 오류가 나는 것이 었습니다.

해당 코드입니다. 저희는 Grails + Groovy를 사용 중인데 Grails의 restBuilder를 사용해서 Json으로 변환된 Map을 이용해 Rest API 호출을 하는 간단한 코드입니다.

restBuilder 는 Spring 의 RestTemplate 으로 구현되어있습니다.

이제 동료 개발자가 만든 받는 쪽 소스를 보면

이렇게 간단하게 Delete 메서드로 요청한 Json String을 RequestBody로 파싱 해서 객체로 만들어 주는 간단한 Controller 코드입니다.
하지만 요청을 하면 오류…… API 서버 로그를 보면

이렇게 오류가 나오고 있었죠.
처음에는 또 Grails 가 이상하게 구현이 되어있는 줄 알고 Break 포인트 걸어가며 한줄한줄 소스를 까 보았는데 문제가 없는 상황…

그래서 구글링을 해서 Http Spec을 확인…

아 젠장 ㅜㅜ Spec먼저 보고 할껄…

허접 번역을 하면 “Delete 요청은 payload body 가 필요가 없다.  Delete 메서드로 payload body를 실어 요청을 하면 요청이 거절될 수 있다.

일단 API 서버가 제 담당이 아니지만 당장 일정이 있기에 최소한의 수정으로 끝내고자 일단 Delete Method를 이용해 payload body 를 보낼 방법을 찾기 시작했습니다.

일단 나온것이

톰켓의 컨넥터 설정을 바꿔서 DELETE 메서드와 같이 오는 payload body를 파싱 하는 방법 , 해당 코드는 Tomcat를 사용할때 설정으로 하는 방법이고

Spring Boot 의 embedded tomcat 을 사용한다면.

대략 이런식으로 해주면 됩니다.

하지만 위 방법으로 결국 동작 하지 않았습니다…. 결국 그냥 동료에게 양해를 구하고 …

그냥 내가 직접 API서버의 코드를 PathVariable 로 ID 값을 받도록 수정을 했습니다.

이상으로 나만 모르는 Delete method 사용법이었습니다.

오늘의 교훈… 머리가 나쁘면 몸이 고생하며 괜한 야근을 한다… ㅜㅜ 스펙을 읽어보고 개발을 하자.

그런데 의문점 하나는 Swagger 에서는 delete method 로 payload body 에 json 데이타를 같이 보내면
동작을 한다는거…. 이거 소스한번 까봐야 겠어요.. 지금은 너무 힘들어서 일단 여기까지. ㅜㅜ

[Spring Cloud] Config Server에 관하여 알아봅시다.

안녕하세요. 이경일입니다.
전부터 Spring Cloud Config서버에 관해서 정리를 하고 싶었는데 SpringCamp 2017 준비와 먹고사는 문제로 하지를 못했네요.

오늘 연휴를 맞아 Spring Cloud Config서버에 관해서 정리를 좀 해보려고 합니다.

What is Spring Cloud Config?

모든 Application은 설정이 필요합니다. DB주소와 접속 정보는 어떻고 연동할 Server의 주소는 뭐고 NoSql의 접속 정보 등등 아마 연차가 높으신 개발자 선배님들은 전에 이런 정보들을 소스코드에 하드코딩을 하고 필요할 때마다 주석을 풀었다 걸었다 하며 배포를 하던 시절을 경험하셨을 걸로 생각됩니다.

이러다가 Framework이 발전과 코드와 설정이 분리되어 만들자(?)라는 패러다임으로 application.properties 같은 환경에 따라 설정을 다르게 할 수 있는 방법이 나와서 지금도 많이 이용하고 있습니다.

따라서 이제는 환경별로 (local, dev, stage, real… 등등) 설정 파일을 분리하여 profile을 사용해 배포가 가능하게 되었죠. 하지만 이 방법에도 문제점은 있습니다. 설정 파일이 변경이 되면 다시 빌드&배포를 해야 한다는 점입니다.

또한 오늘날처럼 Traffic홍수의 시대에 더 이상 단일 Application으로는 이 Traffic을 감당하기가 어렵게 되었습니다.

서버의 규모가(클라우드 환경의 VM) 100대가 넘어가는 것이 일상처럼 되었습니다. (VM이 100대면 안에 Application은 더 많죠..) 이런 상황에서 동일한 Application에 다양한 환경으로 배포를 해야 하는 필요성이 생겼으며 이런 필요성을 효율적으로 관리하기 위해 해당 설정을 환경별로 중앙에서 관리를 해야 할 필요성이 생겼습니다.

Basic of Spring Cloud Server Architecture.

기본적인 구조를 설명하기 위해 위의 그림을 그려봤는데요. Spring Cloud Config의 경우 코드와 설정을 분리하기 위해 설정 파일들을 외부의 Git repository에 보관을 합니다. (코드와 설정의 분리는 Twelve factors 방법론을 참고하시면 좋을 거 같습니다.)

Spring Cloud Config Server의 경우 Git에 올라가 있는 properties, yml로 작성한 설정들을 내려주며 Spring Boot로 작성된 Application의 경우 bootRun시 해당 설정을 http로 받아서 에 사용하게 됩니다.

따라서 100개던 1000개던 동일한 환경과 설정으로 Application구동이 가능하여 설정을 중앙에서 관리를 할 수가 있으며 중요한 점이 Spring Cloud Server의 경우 설정을 Json으로 내려주고 있는 것입니다.

때문에 어떤 언어라도 해당 설정을 가져다 쓸 수 있습니다. 아마 이미 Open Source로 Client library 등이 나와있을 걸로 생각됩니다. (?)

참고로 Spring Cloud에서 또 중요한 모듈이 Service Discovery & Registry서버인 Eureka인데요 이 Eureka도 마찬가지로 Rest API 기반이라 다양한 언어에서 사용이 가능하며 Library도 많이 나와 있습니다. (?)

대중적인 Json포맷과 RestAPI를 지원한다는 점에서 Spring Cloud는 매우 유연한 Platform이라고 생각이 됩니다.

Micro Service Architecture로 구현 시에 꼭 동일 언어로 구현할 필요는 없습니다. 진정한 MSA를 위해서는 작은 Service와 해당 Service의 성격에 잘 맞는 언어를 사용하면 된다고 생각됩니다. 따라서 Spring Cloud Platform은 MSA를 구성할 때 그 중심점으로 사용하기에 적당하다고 생각이 됩니다.

How to use Spring Cloud Config?

일단 시작을 하기 전에 Git repository를 준비합니다. Public이던 Private이던 상관은 없습니다.

1. Create Spring Cloud Config Server

Spring Initializr에 접속합니다. (http://start.spring.io) “Search for dependencies”에 Config Server를 입력해서 선택을 해주면 위의 이미지처럼 “Selected Dependencies”에 들어오게 됩니다. 그 상태로 Generate Project 해주면 해당 프로젝트가 다운로드됩니다.

해당 프로젝트의 Application.java파일을 열고 @EnableConfigServer 에너테이션만 붙여주면 끝! 이 아니라 설정이 남았습니다.

2. Connect Git Repository with Spring Cloud Config Server

아까 처음에 만들어둔 Git repository를 연결해 보겠습니다.

Git repository 주소를 복사해서

application.yml 파일의 spring.cloud.config.server.git.uri 부분에 주소를 적으시면 됩며 Public Repository의 경우는 username과 password에 적지 않으셔도 됩니다.

(운영 서버에서는 ssh public key를 이용해서 접속하시면 됩니다.)

보통은 사내 Git Repository는 LDAP을 이용한 인증을 하는데요.  저는 실수로 username, password를 적고 git 에 push를 한 적이 있습니다. ㅜㅜ 하아… 제 정보를 몇 명이나 보았을지… 다른 분들은 조심하시기 바랍니다…

3. Create yml and Push to Git Repository

다음은 설정 파일을 환경별로 만드시면 됩니다.

  • dev : csclient-dev.yml
  • beta: csclient-beta.yml
  • real: csclient-real.yml
참고로 파일을 만드실 때는 {프로젝트명}-{환경명}.{yml 또는 properties}로 만드시면 됩니다.

안에 내용은 간단하게 Hello World로 보여드리기 위해…

이렇게 넣었습니다.

다음에 이 내용을 Git Repository에 Push 해주시면 됩니다.

이러면 Config Server사용 준비는 끝났습니다.

4. Spring Cloud Config Server bootrun and check config value

이제 bootrun을 합니다. 다음 실행이 완료되면 브라우저에 아래의 URL로 접속해 보시면 됩니다.

  1. http://localhost:8093/csclient/dev
  2. http://localhost:8093/csclient/beta
  3. http://localhost:8093/csclient/real

이렇게 우리가 yml로 설정했던 데이터가 Json으로 나오는 것을 볼수 있죠. 이걸로 Spring Cloud Config 서버 준비는 끝났습니다.

How to use Spring Cloud Config on Spring Boot Application?

이제 Spring Cloud Config서버를 Spring Boot Application에 연결하여 사용하는 csclient서비스를 만들어 보도록 하겠습니다.

Spring Cloud Config서버를 만들었던 것처럼 http://start.spring.io 에 접속해서 프로젝트를 만들도록 합니다.

config client와 web을 그리고 actuator와 view template로는 Freemarker 추가한 다음 Generage Project를 눌러서 프로젝트를 생성해 주시면 됩니다.

Project를 열고

bootstrap.yml파일을 추가한 뒤 이미지와 같이 config 서버 설정을 해줍니다.

bootstrap.yml파일은 spring cloud application에서 application.yml 파일보다 먼저 실행이 되기 때문에 config 서버의 설정을 써주면 bootrun 되기 전 Config Server에서 환경에 맞는 설정 파일을 불러와 실행이 되게 됩니다.

따라서 먼저 Build시에 application.yml보다 먼저 설정이 필요할 경우 bootstrap.yml에 설정을 해주면 됩니다. 그러면 이제 실행을 해보도록 하겠습니다.

그전에 아까 만들었던 설정파일 3개 기억 하고 계십니까?

  • dev : csclient-dev.yml
  • beta: csclient-beta.yml
  • real: csclient-real.yml

boot Application을 실행시킬 때 -Dspring.profiles.active={환경} 옵션을 같이 사용해 주셔야 합니다. 이렇게 실행을 시켜주면

제대로 실행이 된 것을 알 수 있습니다. 이제 Config 에 설정한 값을 이용해서 Hello World를 찍어 보도록 하겠습니다.

이제 csclient프로젝트에 Service를 2개를 만듭니다.

보면 Static, Dynamic 2가지 서비스입니다. 일단 둘 다 config server에서 데이터를 가지고와 출력해 주는 Service이지만 하나는 일반적인 Application처럼 처음 Build시에 정해진 값이 다시 빌드되기 전까지 바뀌지 않습니다. 하지만 Dynamic은 Config Server의 기능을 이용해 Dynamic 하게 재 빌드 없이 값이 바뀔 겁니다.

이제 코드를 보겠습니다.

먼저 ConfigServerTestStaticService 입니다.

코드에서 보다시피 아까 앞에서 config repository 에 저장했던 yml 파일에서 데이터를 꺼내와서 Map에 담아 리턴해 주는 단순한 코드입니다.

이제 ConfigServerTestDynamicService입니다.

ConfigServerTestStaticService와 달라진 것은 저 @RefreshScope가 달려있다는 것이죠. 저 에노테이션이 달려있는 Class는 config repository에 yml을 변경해 주면 Build 없이 자동으로 변경이 됩니다. (사실 완전 자동은 아닙니다… 이유는 뒤에서..)

마지막으로 이 2 서비스를 연결해줄 Controller를 만들도록 합니다.

ConfigServerTestController를 만들었습니다. 해당 Controller에서는 각 /config/static과 /config/dynamic을 각 서비스로 연결해주고 있습니다.

마지막으로 단순하게 map에 들어있는 값을 찍어주는 Freemarker view template를 만들었습니다.

 

이제 테스트를 해보도록 하겠습니다.

 

2가지 “/config/static”, “/config/dynamic“ 에 접속한 결과입니다.

csclient Application 빌드 시에 -Dspring.profiles.active=dev 옵션을 가지고 실행이 되었기 때문에 Config Repository에서 csclient-dev.yml 파일을 읽어와 Build

가 되었겠지요. 지금은 2 url의 결과가 보시다시피 같습니다.

이제 csclient-dev.yml 파일을 변경해 보도록 하겠습니다.

second 부분에 기존 Spring에서 Config Server로 변경하고 Git Repository에 Push를 해주었습니다.

Push 해주었으니 값이 바뀌겠지?라고 생각하실 수도 있는데요. 그러면 정말 좋은데 아쉽게도 아닙니다. 한 가지를 더 해줘야 하는데요.

이렇게 /refresh url로 Body는 빈 값으로 POST method를 이용해 호출을 한번 해줘야 합니다.

결과를 보면

/config/static은 그대로인데

@RefreshScope을 사용했던 /config/dynamic은 변경된 것을 보실 수 있습니다. 정말 쉽다고 생각하지 않으신가요?

제 개인적인 생각인데 아마 Spring Cloud Config서버를 개발했던 사람들은 충분히 Git Repository의 변경점을 감지해서 수동으로 빈 값 /refresh POST를 호출해 주지 않아도 충분히 자동으로 갱신을 시켜줄 수 있었겠지만. 아마 실수로 Push 하는 경우도 있어 (큰 프로젝트일수록 여러명이 작업을 할 테니) 사고를 방지하고자 이런 방식으로 한 것이 아닌가 생각합니다.

(각 서버별로 POST를 호출해 줄 때 그냥 for문에 넣고 동기로 호출해주면 갱신 대상 서버가 많을 경우 시간이 오래 걸립니다. SpringCamp 2017에서 toby님의 발표를 들으셨으면 아마 해결 방법을 알고 계실 것이라고 믿습니다. ^^)
이상으로 Spring Cloud Config 서버에 관해서 알아보았습니다.
정말 완전히 기본적인 사용법만 알아보았는데요 다양한 사용법과 활용법이 있으니 Spring Cloud Config Reference를 참고하시고 혹은 Google링을 해서 활용방법을 찾아보시는 것도 좋습니다. 아니면 저에게 문의를 주셔도 됩니다.
감사합니다.

[MicroService Architecture] SpringCloud를 이용한 상품 MSA적용을 시도한 경험을 공유합니다.

저는 커머스플랫폼을 개발하는 조직에 소속되어 있습니다. 여기서 말은 못하지만 다들 잘 아시는 커머스 서비스의 백단 플랫폼을 만드는 조직 이었는데요. 이때 기존 커머스 플랫폼은 구축한지 오래된 Monolithic Architecture구조로 되어있었습니다.

이것을 MicroService Architecture로 전환 하기위한 시도의 일환으로 제가 사내에서 발표한 내용을 당시 개발셀장님에게 보안적으로 이슈가 있는 부분을 가리고 블로그에 블로깅해도 된다는 허락을 받아 블로깅을 하게 되었습니다.

  • 왜? Micro Service Architecture (이하 MSA) 를 생각하게 되었나?  
    • 커머스 플랫폼 프로젝트는 Groovy, Grails를 이용하여 Monolithic architecture 로 build된 프로젝트 입니다. 모든 도메인의 로직이 한 프로젝트안에 들어있죠… 뭐… 작은 규모의 프로젝트라면 큰 문제는 없습니다.
    • screen-shot-2016-12-05-at-10-48-48-pm
    • 하지만 이런 커머스 플랫폼 정도의 규모라면 Monolithic architecture의 구조문제및 복합적인 문제가 발생하기 시작합니다.
    • screen-shot-2016-12-03-at-11-07-02-am
      • 개발자가 Local 에서 프로젝트 빌드시에 덩치가 크기 때문에 빌드 시간이 엄청나게 오래걸림 (Grails 라서 오래걸리는 건지… 덩치가 커서 그런건지… 맥북프로 레티나 최고급형인데도… 가끔 분단위로도….)
      • 한프로젝트에서 작은 서브 프로젝트들이 코어 소스를 공유하고 있고 각 프로젝트별로 소스참조가 있기 때문에 작은 실수로 전체 프로젝트 빌드 실패나 한곳에서 낸 장애가 서비스 전체로 번지기도 함
      • 도메인 혹은 로직들의 Coupling이 강하기 때문에 성능문제나 장애시 원인 파악 혹은 수정하기가 힘들다… (옵션에서 수정하면 기본 상품 정보에서 장애가 터지기도 하고…)
      • 도메인의 CRUD 혹은 비즈니스로직이 이곳저곳 분산되어 있기 때문에 새로운 팀원이 와서 파악을 하거나 기존에 있던 팀원도 장애 포인트를 찾는데 시간이 걸리기도 합니다. (힘들어요..)
      • 로직 관련 이야기를 더 하면 view 영역에 GSP 혹은 Javascript로직에도 중요한 로직이 있으며 Javascript 또한 같은 도메인의 로직임에도 분산되어 있어서 따라가기가 힘들다.
      • 간단한 기능 혹은 bugfix 에도 불구하고 전체 배포를 해야한다. (배포 시간도 오래 걸리기도 하고… GSP 같은거 수정해도 다시 다 배포…)
      • Groovy 나 Grails 문제… 일단 문제 생겨서 구글링 해도 바로 해결점을 찾기란 좀 힘들고 시간걸리고 스트레스 받음 그리고 개발자 커리어 관리에 안좋음… (자네 카카오에서 뭐했나? 저는 Grails를 이용하여 어쩌구저쩌구….??? Grails가 뭔가요??)
  • 그럼 개선방법은?
    • screen-shot-2016-12-05-at-10-51-31-pm
    • 제가 속한 상품개발유닛장이자 제가 존경하는 개발자이며 Java 17년차 외길인생 이신 일명 “개발의신” 이셨으나 최근 Groovy를 만나곤 Java를 버렸다는 최모 개발자의 의견을 일단 들어보았습니다.
      • 최모 개발자 “Grails 와 Groovy를 유지한 상태로 일단은 기존 로직 정리 및 리펙토링 하자” 라고 하셨는데… (정확히는 리펙토링 하고 정리후 새로운 language 나 framework 을 적용하도록 하자)
      • 그래서 고민해 보니 일단 Grails는 하기가 싫었습니다. (정확하게 하기가 싫다기 보다는 이걸 파서 익숙해질 시간에 Spring 이나 더 실무에서 했음 했죠…)
      • 기존 로직 정리 및 리펙토링 하려고 보니… 아.. 헬이네요.. 할일이 태산… 그냥 정리와 리펙토링을 새 그릇에서 했음 좋겠다.. 라고 생각 했죠.
      • 그리고 화제가 되는 MSA로 다시 상품 도메인을 구성을 해보자 라고 생각 했죠. (MSA가 왜 상품쪽에 적합한지는 아래에서 설명 하도록 하겠습니다.…)
  • 그래서 일단은 기술스택을 정했습니다.
    • 기본 language와 framework은 대중적이며 개발자 커리어 관리에도 좋도록 : Java 1.8, Spring boot 1.4, MariaDB
    • 여기에 요즘 인기있는 (김영한님이 쓴책 다들 한권씩 가지고 계시죠?? 자바 ORM 표준 JPA 프로그래밍)  screen-shot-2016-12-03-at-11-26-50-am
    • 그리고 캐시나 빠른 Response가 필요할때는 Redis (국내에서 Redis를 가장 잘 다루기로 소문난 강대명님이 사내에 계시기 때문에 Redis 관련 문의는 바로바로 할수 있다는 장점)screen-shot-2016-12-03-at-11-29-26-am
    • 여기서 해당 기술스택을 이용해 MSA를 적용하기 위해서 Spring Cloud 를 이용하기로 했습니다.
      • Spring Cloud란 Spring 의 프로젝트 이름 입니다. (http://projects.spring.io/spring-cloud/)screen-shot-2016-12-03-at-11-31-11-am
      • 쉽게 생각하면 Spring boot 에 좋은 library 를 더해서 개발자들이 MSA 서비스를 쉽게 만들수 있도록 도와주겠다 라는건데요. 여기서 좋은 library는 대부분 Netflix 개발자들이 만들어서 Open Source 한것이 대부분이라고 합니다. Netflix에서 지금 쓰고 있구요.
  • 본격적으로 들어가기 전에 Spring Cloud에 관해서 더 이야기하기 전에 정리를 하고 넘어갈것이 있습니다. 
    • Monolithic architecture 는 다 아시죠?? 그냥 정통적인 web service 개발 architecture 라고 생각하면 됩니다. 한 모듈안에 모든 기능이 다 들어있죠.screen-shot-2016-12-03-at-11-32-49-am
    • 장점은 빠른 개발속도와 Test가 쉽고 ScaleUp구조에 유리합니다.
    • MSA 도 다 아시겠지만. 한번더 학습하고 넘어간다 생각하며 귀차니즘의 압박이 오더라도 한번더 보고 넘어 가도록 하죠. 장점을 정리해 볼께요..screen-shot-2016-12-03-at-11-37-22-am
      • 각 중요한 도메인의 기능을 작게 나누에 독립적으로 실행이 가능하도록 하자. (자율적이며 Transaction 단위로 분리하는게 좋음)
      • 따라서 한가지 일을 자~알 하는데 초점을 맞추자.
      • 기능추가나 bugfix 배포,복구 등이 자유로우며 한 모듈의 장애가 다른 모듈로 전파되지 않는다.
      • 코드의 양이 줄어들어 누구나 쉽게 파악이 가능해서 바로 현업 투입이 가능 하도록 하자.
      • 모듈 하나하나가 규모가 작기 때문에 신기술 적용이나 요즘 좋은 배포 시스템 연동이 매우 쉽다.screen-shot-2016-12-03-at-11-40-13-am
      • Rest API와 Json을 이용한 모듈간의 자유로은 communication이 가능함
      • Rest API(Json)는 Platform 이나 language 에 독립적이다.
        • 학습 하기가 매우 쉽다. (사람이 보기 쉽다. 개발적인 지식이 없는 기획자나 디자이너 들도 분석이 쉽게 가능하다.)
        • 요청의 결과가 Json으로 나오기 때문에 캐시를 적용하기가 매우 쉽다.
      • 각 모듈별로 개발자가 틀릴경우에 장점이 나오는데
        • A와 B모듈을 개발하는 개발자가 다른경우에도 서로간의 통신 Format 만 정해 놓으면 (Json 의 key 데이타 등) 서로간에 모듈이 완성이 될때까지 기다릴 필요가 없다. Mock 으로 Json 을 만들어 놓으면 되니깐. (그러니 눈치보며 서로 쪼는 행동을 할 필요도 없으니 좋다.)screen-shot-2016-12-03-at-11-41-48-am
    • 그럼 MSA는 구세주 인가요?screen-shot-2016-12-03-at-11-42-40-am
      • 당연히 그럴리가 없죠 … 아무튼 … 단점도 존재 합니다.
      • 너무 작은 서비스에서 MSA를 적용하면 오히려 낭비 (인력자원, 귀차니즘, 돈 등등….)
      • 또 이게 기능을 너무 많이 쪼개면 오히려 복잡도 증가 (상품 정보 하나 조회 하는데 Rest 호출 20번에 만약 리스트 조회 라면 x 100번 …)
      • 그리고 보통은 MSA 쪼갤때 각각의 모듈은 독립적인 DB 를 사용하도록 되어있습니다. screen-shot-2016-12-03-at-11-45-36-am이런식으로 말이죠… 하지만 이 때문에 Transaction 으로 묶기가 좀 힘들어요… (그래서 일단은 상품쪽은 같은 물리적인 DB 에 Database 를 논리적으로 나누어 갈까해요.)
      • 그리고 각 모듈마다 설정이나 기동방식이 다르다면 좀 귀찮죠…
      • 하나하나 git clone / pull 하는것도 일이고..
      • 그리고 초기 개발시에는 MSA 로 원활히 개발을 위한 인프라준비 등으로 개발 시간이 오래 걸리는것도 단점 입니다.
  • 그러면 다시 도메인 이야기로 돌아와서.. 왜 상품도메인이 MSA 적용하기가 좋은건데? 
    • 상품 도메인은 우리의 상품의 등록 승인등의 프로세스로 인하여 각 서브 도메인별로 의존도가 낮습니다.screen-shot-2016-12-03-at-11-50-34-am
    • 간단하게 설명을 하자면 이 커머스 플랫폼의 경우 오픈마켓이 아니기 때문에 상품등록시 승인 구조를 가지고 있습니다. screen-shot-2016-12-05-at-10-53-50-pm
    • 엠디가 상품 승인과 노출의 모든 권한을 가지고 있습니다. 따라서
    • 만약 모듈중에 하나가 문제가 생겨서 상품등록시에 배송비 정보를 빼고 등록이 되었다면.. 승인 거부를 하면 됩니다. screen-shot-2016-12-05-at-10-58-19-pm-copy
    • 이렇게 옵션 뿐만 아니라 이미지, 고시정보, 배송정보 등 각각 분리가 쉬운 구조로 되어있습니다. (일부 서브도메인의경우 UX가 완전 독립이 되어있죠)
    • 그래서 상품의 SpringCloud를 이용한 MSA 적용 Architecture 는 다음과 같습니다. (V. 0.5)screen-shot-2016-12-05-at-11-03-31-pm-copy
    • 보시다 보면 다른것은 다 하실텐데 Eureka는 뭐여?? 라고 생각하실껍니다. 이제 그 이야기를 하죠.
  • 이제 Spring Cloud에 관해서 더 이야기를 해보도록 하죠.
    • 위에서 잠깐 언급 했지만 Spring Cloud는 Spring boot + library(잡다한) 입니다.
    • 이야기를 하기전에 Twelve factors 방법론을 살펴보고 넘어가는 것이 좋습니다. (요약하면)
      • 설정 자동화를 통하여 최근 등장한 클라우드 플랫폼에 배포를 쉽게 한다.
      • 개발환경과 운영환경의 차이를 최소화하며 지속적인 (수시로) 배포가 가능하도록 한다.
      • 개발 환경이나 방식을 크게 바꾸지 않고도 ScaleUp, Out을 가능하게 한다.
    • 이중에 우리가 가장 많이 사용해야 할것 요약하면..
      • 버전이 관리되는 하나의 코드베이스를 사용하여 분리된 환경설정으로 배포가 가능하게 한다. (더이상 소스를 알파 베타 리얼 이딴거 없도록 하자)screen-shot-2016-12-03-at-12-02-22-pm
    • 어플리케이션을 stateless 하도록 프로세스를 실행 하게 만들도록 하자
      • MSA에서 사용하는 Http프로토콜은 자체적으로 상태를 저장하거나 유지할수 없다. application 딴에서 상태 정보를 유지관리해야 한다. 그래서 Http 그 자체는 stateless하다고 할수 있다.
      • 그러니깐 sticky session 이나 로그인 세션등 이런거 쓰지말자는 거다.
      • 각각의 요청은 독립적인것이라고 생각하며 이전에 했던 요청에 의존적이지 않다는 것이다. 따라서 각각의 요청은 모든 서버가 처리가 가능하도록 모든 정보를 제공해야 한다.
      • 단… 클라이언트가 요청을 할때 상태 정보를 전달해야하니 네트워크 리소스를 그만큼 소모해야 하고 서버는 해당 요청을 받아 처리를 더 해야하는 리소스는 필요하다.screen-shot-2016-12-03-at-12-03-21-pm
      • 당장 와닿는것만 정리해 보았고 나머지 Twelve factors 방법론은 알아서 찾아보세요.screen-shot-2016-12-03-at-12-04-27-pm
    • Spring Cloud 에서 가장 중요한 모듈은 Eureka 라고 할수 있습니다. Eureka는 MSA를 구성함에 있어서 각 모듈의 Lookup, Scale상태, 모듈의 Health 상태등을 관리하는 모듈 입니다.screen-shot-2016-12-03-at-12-06-43-pm
      • Client 에서 account서비스에 요청이 들어오면 Eureka는 account 서비스가 어디에 있는지 요청이 가능한 상태인지 체크를 해서 해당 서버의 ip와 포트의 정보를 돌려주게되고 클라이언트를 해당 정보를 요청하게 됩니다.
      • 코드를 간단하게 보면 screen-shot-2016-12-03-at-12-07-48-pm소스코드로 보면 이렇게 됩니다. app_name 에 같은 모듈이 서로 다른 포트로 100개 달려있건 1개가 달려있건 상관없죠. (Client side Loadbalancing 라고 부릅니다)
      • Eureka는 DashBoard를 지원하는데요. 여기에서 각 모듈의 상태를 확인할수 있죠.screen-shot-2016-12-03-at-12-09-04-pm
    • 그리도 또 중요한것이 설정의 분리 입니다.
      • 설정이라하면 Db 접속정보 URL 정보 기타등등 많은 설정 정보가 있죠? 이것들은 보통 프로젝트에 들어있습니다. (YML이나 Properties파일로 따로 저장하는 설정 파일들) screen-shot-2016-12-03-at-12-12-28-pm
      • 이런 설정 파일들을 더이상 프로젝트 내부가 아닌 외부 GIT에 올려 놓습니다.screen-shot-2016-12-03-at-12-13-11-pm
      • 위의 이미지를 보시면 YML 파일을 app의아이디-환경명.yml 파일로 만들어 두고 GIT Repository에 올리곤 spring config 서버에 연결만 해 두면…screen-shot-2016-12-03-at-12-15-00-pm
      • GIT Repository에 YML 파일로 올려둔 설정을 가져다 쓰는 법은 간단하게 가져다 쓸수 있습니다.screen-shot-2016-12-03-at-12-15-30-pm
      • 그리고 가장 좋은것은 클래스 상단에 이렇게.. @RefreshScope 만 붙여주면 restart 없이 다이나믹하게 설정값을 변경할수 있어요. screen-shot-2016-12-03-at-12-17-07-pm
    • Rest API 호출을 하는것은 불편하고 번거롭다? (네 좀 귀찮을때가 있죠)
      • Spring Cloud 에서는 Rest API 호출한다는 개념이 좀 달라요 즉 일반적으로는 Http Rest 통신의 경우 외부에 있는 자원을 Http 통신을 이용해서 내가 가져다 쓴다. 이제는 Rest 자원도 local 자원처럼 쓸수 있어요.
      • 즉 Eureka로 묶여있는 서비스 내에서는 로칼 레포지토리 호출하듯 interface 를 만들어서 쓰면 됩니다. screen-shot-2016-12-03-at-12-19-23-pm
      • API 주소는 설정파일에 올려놓고 Spring config를 이용해서 쉽게 사용할수 있어요. (언제든 바꿀수 있고 서버 리빌드가 필요 없어요.)screen-shot-2016-12-03-at-12-20-04-pm
      • screen-shot-2016-12-03-at-12-20-36-pm
    • 마지막으로 Hystrix Circuit Breakers 도 쉽게 사용할수 있어요.screen-shot-2016-12-03-at-12-21-24-pm
      • 쉽게 소스 코드로 설명하면 특정 메서드의 실행 조건을 정해서 그 조건에 걸리면 다른 메서드를 실행 하도록 할수있죠. (응답 속도가 느리다거나.. exception 이 난다거나..)screen-shot-2016-12-03-at-12-22-11-pm

이상으로 상품 도메인의 MSA 프로젝트를 위해 제가 사내에서 발표 했던 자료중에 보안에 문제가 있는 부분을 뺴고 정리를 해보았습니다.

결과적으로 프로젝트는 그럼 어떻게 되었냐 하면 제가 퇴사를 하는 바람에 진행을 하지는 못했어요 ㅜㅜ

하지만 프로젝트를 하기 위해 공부를 하고 Architecture를 그리고 데모를 위해 프로젝트를 만들고 상품 도메인의 여러 서브 도메인중 몇가지는 MSA로 직접 개발해 보기도 했으니 개인적으로 성과는 충분 하다고 생각 했습니다.

나중에 기회가 되면 더큰 MSA프로젝트를 해보고 싶네요. ^^

이미지 출처

screen-shot-2016-12-03-at-12-22-47-pm

[GoLang] GoLang 에서 네이밍룰 과 접근 제한자

내가 요즘 관심있어 하는 언어중 하나는 GoLang이다. 간단한 프로젝트도 해봤는데… (http://www.ptcompare.com)

네이밍룰과 접근 제한자가 특이하다.

Java에서는 보통 private, public, protected, default 등이 있지만

GoLang에서는 function에 따로 접근 제한자를 붙이는게 없고

function의 명이 앞글자가 대문자 이면 public ( [ex] func ImPublic () { … } ) 이고

앞글자가 소문자 이면 private 이다.

이 원칙은 변수나, struct등 모든것에 적용된다.

 

[Spring Cloud] 클라이언트 사이드 로드 밸런싱 (Client side Loadbalancing) 이란 ??

최근들어 나는 Spring Cloud에 관심을 가지고 해당 플랫폼을 이용하여 스터디겸 파일럿 프로젝트를 진행중이다.
Spring Cloud를 접하면서 여러가지의 용어들을 접했지만 그중에 가장 흥미로운 용어는 바로 “Client Side LoadBalancing”이었다.
이 낯설지도 않고 익숙하지도 않은 용어는 나를 흥미롭게 했고 해당 용어를 좀더 살펴보았다.

일단 검색엔진을 통해서 검색을 해보았지만… 나 혼자만 몰라서 그런것인지 한글로 검색을 하면 나오지를 않았다.

반면 영어로 하면 괜찮은 자료들이 많이 나왔는데 검색을 해보다 보니 이 개념은 이미 2007년 말부터 나왔던 개념이라는 것을 알게 되었다.

용어란것이 다 그렇지만 이미 우리가 특정 어플리케이션 개발을 할때 이미 쓰이고 있던 방법을 용어화 한것 이었다.
(특히 요즘 유행하는 Micro service archtecture도 예전부터 있던 개념이었다.)
하지만 “에이~ 그런것 이었어? 이미 쓰고 있었구만 뭐” 라며 그냥 넘길 일이 아니다…

왜냐하면 본인도 개발자로서 이직을 몇번 해보았고 개발자가 면접을 볼때는 꼭 용어로 물어 보더라는 것이다…..
본인은 용어에 약하기 때문에 면접관에게 “아 제가 용어에 약한데 한글로 어떤것을 물어보는 것인지 조금 설명이 가능할까요? 라고 한적이 있다.”

그리고 보통은 개발자들은 같이 일할때 용어들을 많이 쓰기 때문에 알아두어야 할 필요가 있다….

사견이 길었는데… 다시 돌아와서.

클라이언트 사이드 로드 밸런싱 (Client Side LoadBalancing) 이란 웹어플리케이션의 부하를 분산하는 방식중에 하나이다.

보통 웹어플리케이션 환경에서 엄청나게 많은 트래픽을 처리하는 방법이 무었일까요? 라고 질문을 할때 대부분의 개발자들은 서버를 여러대를 두어 트래픽을 분산하여 처리를 합니다 라고 대답을 할것이다.

이때 사용하는 것이 Switch로 매우 비싼장비이다. (웹 어플리케이션에서 사용하는 Switch는 보통 L4, L7 스위치를 사용한다.)

Screen Shot 2016-09-17 at 1.36.57 AM
(이게 L4 Switch이다 좀 있어 보인다.)

요즘은 Switch가 설치 및 셋팅이 어렵고 비싸기 때문에 Open Source Software Loadbalancer인 HA proxy를 많이 사용중이다. (듀얼 코어 서버 정도만 되도 초당 2만 세션 정도 처리가 가능하다는 측정 자료도 있다.)

이런 Switch나 HA proxy를 사용하여 부하를 분산하는 방식이 “Server Side LoadBalancing”이다. 보통 이 방식을 우리가 말하는 부하 분산이라고 한다.

Screen Shot 2016-09-17 at 1.37.03 AM
(개념도를 한번 그려보았다. 이런방식으로 서버에 요청하기 전에 Switch가 받아서 각 서버로 분산처리 하도록 한다.)

이 방식에는 문제점이 있다.

1. Switch자체가 처리할수 있는 요청수에 한계가 있다.
2. Switch를 증설하거나 셋팅 설정 하는 어려움이 있으며 그리고 비싸다. (HA proxy도 어쨋든 서버가 필요하다.)
3. Switch가 접속이 문제가 생길것을 대비해서 Switch를 이중화 하기도 한다. 하지만  1,2번 문제와 함께 보통은 Active – Standby로 동작을 하도록 셋팅을 하는데 이떄 문제가 생길동안 이 비싼 장치는 전혀 사용이 되지 않는 문제가 있다.

이런 문제점을 해결하고자 나온 개념이 “Client Side LoadBalancing”이다.

먼저 개념도를 보자

Screen Shot 2016-09-17 at 1.37.14 AM
(simple is best)

“Server Side LoadBalancing”과 차이점은 중간에 Switch를 거치지 않고 바로 서버로 요청이 가는것이다.
Switch에서 하던 분산 역할을 이제는 Client software에서 처리를 하자는 것이다.

이 방식은 클라이언트의 코드에 추가를 하는것이기 때문에 하드웨어 Switch를 추가하는것보다 쉬우며 빠른 문제해결이 가능하다.

“Client Side Loadbalancer”를 구현하기 위해서는 다음을 구현해야 한다.
1. 클라이언트는 랜덤 혹은 특정한 알고리즘으로 (RR같은) 클러스터로 묶여있는 서버중에 랜덤으로 접속을 하여 부하를 분산시킬수 있어야 한다.
2. 서버 접속에 있어 오류사항이 발생했을때 적절하게 대응이 가능해야 한다. 즉 클라이언트는 서버가 특정 시간동안 응답 하지 않는다면 다른 서버를 선택하여 접속해야 한다.
3. 클러스터에 있는 서버들은 최종 사용자에게 (해당 접속 라이브러리를 사용하는 개발자) 한개의 서버로 보여야 한다. (예를들어 http://test-server/ 라는 Alias 를 이용햇 접속을 하며 test-server 는 클러스터의 이름이고 이 test-server 클러스터에는 192.168.0.3, 192.168.0.4, 192.168.0.5 라는 서버가 연결되어있도록 한다.)

위의 1,2,3을 모두 직접 구현할 필요는 없고 이런 기능을 해주는 Library들이 많이 나와있다. 이중에서 Java에서 사용하는 Ribbon 을 이용한 “Client Side Loadbalancer”를 살펴 보도록 하자.

예제코드 는 Java, Spring boot (Cloud) 에 Ribbon을 사용했다고 가정한다.
코드의 목적은 단순하게 서버 컨테이너 3개 (127.0.0.1:8081, 127.0.0.1:8082, 127.0.0.1:8083)를 실행시킨뒤에 클라이언트 컨테이너 (127.0.0.1:8080)에서 해당 서버로 요청하는 코드이다 하지만 다른점은 서버로 Http 요청하는 코드가 다르다는 것이다.

예를들어
Http://127.0.0.1:8081~8083/req-server 로 요청하는것이 아닌 Http://cslb-server/req-server 로 요청하는것이다.
(cslb-server 는 127.0.0.1:8081, 127.0.0.1:8082, 127.0.0.1:8083 서버의 host 정보를 묶은 클러스터 이름이다.)

서버쪽은 코드를 볼것도 없고. (그냥 요청 들어오면 해당 서버의 포트와 요청 시간을 찍어주는 코드)
127.0.0.1:8081, 127.0.0.1:8082, 127.0.0.1:8083 이렇게 3개의 컨테이너를 실행시켜 준다.

Screen Shot 2016-09-17 at 1.37.23 AM

클라이언트쪽 코드를 보면

클러스터로 묶는 서버 설정쪽 (Ribbon)

– application.yml
Screen Shot 2016-09-17 at 1.37.33 AM

– CslbClientApplication.java
Screen Shot 2016-09-17 at 1.37.42 AM

해당 프로젝트를 실행 시킨뒤에 Loadbalancing이 잘 이루어졌나 확인해 보면

Screen Shot 2016-09-17 at 1.37.52 AM

요청 시간을 보면 Loadbalancing이 잘 이루어진것을 볼수가 있다.

Jmeter를 이용해서 failover를 확인해 보도록 하자

Screen Shot 2016-09-17 at 1.38.04 AM

요청을 시작하면

Screen Shot 2016-09-17 at 1.38.27 AM

그중에 8083 컨테이너를 중지하면

Screen Shot 2016-09-17 at 1.38.37 AM

Jmeter상에서 요청 통계를 살펴보면

Screen Shot 2016-09-17 at 1.38.49 AM

error 확률이 0.01% (물론 요청수가 많아서 그렇지만) 빼고는 정상적으로 처리된것을 볼수있다.
(error는 중지 시키는 순간 해당 세션에 물려있는 요청이 있기때문에 어쩔수가 없다.)

failover시점은 설정으로 health check 시간 간격을 더 줄여서 최소한으로 줄일수는 있긴하다.

Screen Shot 2016-09-17 at 1.38.57 AM

소스와 예제를 보니 “Client Side Loadbalancing”에 관해서 이해를 했으리라 믿는다.

참고로 Spring Cloud의 Eureka를 사용하면 Ribbon을 따로 설정해 주지 않아도 “Client Side Loadbalancing”을 사용할수 있으니 참고 바란다. (그건 시간나면 다음 포스팅에)

해당 demo의 소스는 : https://github.com/LeeKyoungIl/Cslb 여기에서 확인해 볼수 있으니 참고 바란다.