반응형

[macbook 프로메테우스 설치]

손쉬운 방법이 있어서 퍼옴


출처 : https://brewinstall.org/Install-prometheus-on-Mac-with-Brew/

 

  1. ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
  2. brew install prometheus

반응형

'DevOps' 카테고리의 다른 글

미니큐브 설치 맥  (0) 2018.12.23
fluentd mac설치  (0) 2018.12.18
MSA모니터링-Prometheus개념  (0) 2018.11.29
MSA-Spring Cloud  (0) 2018.09.28
Hystrix-Isolation  (0) 2018.09.28
반응형

계속 뭔가 해야 할일들이 바뀌네요.이게뭔지...


모니터링 툴 찾다가 Prometheus 잘 정리된 사이트가 있어 퍼옵니다.

자료 수집후에...한번 자체 정리 한번 해야겠다...




출처 : https://blog.outsider.ne.kr/1254



Prometheus

모니터링이나 APM 도구를 많이 써보고 잘 아는 것은 아니지만 Prometheus는 다른 모니터링 도구와는 좀 다른 특성을 가진 도구이다.

Prometheus 사이트

From metrics to insight라는 문구가 인상적이다. 주변의 추천도 있고 최근에 관심을 가지고 보고 있어서 그런지 요즘 주목을 많이 받는 것으로 보인다.

Prometheus는 오픈소스이고 Apache 2 라이센스로 공개되어 있어서 유료 걱정 없이 사용할 수 있다.

Prometheus 아키텍처

Prometheus의 구조는 Prometheus 저장소에 나와 있는 아키텍처를 보면 좀 이해하기가 쉽다. 처음에는 꽤 복잡해 보였지만 동작 방식을 이해하면 이 그림을 이해할 수 있게 된다. 다른 모니터링 도구와 가장 다른 점은 대부분의 모니터링 도구가 Push 방식 즉, 각 서버에 클라이언트를 설치하고 이 클라이언트가 메트릭 데이터를 수집해서 서버로 보내면 서버가 모니터링 상태를 보여주는 방식인데 반해서 Prometheus는 Pull 방식이다. 그래서 서버가 각 클라이언트를 알고 있어야 하는게 아니라 서버에 클라이언트가 떠 있으면 서버가 주기적으로 클라이언트에 접속해서 데이터를 가져오는 방식이다.

몇 가지 특징을 짚고 넘어갈 수 있는데 구성을 크게 나누면 Exporter, Prometheus Server, Grafana, Alertmanager로 나눌 수 있다. 뒤에서 실제로 사용해 보면 더 이해가 가겠지만, 사전에 알아두어야 할 특징을 정리해 보자.

  • Exporter는 모니터링 대상의 Metric 데이터를 수집하고 Prometheus가 접속했을 때 정보를 알려주는 역할을 한다. 호스트 서버의 CPU, Memory 등을 수집하는 node-exporter도 있고 nginx의 데이터를 수집하는 nginx-exporter도 있다. Exporter를 실행하면 데이터를 수집하는 동시에 HTTP 엔드포인트를 열어서(기본은 9100 포트) Prometheus 서버가 데이터를 수집해 갈 수 있도록 한다. 이 말은 웹 브라우저 등에서 해당 HTTP 엔드포인트에 접속하면 Metric의 내용을 볼 수 있다는 의미이다.

    • Exporter를 쓰기 어려운 배치잡 등은 Push gateway를 사용하면 된다. 내가 보기에는 Push gateway도 결국 Exporter의 일종이라고 생각된다.
    • 웹 애플리케이션 서버 같은 경우의 Metric은 클라이언트 라이브러리를 이용해서 Metric을 만든 후 커스텀 Exporter를 사용할 수 있다.
  • Prometheus Server는 Expoter가 열어놓은 HTTP 엔드포인트에 접속해서 Metric을 수집한다. 그래서 Pull 방식이라고 부른다.
  • Prometheus Server에 Grafana를 연동해서 대시보드 등의 시각화를 한다.
  • 알림을 받을 규칙을 만들어서 Alert Manager로 보내면 Alert Manager가 규칙에 따라 알림을 보낸다.

내가 그랬듯이 이렇게 특징을 봐도 하나도 이해가 안 갈 텐데 실제로 사용을 해보면 훨씬 쉽게 이해가 되므로 하나씩 살펴보자.

Prometheus Server

Prometheus 서버는 다운로드 페이지에서 OS 별로 다운로드 받을 수 있고 설치 문서를 보면 Docker 이미지로도 제공하고 있다. 편한 방식을 사용하면 되는데 여기서는 설명을 위해서 직접 다운로드를 받아서 사용해 보자. 현재 버전은 1.4.1이다.

다운받아서 압축을 풀면 prometheus 파일과 예시 설정 파일인 prometheus.yml파일이 존재하는데 이 파일을 이용해서 다음과 같이 실행하면 된다.

Bash
$ ./prometheus -config.file=prometheus.yml
INFO[0000] Starting prometheus (version=1.4.1, branch=master, revision=2a89e8733f240d3cd57a6520b52c36ac4744ce12)  source=main.go:77
INFO[0000] Build context (go=go1.7.3, user=root@e685d23d8809, date=20161128-09:59:22)  source=main.go:78
INFO[0000] Loading configuration file prometheus.yml     source=main.go:250
INFO[0000] Loading series map and head chunks...         source=storage.go:354
INFO[0000] 0 series loaded.                              source=storage.go:359
INFO[0000] Starting target manager...                    source=targetmanager.go:63
INFO[0000] Listening on :9090                            source=web.go:248

9090포트로 Prometheus 서버가 실행된 것을 볼 수 있다.

Prometheus 웹 콘솔

서버를 띄운 서버가 52.192.141.64라고 한다면 http://52.192.141.64:9090으로 접속하면 http://52.192.141.64:9090/graph로 리다이렉트가 되는데 위와 같은 화면을 볼 수 있다. 여기서 간단하게 수집된 데이터를 볼 수 있다. 맨 위의 입력 칸은 데이터 조회를 위한 평가식을 입력하는 곳인데 평가식은 뒤에서 좀 더 자세히 설명하고 여기서는 셀렉트박스에서 원하는 값을 선택하고 Execute를 누르면 된다.

Prometheus 웹에서 보이는 Metric 종류

셀렉트박스를 열면 수집하고 있는 매트릭의 목록을 볼 수 있다. 원하는 매트릭을 선택하고 Execute를 누르면 하단에서 그래프를 볼 수 있다.

Prometheus 웹에서 특정 매트릭을 선택해서 그래프를 볼 수 있다

이 화면을 간단한 대시보드처럼 사용할 수도 있다.

prometheus.yml

이제 Prometheus 서버를 띄울 때 사용한 설정 파일인 prometheus.yml를 살펴보자.

# 전역 설정
global:
  scrape_interval:     15s # 15초마다 매트릭을 수집한다. 기본은 1분.
  evaluation_interval: 15s # 15초마다 규칙을 평가한다. 기본은 1분.

  # 외부 시스템에 표시할 이 서버의 레이블
  external_labels:
      monitor: 'codelab-monitor'

# 규칙을 로딩하고 'evaluation_interval' 설정에 따라 정기적으로 평가한다.
rule_files:
  # - "first.rules"
  # - "second.rules"

# 매트릭을 수집할 엔드포인드로 여기선 Prometheus 서버 자신을 가리킨다.
scrape_configs:
  # 이 설정에서 수집한 타임시리즈에 `job=<job_name>`으로 잡의 이름을 설정한다.
  - job_name: 'prometheus'

    # metrics_path의 기본 경로는 '/metrics'이고 scheme의 기본값은 `http`다

    static_configs:
      - targets: ['localhost:9090']

기본으로 제공된 파일에서 주석으로 된 설명만 추가로 달아놨다.

  • global부분은 매트릭 수집을 위한 전역설정 값이다. 프로메테우스 서버를 프로메테우스 서버에 연결하거나 Alert Manager 등에서 이 서버의 데이터를 가져갈 수 있는데 그때 구분할 수 있도록 external_labes로 이름을 지정할 수 있다.
  • rule_files는 Alert 규칙 등 필요한 규칙을 작성해서 사용할 수 있는데 현재는 아무런 규칙을 지정하지 않았다.
  • scrape_configs가 실제 수집 대상을 지정하는 설정이다. 여기서는 prometheus라는 이름으로 한 개만 설정했다. 이는 여러 대상을 설정할 수 있다는 것이고 targets가 배열로 지정하므로 서버군을 모두 여기 지정할 수 있다.
  • targets를 localhost:9090으로 설정했는데 이는 Prometheus 서버 자신을 가리킨다. targets에 지정하는 것은 Prometheus 서버가 접근해서 데이터를 가져올 Exporter의 HTTP 엔드포인트이다. 그래서 localhost:9090으로 지정을 하면 기본값인 metrics_path를 이용해서 localhost:9090/metrics에 접근해서 데이터를 가져온다. 이는 Prometheus 서버가 매트릭을 수집하는 서버인 동시에 매트릭을 노출하는 Exporter이기도 하다는 의미이다.
  • 이 예시에서는 scrapte_configs를 수동으로 지정했는데 실제 사용한다면 이런 식으로 모니터링할 서버를 수동으로 관리할 수는 없다. CONFIGURATION 문서를 보면 모니터링 대상을 Consul, DNS 기반의 서비스 디스커버리를 이용해서 동적으로 설정할 수 있다.

웹 브라우저에서 http://52.192.141.64:9090/metrics에 접근하면 Exporter가 노출한 매트릭 데이터를 볼 수 있다.

Prometheus Exporter가 노출한 매트릭 정보

Prometheus는 클러스터링 기능이 없다. 한대의 Prometheus 서버로 수천 대의 서버를 다 모니터링할 수는 없는데 내가 아는 선에서는 현재 Prometheus 서버를 Prometheus 서버에 연결해서 사용하는 방식으로 확장하는 것으로 알고 있다.(아직 구축해 본 것은 아니라서...) 즉, 100대를 수집하는 Prometheus 서버를 두고 이 서버가 수집한 데이터를 다시 수집하는 Prometheus 서버를 또 뒤에 둔다는 것이다.

Exporter

간단히 Prometheus 서버를 살펴봤으므로 이제 매트릭을 제공하는 Exporter를 살펴봐야 한다. 위에도 얘기했듯이 Exporter는 필요한 매트릭을 수집하고 이를 HTTP 엔트포인트로 Prometheus에 노출하는 역할을 한다. 문서를 보면 Consul exporter, MySQL server exporter, RabbitMQ exporter, HAProxy exporter, Docker Hub exporter, AWS CloudWatch exporter 등 다양한 Exporter를 지원하고 있다. 필요한 곳에 Exporter를 설정해서 Prometheus가 이를 수집하도록 하면 된다.

Exporter를 다 살펴볼 수는 없으므로 서버의 CPU, 메모리, 디스크 등의 상태를 모니터링하는 Node exporter를 사용해 보자. Node exporter는 Docker로도 사용할 수 있으므로로 여기서는 간단히 Docker로 띄워보자.

Bash
$ docker pull prom/node-exporter
Using default tag: latest
latest: Pulling from prom/node-exporter
8ddc19f16526: Pull complete
a3ed95caeb02: Pull complete
8279f336cdd3: Pull complete
81998f54d5a6: Pull complete
Digest: sha256:5655be43374133b9ad72b8761b5661762acf513126c0f147ce1902ac52230f52
Status: Downloaded newer image for prom/node-exporter:latest

$ docker run -d -p 9100:9100 --net="host" prom/node-exporter
cf70bc7297369012ce15683352cf1246c89d1410f44d80758fd69ef58123c838

이 서버의 IP가 52.69.72.174라고 한다면 9100포트로 Node exporter를 띄웠으므로 http://52.69.72.174:9100/metrics에 접속을 하면 다음과 같이 Exporter가 수집한 매트릭 정보를 볼 수 있다.

Node exporter가 노출한 매트릭 정보

이제 모니터링할 서버가 생겼으므로 앞에서 본 prometheus.yml에서 이 서버를 모니터링하도록 추가해 보자.


scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'test-server' scrape_interval: 10s static_configs: - targets: ['52.69.72.174:9100']

기존에 있던 prometheus 잡외에 test-server라는 이름으로 새로운 잡을 추가하고 위에서 설정한 Node-exporter를 바라보도록 했다.

Node exporter에서 수집한 매트릭을 Prometheus 웹에서 본 화면

이제 서버를 다시 띄우고 모니터링을 하면 앞에서 본 Prometheus 서버 페이지인 http://52.192.141.64:9090/graph에서 test-server에 대한 매트릭정보를 볼 수 있다.

표현식 언어

Prometheus에서는 수집한 매트릭 데이터를 조회할 수 있는 함수형 표현식 언어를 제공하고 있다. 이 표현식 언어로 매트릭 데이터를 실시간으로 조회할 수 있고 위에서 셀렉트 박스로 특정 매트릭을 선택해서 데이터나 그래프를 본 것도 이 표현식 언어를 사용한 것이다.

  • 앞에서 조회해 본 process_cpu_seconds_totalnode_cpuhttp_requests_total 등을 인스턴스 벡터라고 부른다. 이 인스턴스 벡터로 쿼리를 할 수 있다.
  • 인스턴스 벡터 뒤에 { }로 레이블을 지정하면 필터링을 할 수 있는데 http_requests_total{job="prometheus",group="canary"}와 같이 사용한다. 이렇게 하면 http_requests_total에서 job 이름이 prometheus이고 group은 canary인 정보만 조회한다.

    • 레이블에서는 =!==~!~같은 논리연산자를 사용할 수 있고 여기서 ~는 정규표현식 비교를 의미한다. 그래서 http_requests_total{environment=~"staging|testing|development",method!="GET"}와 같이 사용할 수 있다.
  • 인스턴스 벡터 뒤에 레인지 벡터라고 부르는 []를 사용할 수 있다. http_requests_total{job="prometheus"}[5m]라고 하면 최근 5분의 값을 조회한다.
  • node_cpu offset 10m처럼 특정 시간의 값을 조회하는 오프셋 모디파이어를 사용할 수 있다.

그 외에 연산자나 함수를 이용해서 조회할 수 있다.

반응형

'DevOps' 카테고리의 다른 글

fluentd mac설치  (0) 2018.12.18
macbook에 프로메테우스 설치  (0) 2018.11.30
MSA-Spring Cloud  (0) 2018.09.28
Hystrix-Isolation  (0) 2018.09.28
API Gateway: Zuul  (0) 2018.09.03
반응형

apache poi(XSSFWorkbook API)가 갖고 있는 메모리 이슈로 인해 full gc가 발생하는 현상 해결을 위해

엑셀파일 read시에 메모리 사용증대가 발생되지 않는 개선된 api가 필요


구글링 결과 어떤 분이 감사한 모듈을 개발해 놓으셨다.

해결책은 StreamingReader 를 사용하는 것


해결을 위해서는 아래 내용을 참고하면 된다.

해당 블로그에 잘 설명되어 있음

https://github.com/monitorjbl/excel-streaming-reader


회사에서 해당 모듈을 사용하여 고질적 문제를 해결함.


개요는 아래와 같음

  <dependency>
    <groupId>com.monitorjbl</groupId>
    <artifactId>xlsx-streamer</artifactId>
    <version>2.0.0</version>
  </dependency>

Implementation Details

This library will take a provided InputStream and output it to the file system. The stream is piped safely through a configurable-sized buffer to prevent large usage of memory. Once the file is created, it is then streamed into memory from the file system.

The reason for needing the stream being outputted in this manner has to do with how ZIP files work. Because the XLSX file format is basically a ZIP file, it's not possible to find all of the entries without reading the entire InputStream.

This is a problem that can't really be gotten around for POI, as it needs a complete list of ZIP entries. The default implementation of reading from an InputStream in POI is to read the entire stream directly into memory. This library works by reading out the stream into a temporary file. As part of the auto-close action, the temporary file is deleted.

If you need more control over how the file is created/disposed of, there is an option to initialize the library with a java.io.File. This file will not be written to or removed:

File f = new File("/path/to/workbook.xlsx");
Workbook workbook = StreamingReader.builder()
        .rowCacheSize(100)    
        .bufferSize(4096)     
        .open(f);

This library will ONLY work with XLSX files. The older XLS format is not capable of being streamed.




반응형

'각종Tip모음' 카테고리의 다른 글

apache poi 메모리 이슈해결  (0) 2019.01.26
이클립스(eclipse)가 시동 시 응답없음  (0) 2013.12.24
@SuppressWarnings  (0) 2013.11.28
반응형

늦게나마 MSA를 학습중이다.

spring cloud를 주로 보고 있는데 양이 방대하구먼...^^;;

영어때문에 괴로워 하다가 한글로 개략적 사항들을 정리해 놓은 깃이 있어서 퍼왔다.

https://coe.gitbook.io/guide/


반응형

'DevOps' 카테고리의 다른 글

fluentd mac설치  (0) 2018.12.18
macbook에 프로메테우스 설치  (0) 2018.11.30
MSA모니터링-Prometheus개념  (0) 2018.11.29
Hystrix-Isolation  (0) 2018.09.28
API Gateway: Zuul  (0) 2018.09.03
반응형

출처 : http://woowabros.github.io/experience/2017/08/21/hystrix-tunning.html


API G/W에서  Clustering 관련사항을 확인하기 위해 hystrix에 접근하고 있다. hystrix에 대해서 잘 정리된 글이 있어 퍼왔고, 
궁금했던 isolation 을 발췌한다.

Isolation 이해

이번에는 application.yml 파일을 살펴봤습니다. 저희가 Hystrix를 어떻게 설정하고 있나 궁금했기 때문입니다.

zuul.ribbonIsolationStrategy: SEMAPHORE

어라, 또 한 번 반갑지 않으신가요? 이것도 위에서 살펴봤던 코드와 관련 있고요. 그림 3의 5번 부분이기도 합니다. 아무래도 격리 전략Isolation Strategy을 살펴봐야겠다고 느꼈습니다. 이 때, Hystrix, How it Works를 주로 참고했고요. 한 문장으로 정리하면 다음과 같습니다.

“일종의 Bulkhead 패턴이며, 각 서비스에 대한 의존성을 격리하고 동시 접근을 제한한다.”

hystrix_isolation

그림 4. Hystrix Isolation

위 그림은 Hystrix가 어떻게 의존 서비스들을 격리하는지 보여줍니다. 빨간색으로 표기된 네모 상자는 특정 서비스의 호출이 지연되고 있다는 뜻인데요. 나머지 서비스에는 영향을 주지 않고 있습니다. 또한, 동시 접근 제한을 서비스별로 다르게 설정하고 있습니다.

격리의 방법으로는 2가지가 존재합니다.

  1. Thread & Thread Pools
  2. Semaphore (Counter라고 표현하기도 하는데, 개인적으로는 이 용어가 더 와닿습니다)

Thread 방식에서는 서비스 호출이 별도의 스레드에서 수행됩니다. 예컨대, Tomcat의 스레드 풀과 서비스에 대한 호출 스레드가 격리되는 거죠. 이렇게 하면 네트워크상의 타임아웃 위에 스레드 타임아웃을 둘 수 있습니다. 하지만 별도의 스레드를 사용하는 만큼 비용이 수반됩니다. 여기서는 이를 가리켜 연산 오버헤드computational overhead라고 표현하네요.

한편, Semaphore 방식에서는 서비스 호출을 위해 별도의 스레드를 만들지 않습니다. 단지 각 서비스에 대한 동시 호출 수를 제한할 뿐입니다. 위에서 살펴봤던 코드 덕분에, 내용을 좀 더 쉽게 이해할 수 있었습니다. 아래 그림은 Thread와 Semaphore의 차이를 보여줍니다.

hystrix_isolation

그림 5. Thread와 Semaphore의 격리 방식 (크게 보기)

Isolation 권장사항

위에서 살펴봤던 코드 중에 executionSemaphore.tryAcquire() 부분이 있습니다. 여기 코드를 좀 더 살펴보면, HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS를 만나게 되는데요. 이는 execution.isolation.semaphore.maxConcurrentRequests 프로퍼티에 대응하는 상수입니다. 처음에는 이 수치를 좀 더 늘리려고 했습니다. 왜냐면 기존에 저희가 SEMAPHORE를 격리 전략으로 사용하고 있었고요. 여기서 Semaphore 구간에 진입할 수 있는 요청의 수만 늘려주면, API Gateway의 동시 처리량이 늘어날 테고, 그러면 502 발생 가능성이 감소할 거로 생각했기 때문입니다. 프로퍼티에 대한 설명은 여기를 참고하세요. (폴백fallback을 설정한 경우도 가용한 Semaphore 개수의 제한을 받습니다.)

하지만 Hystirx Configuration 문서를 좀 더 살펴보면, 아래와 같은 내용이 있습니다.

The default, and the recommended setting, is to run HystrixCommands using thread isolation (중략)

Commands executed in threads have an extra layer of protection against latencies beyond what network timeouts can offer.

Generally the only time you should use semaphore isolation for HystrixCommands is when the call is so high volume (hundreds per second, per instance) that the overhead of separate threads is too high; this typically only applies to non-network calls.

HystrixCommand를 사용할 때는 Thread 격리를 권장한다는 내용입니다. (서비스 호출의) 지연으로부터 보호되는 별도의 계층을 가질 수 있으니까요. 그리고 Semaphore를 써야 하는 경우는 유일하다고 합니다. 꽤 강력한 어조네요. 의구심이 생길 만큼요. 바로 “호출량이 너무 많아서 분리된 스레드의 사용이 주는 오버헤드가 큰 경우”이고요. 네트워크 요청이 발생하지 않는 경우non-network call가 보통 여기에 해당한다고 합니다. 좀 더 찾아보니, Netflix API도 아주 일부만 Semaphore를 사용하는 군요. 인메모리 캐시에서 메타 데이터를 가져오거나, Thread 방식에 대한 퍼사드facade에 한해서요.

반응형

'DevOps' 카테고리의 다른 글

fluentd mac설치  (0) 2018.12.18
macbook에 프로메테우스 설치  (0) 2018.11.30
MSA모니터링-Prometheus개념  (0) 2018.11.29
MSA-Spring Cloud  (0) 2018.09.28
API Gateway: Zuul  (0) 2018.09.03
반응형

출처 : https://github.com/KD4/TIL/blob/master/MSA/zuul.md


API Gateway: Zuul

API GATEWAY이란?

Micro Service Architecture(이하 MSA)에서 언급되는 컴포넌트 중 하나이며, 모든 클라이언트 요청에 대한 end point를 통합하는 서버이다. 마치 프록시 서버처럼 동작한다. 그리고 인증 및 권한, 모니터링, logging 등 추가적인 기능이 있다. 모든 비지니스 로직이 하나의 서버에 존재하는 Monolithic Architecture와 달리 MSA는 도메인별 데이터를 저장하고 도메인별로 하나 이상의 서버가 따로 존재한다. 한 서비스에 한개 이상의 서버가 존재하기 때문에 이 서비스를 사용하는 클라이언트 입장에서는 다수의 end point가 생기게 되며, end point를 변경이 일어났을때, 관리하기가 힘들다. 그래서 MSA 환경에서 서비스에 대한 도메인인 하나로 통합할 수 있는 API GATEWAY가 필요한 것이다.

Zuul은 Netflix에서 만든 API Gateway 오픈소스로 Java 프로젝트이다. 다른 오픈소스로는 KONG, API Umbrella, tyk.io 등이 있다.

Zuul

Zuul은 넷플릭스에서 만든 API Gateway로 그루비로 작성되었으며 filter 개념을 통해서 API Gateway에 추가 기능을 넣고 관리할 수 있다.

Zuul은 Filter를 통해서 다음과 같은 기능을 구현하고있다.

  • Authentication and Security : 클라이언트 요청시, 각 리소스에 대한 인증 요구 사항을 식별하고 이를 만족시키지 않는 요청은 거부
  • Insights and Monitoring : 의미있는 데이터 및 통계 제공
  • Dynamic Routing : 필요에 따라 요청을 다른 클러스터로 동적으로 라우팅
  • Stress Testing : 성능 측정을 위해 점차적으로 클러스터 트래픽을 증가
  • Load Shedding : 각 유형의 요청에 대해 용량을 할당하고, 초과하는 요청은 제한
  • Static Response handling : 클러스터에서 오는 응답을 대신하여 API GATEWAY에서 응답 처리

Zuul Core Architecture.png출처 - https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee

위는 Zuul 아키텍처를 나타내는 그림이다.

  • Filter File Manager에서는 일정 주기(정해진 시간) 마다 정해진 directory에서 groovy로 정의된 filter 파일을 가져온다.
  • ZuulServlet은 HttpServlet을 재정의 하였다.
  • Request 요청이 들어 올때마다 아래와 같이 preRoute(), route(), postRoute()에서 ZuulFilter Runner를 실행한다.
  • 각 단계에서 Filter들은 비동기로 실행되며 통신을 위해서 RequestContext를 공유한다.
  • ZuulFilter Runner는 Filter에 정의된 기능을 실행한다.
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    try {
        init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

        // Marks this request as having passed through the "Zuul engine", as opposed to servlets
        // explicitly bound in web.xml, for which requests will not have the same data attached
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();

        try {
            preRoute();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            route();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            postRoute();
        } catch (ZuulException e) {
            error(e);
            return;
        }

    } catch (Throwable e) {
        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
    } finally {
        RequestContext.getCurrentContext().unset();
    }
}

Zuul filter

Zuul은 필터를 통해서 API Gateway에 필요한 기능들을 추가하거나 관리할 수 있다.

각 리퀘스트는 네 가지 라이프사이클 흐름으로 처리되며 아래와 같이 네 가지 필터를 탄다.

  • Pre Filter - 라우팅 전에 실행되는 필터, 인증과 로깅 기능은 주로 여기에 들어간다.
  • Routing Filter - 요청에 대한 라우팅을 다루는 필터, Apache http client를 사용하여 정해진 Url로 보낼수 있고, Neflix Ribbon을 사용하여 동적으로 라우팅 할 수도 있다.
  • Post Filter - 라우팅 후에 실행되는 필터, response에 HTTP header를 추가하거나, response에 대한 응답속도, Status Code, 등 응답에 대한 statistics and metrics을 수집한다.
  • Error Filter - 에러 발생시 실행되는 필터, 필터 실행 후 Post Filter가 실행된다.

Request Lifecycle 출처 - https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee

Filter Interface

필터를 정의하려면 ZuulFilter를 상속받아서 아래 네 개의 메소드를 구현해야한다.

String filterType();

int filterOrder();

boolean shouldFilter();

Object run();

To Run, or Maybe Not

shouldFilter는 해당 필터가 실행될 것인가에 대한 로직을 담는다. true를 리턴한다면 필터가 실행된다.

Ordering

filterOrder는 같은 Type에서 실행되는 필터들과의 우선순위를 나타내는 int값을 리턴한다.

Filter Types

이 메소드는 필터 타입에 대한 String 값을 리턴한다. Zuul에서 제공하는 기본 필터타입은 pre, routing, post, error이다. 이 밖에 필터 타입은 GroovyProcessor.runFilters를 직접 구현해서 추가할 수 있다.

Filter Coordination

run() 메소드는 이 필터가 어떤 작업을 하는지 정의한다.

다음은 ZuulFilter를 상속받아서 내부 IP에 대한 접근인지를 확인하는 필터 예제이다.

class InternalCheckFilter : ZuulFilter() {

    private val internalIpRegex = Regex("(0:|127\\.|10\\.|172\\.|192\\.).*")


    override fun filterType() = 'pre'

    override fun filterOrder() = -1

    override fun shouldFilter(): Boolean {
        val uri = RequestContext.getCurrentContext().request.requestURI ?: ""
        return (uri.contains("/internal") || uri.contains("/universal"))
    }

    override fun run(): Any? {
        val ctx = RequestContext.getCurrentContext()
        val request = ctx.request
        val ip = request.remoteAddr

        if (!isInternalIp(ip)) {
            ctx.setSendZuulResponse(false)
            ctx.responseStatusCode = HttpStatus.FORBIDDEN.value()
        }

        return null
    }

    fun isInternalIp(ip: String?): Boolean {
        return ip?.matches(internalIpRegex) ?: false
    }

}


반응형

'DevOps' 카테고리의 다른 글

fluentd mac설치  (0) 2018.12.18
macbook에 프로메테우스 설치  (0) 2018.11.30
MSA모니터링-Prometheus개념  (0) 2018.11.29
MSA-Spring Cloud  (0) 2018.09.28
Hystrix-Isolation  (0) 2018.09.28
반응형

MVVM아키텍쳐 패턴 소개 사이트

좋은 사이트가 있어 정보 기록차 링크합니다

https://justhackem.wordpress.com/2017/03/05/mvvm-architectural-pattern/


반응형
반응형

좋은 사이트 링크합니다

자바스크립트 관련 요약본? 실수모음?잘된 사이트

http://bonsaiden.github.io/JavaScript-Garden/ko/#intro

반응형
반응형

펌: http://daddycat.blogspot.kr/2011/05/chapter-3-wsdlweb-service-description.html

 

 

Chapter 3 WSDL(Web Service Description Language)


SOAP은 원격 프로시저 호출을 할 수 있는 구조만 제공할 뿐이고, 실제로 호출하는 원격 프로시저명과 필요한 인자, 그리고 반환형이 무엇인지 모른다면 원격 프로시저를 호출하는 내용을 작성 할 수가 없다. 또한 SOAP 메시지를 전송하는 전송 프로토콜의 종류 및 웹 서비스 시스템의 URL(endpoint) 정보도 알고 있어야 한다. 그래서 웹 서비스 제공자는 웹 서비스의 기능을 명세화하여, UDDI 레지스트리에 공개를 해야 한다. WSDL로 XML 문서를 작성하여 웹 서비스에 대한 내용을 명세화 한다. WSDL 문서 사용의 장점은 다음과 같다.
· 서비스 공용 저장소(UDDI)를 이용할 경우 실제 Web Service의 위치와 상관 없이 자신이 원하는 서비스를 찾아서 이용할 수 있다.
· WSDL은 누구나 참조가 가능하기 때문에 이를 통해 쉽게 Web Service 사용에 필요한 정보를 얻을 수 있다.
1. WSDL 문서의 구조

· types는 교환된 메시지를 설명하는 데 사용된 데이터 형식 정의를 제공합니다.
· message는 전송되는 데이터의 추상 정의를 나타냅니다. 메시지는 논리적인 부분으로 이루어져 있으며 각 부분은 특정 형식 시스템 내의 정의와 연관되어 있습니다.
· portType은 추상 작업의 집합이며 각 작업은 입력 메시지 및 출력 메시지를 참조합니다.
· binding은 특정 portType에 의해 정의된 메시지 및 작업에 대해 구체적인 프로토콜과 데이터 형식을 지정합니다.
· port는 바인딩에 대해 주소를 지정하기 때문에 단일 통신 종점을 정의합니다.
· service는 관련된 포트의 집합을 집계하는 데 사용됩니다.
<definition>
WSDL 문서의 루트 엘리먼트이다. 일반적으로 WDSL 문서 내에서 사용되는 대부분의 네임스페이스들을 선언한다. tarrgetNamespace는 현재 WSDL 문서에서 작성될 내용에 대한 네임스페이스 이름을 기술해 준다. WSDL 문서에서 정의되는 원격 프로시저는 이 네임스페이스 이름을 갖는다. 그리고 웹 서비스 클라이언트는 원격 프로시저를 호출할 때 반드시 이 네임스페이스 이름을 이용해서 QName 형태로 원격 프로시저명을 기술해야 한다.
<definitions name="StockQuote"
targetNamespace="http://example.com/stockquote.wsdl"
xmlns:tns="http://example.com/stockquote.wsdl"
xmlns:xsd1="http://example.com/stockquote.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
</definitions>


WSDL 문서에서 관례적으로 많이 사용되는 네임스페이스 선언
접두사
이름 공간 URI
정의
wsdl
http://schemas.xmlsoap.org/wsdl/ WSDL 프레임워크에 대한 WSDL 이름 공간.
soap
http://schemas.xmlsoap.org/wsdl/soap/ WSDL SOAP 바인딩에 대한 WSDL 이름 공간.
http
http://schemas.xmlsoap.org/wsdl/http/ HTTP GET & POST 바인딩에 대한 WSDL 이름 공간.
mime
http://schemas.xmlsoap.org/wsdl/mime/ MIME 바인딩에 대한 WSDL 이름 공간.
soapenc
http://schemas.xmlsoap.org/soap/encoding/ SOAP 1.1에 의해 정의된 인스턴스 이름 공간.
soapenv
http://schemas.xmlsoap.org/soap/envelope/ SOAP 1.1에 의해 정의된 인스턴스 이름 공간.
xsi
http://www.w3.org/1999/XMLSchema-instance XSD에 의해 정의된 인스턴스 이름 공간.
xsd
http://www.w3.org/1999/XMLSchema XSD에 의해 정의된 스키마 이름 공간.
tns
(다양함) “this namespace”를 의미하는 (tns) 접두사는 현재 문서를 참조하는 규칙으로 사용됩니다.
(기타)
(다양함) 다른 모든 이름 공간 접두사는 단지 예제일 뿐입니다. 특히, “http://example.com”으로 시작하는 URI는 어느 정도 응용 프로그램 종속적이거나 컨텍스트 종속적인 URI[4]를 나타냅니다.


<message>
원격 프로시저의 인자에 대한 정보와 리턴값에 대한 정보를 기술하는 엘리먼트이다. 인자에 대한 정보로는 인자의 수 및 인자의 데이터 타입을 말하고, 리턴값에 대한 정보는 리턴값에 대한 데이터 타입을 말한다. 원격 프로시저의 인자 하나는 하나의 message 엘리먼트로 작성된다.
String addBook1(String title, int price);
이 메서드를 message로 표현
<message name="addBookRequest">
<part name="title" type="xsd:string"/>
<part name="price" type="xsd:int"/>
</message>
<message name="addBook1Response">
<part name="result" type="xsd:string"/>
</message>


<type>
원격 프로시저의 인자 및 리턴값의 데이터 타입으로 사용될 복합 타입을 기술하기 위한 엘리먼트이다. 만약 인자 및 리턴값의 데이터 타입에 복합 타입을 사용하지 않는다면 WSDL 문서에서 이 엘리먼트는 생략될 것이다.
일반적인 분산 컴퓨팅 응용프로그램에서 인자형 및 리턴형이 객체 타입이라면 전송되기 전에 직렬화를 거쳐 이진 포맷으로 변환해야 한다. 그리고 받는 쪽에서는 역직열화르 ㄹ거쳐 객체로 다시 생성하는 작업을 해야 한다. 웹 서비스의 경우에도 웹 서비스 클라이언트 스텁 객체를 통해서 인자를 요청 SOAP 메시지로 마샬링하고, 웹 서비스 시스템은 타이 객체를 통해 SOAP 메시지를 언마샬링하여 원래의 인자 데이터형으로 복원한다.
WDSL 문서의<message> 엘리먼트에 기술되는 인자 및 리턴값의 데이터 타입이 XML Schema의 빌트인 심플타입으로 표현될 수있다면 XML Schema 타입을 직접 사용하지만 배열 및 구조체와 같은 XML Schema에 없는 데이터 타입이 인자 및 리턴값의 데이터 타입으로 사용된다면, 반드시 <types> 엘리먼트에 배열 및 구조체에 해당하는 복합 타입을 정의 해야 한다.
<types>
<schema
targetNamespace="복합 타입 네임스페이스명"
xmlns:tns=”복합 타입 네임스페이스명”
xmlns=”http://www.w3.org/2001/XMLSchema”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:wsdl=”http://schemas.xmlsoap.org/wdsl/”
xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/">
<complexType name="복합 타입명">

</complexType>
</schema>
</types>


배열정의
<complexType name="배열의 이름">
<complexContent>
<restriction base=”soap-enc:Array”>
<attribute ref=”soap-enc:arrayType” wsdl:arrayType=”데이터 타입[]”/>
</restriction>
</complexContent>
</complexType>


구조체 정의
<complexType name="클래스명">
<sequence>
<element name=”멤버변수(Field)명” type=”데이터 타입”/>
<element name=”멤버변수(Field)명” type=”데이터 타입”/>

</sequence>
</complexType>


<portType>, <operation>
Web Service의 interface를 정의하는 부분 interface를 정의 한다는 의미는 <message>에서 정의한 메시지를 이용하여 어떻게 Web Service의 Method를 호출할 지에 대해 기술한다는 것을 의미 한다. <portType>은 하나 이상의 <operation>을 포함한다.
Java 메서드
public String say(String name) {
return name+"님 안녕하세요 Apache WSDL 입니다.";
}


WSDL문서 정의
<message name="say0Request">
<part name="name" type="xsd:string"/>
</message>
<message name="say0Response">
<part name="return" type="xsd:string"/>
</message>
<portType name="sayerPortType">
<operation name="say">
<input name="say0Request" message="tns:say0Request"/>
<output name="say0Response" message="tns:say0Response"/>
</operation>
</portType>


<binding>
앞에서 정의된 각 요소들을 실제 메시지 전달에 사용되는 프로토콜과 binding 하는 부분이다.
특정 프로토콜로 웹 서비스 시스템에 접속하는 것을 바인딩이라고 한다. 바인딩하는 방법을 모른다면 웹 서비스 클라이언트는 원격 프로시저에 대한 호출 방법을 비록 안다고 하더라도 호출 행위는 할 수 없다. 웹 서비스 클라이언트가 사용해야할 전송용 프로토콜에 대해서는 WDSL 문서상의 이 엘리먼트에 기술해주면 된다. 이 엘리먼트는 웹 서비스 시스템과 웹 서비스 소비자인 클라이언트 응용프로그램 간의 통신 방법을 기술하는 엘리먼트이다.
바인딩의 종류
· SOAP 바인딩 : 웹 서비스 시스템의 원격 프로시저를 호출할 때 SOAP를 통해서 호출 요청하는 방식을 말한다.
· HTTP 바인딩 : HTTP는 주로 SOAP메시지의 전송 프로토콜로 많이 사용되지만, 원격 프로시저 호출용 프로토콜로 직접 사용될 수 있다. HTTP만 해석하고, 전송할 수 있는 클라이언트 응용프로그램에서 웹 서비스의 원격 프로시저를 호출할 때 유용하다. 이러한 클라이언트 종류로 는 웹 브라우저, 휴대폰, PDA등이 있다.
· MIME 바인딩 : MIME는 SOAP과 HTTP에서 함께 사용하여 문자 데이터와 바이너리 데이터를 함께 전송할 때 사용할 수 있는 방식이다.
가장 흔히 사용되는 SOAP 바인딩
<binding name="sayerBinding" type="tns:sayerPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="say">
<soap:operation soapAction="" style="rpc"/>
<input name="say0Request">
<soap:body use="encoded" namespace="ApacheWSDL" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output name="say0Response">
<soap:body use="encoded" namespace="ApacheWSDL" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>


<service>, <port>
수신자와 웹 서비스 시스템의 URL(종점 : endpoint)정보를 기술한다.
<service name="ApacheWSDL">
<port name="sayerPort" binding="tns:sayerBinding">
<soap:address location="http://localhost:8080/soap/servlet/rpcrouter"/>
</port>
</service>


자동 생성된WSDL
일반적으로 WSDL은 개발 IDE툴이나, JAX-RPC에서 제공하는 wsdeploy.bat, wscompile.bat을 이용하여 자동 생성한다. 다음은 oracle Jdeveloper을 이용한 자동생성 문서의 예이다.
<?xml version = '1.0' encoding = 'UTF-8'?>
<!--Generated by the Oracle9i JDeveloper Web Services WSDL Generator-->
<!--Date Created: Thu Jan 15 16:26:34 KST 2004-->
<definitions
name="TypeMappingWS"
targetNamespace="http://model/UserInfo.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://model/UserInfo.wsdl"
xmlns:ns1="http://model/ITypeMappingWS.xsd">
<types>
<schema
targetNamespace="http://model/ITypeMappingWS.xsd"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<complexType name="model_user" jdev:packageName="model" xmlns:jdev="http://xmlns.oracle.com/jdeveloper/webservices">
<all>
<element name="name" type="string"/>
<element name="age" type="int"/>
</all>
</complexType>
</schema>
</types>
<message name="getUser0Request">
<part name="name" type="xsd:string"/>
<part name="age" type="xsd:int"/>
</message>
<message name="getUser0Response">
<part name="return" type="ns1:model_user"/>
</message>
<portType name="UserInfoPortType">
<operation name="getUser">
<input name="getUser0Request" message="tns:getUser0Request"/>
<output name="getUser0Response" message="tns:getUser0Response"/>
</operation>
</portType>
<binding name="UserInfoBinding" type="tns:UserInfoPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getUser">
<soap:operation soapAction="" style="rpc"/>
<input name="getUser0Request">
<soap:body use="encoded" namespace="TypeMappingWS" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output name="getUser0Response">
<soap:body use="encoded" namespace="TypeMappingWS" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="TypeMappingWS">
<port name="UserInfoPort" binding="tns:UserInfoBinding">
<soap:address location="http://localhost:8080/soap/servlet/rpcrouter"/>
</port>
</service>
</definitions>

 

반응형

'WebService' 카테고리의 다른 글

WS security  (0) 2014.01.29
반응형

gc에 대해서 가장 개요및 명확한 설명을 한 글이어서 퍼온다.

작성자분이 펴낸 책도 읽기를 개인적으로 권고하고 싶다.

출처 : http://d2.naver.com/helloworld/1329


Java Garbage Collection

지극히 개인적이고 주관적인 판단 기준을 먼저 밝힌다면, 가비지 컬렉션(Garbage Collection, 이하 GC)에 대해 잘 알고 있을수록 실력이 좋은 Java 개발자라고 생각합니다. GC 과정에 관심을 가질 정도라면 규모가 일정 이상인 애플리케이션을 제작해 본 경험이 있을 것입니다. 또, 어떤 GC 알고리즘을 선택할 것인지 고민할 정도면 스스로 제작한 애플리케이션의 특징을 정확히 이해하고 있다고 볼 수 있습니다. 이러한 판단 기준이 보편적이지는 않지만, GC에 대한 이해는 훌륭한 Java 개발자가 되기 위한 필수 조건이라는 데에는 별다른 이견이 없을 것입니다. 이 글에서는 GC 이론을 되도록 쉽게 소개하겠습니다. 피가 되고 살이 되는 글이 되기를 바랍니다.

가비지 컬렉션 과정 - Generational Garbage Collection

GC에 대해서 알아보기 전에 알아야 할 용어가 있다. 바로 'stop-the-world'이다. stop-the-world란, GC을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것이다. stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 나머지 쓰레드는 모두 작업을 멈춘다. GC 작업을 완료한 이후에야 중단했던 작업을 다시 시작한다. 어떤 GC 알고리즘을 사용하더라도 stop-the-world는 발생한다. 대개의 경우 GC 튜닝이란 이 stop-the-world 시간을 줄이는 것이다.

Java는 프로그램 코드에서 메모리를 명시적으로 지정하여 해제하지 않는다. 가끔 명시적으로 해제하려고 해당 객체를 null로 지정하거나 System.gc() 메서드를 호출하는 개발자가 있다. null로 지정하는 것은 큰 문제가 안 되지만, System.gc() 메서드를 호출하는 것은 시스템의 성능에 매우 큰 영향을 끼치므로 System.gc() 메서드는 절대로 사용하면 안 된다(다행히도 NHN에서 System.gc() 메서드를 호출하는 개발자를 보진 못했다).

Java에서는 개발자가 프로그램 코드로 메모리를 명시적으로 해제하지 않기 때문에 가비지 컬렉터(Garbage Collector)가 더 이상 필요 없는 (쓰레기) 객체를 찾아 지우는 작업을 한다. 이 가비지 컬렉터는 두 가지 가설 하에 만들어졌다(사실 가설이라기보다는 가정 또는 전제 조건이라 표현하는 것이 맞다).

  • 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
  • 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.

이러한 가설을 'weak generational hypothesis'라 한다. 이 가설의 장점을 최대한 살리기 위해서 HotSpot VM에서는 크게 2개로 물리적 공간을 나누었다. 둘로 나눈 공간이 Young 영역과 Old 영역이다.

  • Young 영역(Yong Generation 영역): 새롭게 생성한 객체의 대부분이 여기에 위치한다. 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다. 이 영역에서 객체가 사라질때 Minor GC가 발생한다고 말한다.
  • Old 영역(Old Generation 영역): 접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC(혹은 Full GC)가 발생한다고 말한다.

영역별 데이터 흐름을 그림으로 살펴보면 다음과 같다.

JavaGarbage1

그림 1 GC 영역 및 데이터 흐름도

위 그림의 Permanent Generation 영역(이하 Perm 영역)은 Method Area라고도 한다. 객체나 억류(intern)된 문자열 정보를 저장하는 곳이며, Old 영역에서 살아남은 객체가 영원히 남아 있는 곳은 절대 아니다. 이 영역에서 GC가 발생할 수도 있는데, 여기서 GC가 발생해도 Major GC의 횟수에 포함된다.

그렇다면 "Old 영역에 있는 객체가 Young 영역의 객체를 참조하는 경우가 있을 때에는 어떻게 처리될까?"라고 궁금해 하는 분도 더러 있을 것이다. 이러한 경우를 처리하기 위해서 Old 영역에는 512바이트의 덩어리(chunk)로 되어 있는 카드 테이블(card table)이 존재한다.

카드 테이블에는 Old 영역에 있는 객체가 Young 영역의 객체를 참조할 때마다 정보가 표시된다. Young 영역의 GC를 실행할 때에는 Old 영역에 있는 모든 객체의 참조를 확인하지 않고, 이 카드 테이블만 뒤져서 GC 대상인지 식별한다.

JavaGarbage2

그림 2 카드 테이블 구조

카드 테이블은 write barrier를 사용하여 관리한다. write barrier는 Minor GC를 빠르게 할 수 있도록 하는 장치이다. write barrirer때문에 약간의 오버헤드는 발생하지만 전반적인 GC 시간은 줄어들게 된다.

Young 영역의 구성

GC를 이해하기 위해서 객체가 제일 먼저 생성되는 Young 영역부터 알아보자. Young 영역은 3개의 영역으로 나뉜다.

  • Eden 영역
  • Survivor 영역(2개)

Survivor 영역이 2개이기 때문에 총 3개의 영역으로 나뉘는 것이다. 각 영역의 처리 절차를 순서에 따라서 기술하면 다음과 같다.

  • 새로 생성한 대부분의 객체는 Eden 영역에 위치한다.
  • Eden 영역에서 GC가 한 번 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
  • Eden 영역에서 GC가 발생하면 이미 살아남은 객체가 존재하는 Survivor 영역으로 객체가 계속 쌓인다.
  • 하나의 Survivor 영역이 가득 차게 되면 그 중에서 살아남은 객체를 다른 Survivor 영역으로 이동한다. 그리고 가득 찬 Survivor 영역은 아무 데이터도 없는 상태로 된다.
  • 이 과정을 반복하다가 계속해서 살아남아 있는 객체는 Old 영역으로 이동하게 된다.

이 절차를 확인해 보면 알겠지만 Survivor 영역 중 하나는 반드시 비어 있는 상태로 남아 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 두 영역 모두 사용량이 0이라면 여러분의 시스템은 정상적인 상황이 아니라고 생각하면 된다.

이렇게 Minor GC를 통해서 Old 영역까지 데이터가 쌓인 것을 간단히 나타내면 다음과 같다.

JavaGarbage3

그림 3 GC 전과 후의 비교

참고로, HotSpot VM에서는 보다 빠른 메모리 할당을 위해서 두 가지 기술을 사용한다. 하나는 bump-the-pointer라는 기술이며, 다른 하나는 TLABs(Thread-Local Allocation Buffers)라는 기술이다.

bump-the-pointer는 Eden 영역에 할당된 마지막 객체를 추적한다. 마지막 객체는 Eden 영역의 맨 위(top)에 있다. 그리고 그 다음에 생성되는 객체가 있으면, 해당 객체의 크기가 Eden 영역에 넣기 적당한지만 확인한다. 만약 해당 객체의 크기가 적당하다고 판정되면 Eden 영역에 넣게 되고, 새로 생성된 객체가 맨 위에 있게 된다. 따라서, 새로운 객체를 생성할 때 마지막에 추가된 객체만 점검하면 되므로 매우 빠르게 메모리 할당이 이루어진다.

그러나 멀티 스레드 환경을 고려하면 이야기가 달라진다. Thread-Safe하기 위해서 만약 여러 스레드에서 사용하는 객체를 Eden 영역에 저장하려면 락(lock)이 발생할 수 밖에 없고, lock-contention 때문에 성능은 매우 떨어지게 될 것이다. HotSpot VM에서 이를 해결한 것이 TLABs이다.

각각의 스레드가 각각의 몫에 해당하는 Eden 영역의 작은 덩어리를 가질 수 있도록 하는 것이다. 각 쓰레드에는 자기가 갖고 있는 TLAB에만 접근할 수 있기 때문에, bump-the-pointer라는 기술을 사용하더라도 아무런 락이 없이 메모리 할당이 가능하다.

간단하게 Young 영역에 대한 GC에 대해서 알아보았다. 위에서 이야기한 두 가지 기술(bump-the-pointer, TLABs)을 반드시 기억하고 있을 필요는 없다. 몰라도 쇠고랑 안차고 경찰 출동 안한다. 그러나 Eden 영역에 최초로 객체가 만들어지고, Survivor 영역을 통해서 Old 영역으로 오래 살아남은 객체가 이동한다는 사실은 꼭 기억하기 바란다.

Old 영역에 대한 GC

Old 영역은 기본적으로 데이터가 가득 차면 GC를 실행한다. GC 방식에 따라서 처리 절차가 달라지므로, 어떤 GC 방식이 있는지 살펴보면 이해가 쉬울 것이다. GC 방식은 JDK 7을 기준으로 5가지 방식이 있다.

  • Serial GC
  • Parallel GC
  • Parallel Old GC(Parallel Compacting GC)
  • Concurrent Mark & Sweep GC(이하 CMS)
  • G1(Garbage First) GC

이 중에서 운영 서버에서 절대 사용하면 안 되는 방식이 Serial GC다. Serial GC는 데스크톱의 CPU 코어가 하나만 있을 때 사용하기 위해서 만든 방식이다. Serial GC를 사용하면 애플리케이션의 성능이 많이 떨어진다.

그럼 각 GC 방식에 대해서 살펴보자.

Serial GC (-XX:+UseSerialGC)

Young 영역에서의 GC는 앞 절에서 설명한 방식을 사용한다. Old 영역의 GC는 mark-sweep-compact이라는 알고리즘을 사용한다. 이 알고리즘의 첫 단계는 Old 영역에 살아 있는 객체를 식별(Mark)하는 것이다. 그 다음에는 힙(heap)의 앞 부분부터 확인하여 살아 있는 것만 남긴다(Sweep). 마지막 단계에서는 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다(Compaction).

Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식이다.

Parallel GC (-XX:+UseParallelGC)

Parallel GC는 Serial GC와 기본적인 알고리즘은 같지다. 그러나 Serial GC는 GC를 처리하는 스레드가 하나인 것에 비해, Parallel GC는 GC를 처리하는 쓰레드가 여러 개이다. 그렇기 때문에 Serial GC보다 빠른게 객체를 처리할 수 있다. Parallel GC는 메모리가 충분하고 코어의 개수가 많을 때 유리하다. Parallel GC는 Throughput GC라고도 부른다.

다음 그림은 Serial GC와 Parallel GC의 스레드를 비교한 그림이다.JavaGarbage4

그림 4 Serial GC와 Parallel GC의 차이 (이미지 출처: "Java Performance", p. 86)

Parallel Old GC(-XX:+UseParallelOldGC)

Parallel Old GC는 JDK 5 update 6부터 제공한 GC 방식이다. 앞서 설명한 Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다르다. 이 방식은 Mark-Summary-Compaction 단계를 거친다. Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.

CMS GC (-XX:+UseConcMarkSweepGC)

다음 그림은 Serial GC와 CMS GC의 절차를 비교한 그림이다. 그림에서 보듯이 CMS GC는 지금까지 설명한 GC 방식보다 더 복잡하다.

JavaGarbage5

그림 5 Serial GC와 CMS GC(이미지 출처)

초기 Initial Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다. 따라서, 멈추는 시간은 매우 짧다. 그리고 Concurrent Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다. 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 것이다.

그 다음 Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되고 있는 상황에서 진행한다.

이러한 단계로 진행되는 GC 방식이기 때문에 stop-the-world 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용하며, Low Latency GC라고도 부른다.

그런데 CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재한다.

  • 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
  • Compaction 단계가 기본적으로 제공되지 않는다.

따라서, CMS GC를 사용할 때에는 신중히 검토한 후에 사용해야 한다. 그리고 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 더 길기 때문에 Compaction 작업이 얼마나 자주, 오랫동안 수행되는지 확인해야 한다.

G1 GC

마지막으로 G1(Garbage First) GC에 대해서 알아보자. G1 GC를 이해하려면 지금까지의 Young 영역과 Old 영역에 대해서는 잊는 것이 좋다.

다음 그림에서 보다시피, G1 GC는 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다. 그러다가, 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행한다. 즉, 지금까지 설명한 Young의 세가지 영역에서 데이터가 Old 영역으로 이동하는 단계가 사라진 GC 방식이라고 이해하면 된다. G1 GC는 장기적으로 말도 많고 탈도 많은 CMS GC를 대체하기 위해서 만들어 졌다.JavaGarbage6

그림 6 G1 GC의 레이아웃(이미지 출처: "The Garbage-First Garbage Collector" (TS-5419), JavaOne 2008, p. 19)

G1 GC의 가장 큰 장점은 성능이다. 지금까지 설명한 어떤 GC 방식보다도 빠르다. 하지만, JDK 6에서는 G1 GC를 early access라고 부르며 그냥 시험삼아 사용할 수만 있도록 한다. 그리고 JDK 7에서 정식으로 G1 GC를 포함하여 제공한다.

그러나 JDK 7을 실서비스에서 사용하려면 많은 검증 기간(1년은 필요하다는 생각이다)을 거쳐야 할 것으로 보이기 때문에, G1 GC를 당장 사용하고 싶어도 더 기다리는 것이 좋다는 것이 개인적인 생각이다. JDK 6에서 G1 GC를 적용했다가 JVM Crash가 발생했다는 말도 몇 번 들었기에 더더욱 안정화될 때까지 기다리는 것이 좋겠다.

마치며

이번 글에서는 Java의 GC에 대해서 아주 간단하게(?) 살펴보았다. 다음 글에서는 Java의 GC 상황을 모니터링하는 방법과 GC 튜닝 방법을 알아볼 예정이다.

마지막으로 한 가지 더 말하고 싶은 것이 있다. 어떤 서비스에서 A라는 GC 옵션을 적용해서 잘 동작한다고 그 GC 옵션이 다른 서비스에서도 훌륭하게 적용되어 최적의 효과를 볼 수 있다고 생각하지 말라는 것이다.

만약 애플리케이션에서 만들어지는 모든 객체의 크기와 종류가 같다면 회사에서 사용하는 모든 WAS의 GC 옵션을 동일하게 설정할 수 있다. 하지만, 각 서비스의 WAS에서 생성하는 객체의 크기와 생존 주기가 모두 다르고, 장비의 종류도 다양하다. WAS의 스레드 개수와 장비당 WAS 인스턴스 개수, GC 옵션 등은 지속적인 튜닝과 모니터링을 통해서 해당 서비스에 가장 적합한 값을 찾아야 한다. 이 이야기는 필자의 경험에서 나온 이야기가 아니고, 2010년 JavaOne에서 Oracle JVM을 만드는 엔지니어들이 한 말이다.

참고 자료

이 글의 내용과 그림은 다음의 자료를 참고했다.


반응형

'Java기초' 카테고리의 다른 글

poi  (0) 2022.11.24
JPA(Java Persistent API)란  (0) 2021.10.21
gc g1  (0) 2015.12.10
gc 방식  (0) 2015.12.10
gc 후 메모리 사용순서  (0) 2015.12.10

+ Recent posts