오랜 시간 동안 http를 썼지만 정식 용어를 모를 때가 많다.


HTTP는 HTTP 응답에서 Last-Modified라는 이름의 헤더를 제공한다. 


Last-Modified 헤더는 데이터가 마지막으로 변경된 시기를 의미한다. Last-Modified 헤더는 피드의 updated 필드의 기능을 대신한다.



클라이언트는 HTTP 헤더의 값을 저장한다. 

그리고 HTTP GET 요청에 클라이언트는 GET 요청과 함께 If-Modified-Since 헤더에 읽기 값을 보낸다. 데이터가 변경되었다면 서버는 HTTP 304(변경 되지 않음) 상태 코드를 응답한다. 그리고 상태 코드를 제외하고 어떠한 데이터를 전송하지 않는다.


데이터가 변경되었는지 쉽게 결정할 수 있다. 예를 들어 데이터베이스의 마지막 변경 시간을 결정하는 코드를 구현할 수 있다. 이는 모든 데이터를 Atom 표현으로 변환하는 것보다 훨씬 효율적이다.


데이터가 실제로 변경되었다면 요청에 대해 200(OK)으로 정상적으로 응답된다. 또한 클라이언트가 HTTP 캐싱을 다시 사용할 수 있도록 Last-Modified 헤더에 새로운 값이 전송된다.


지정된 If-Modified-Since 헤더를 포함한 요청을 데이터베이스에서는 마지막 변경 시간과 비교하고 결정하는 데 사용된다. 데이터가 변경되지 않으면 HTTP 304 상태 코드를 리턴한다. 이 경우 HTTP 요청에 응답하는 데 하나의 데이터베이스 쿼리만 필요하다.



이런 방식을 Conditional Get (조건부 Get)라 한다.

'영어앤영문권' 카테고리의 다른 글

Stalemate- 교착 상태  (0) 2019.08.24
snowflask system(눈송이 시스템)  (0) 2019.08.16
self-contained system (SCS) 번역  (0) 2018.08.28
authz, authn  (0) 2018.08.10
BYOD  (0) 2018.07.28
Posted by '김용환'
,

OpenTsdb 분석

scribbling 2018. 8. 31. 12:50





opentsdb 소스(https://github.com/OpenTSDB/opentsdb )를 살펴보니.. 큰 특징은 다음과 같다.


<내용>

1. grafana를 지원한다. (사실 이것 때문에 쓰는 것임)


2. hbase 쿼리 튜닝은 되어 있지 않다(start, end로 해서 읽어옴, 대용량 데이터에 엄청 취약)


3. gpgl 이다. (소스를 건들 수 없다)

    OpenTSDB is free software and is available under both LGPLv2.1+ and GPLv3+.


4. 성능을 위해 async hbase(Defered)를 사용했다. (그러나 성능 이슈는 앱의 쓰레드 부분이 아니라 쿼리 부분에서 발생한다)


5. 야후 개발자 한명이 혼자 다 개발한다. 따라서 확장성에서 취약하다. 아저씨 코드이다. 


6. 현재 3.0 개발중(google bigtable도 깔끔하게 지원될 것 같다)


7. 데이터가 많아지면 트래픽에 굉장히 취약하다. 대용량 환경에서는 druid가 답이다. 


7. 2.3까지 pre-aggregation, google big table/cassandra 지원은 완벽하지 않다. hbase 빼고는 답이 없다. (대용량에 취약하다.)


8. druid vs opentsdb 

https://community.hortonworks.com/questions/89810/druid-vs-opentsdb-for-tick-data.html

https://www.popit.kr/time-series-olap-druid-%EC%9E%85%EB%AC%B8/ (람다 아키텍처 지원)

https://groups.google.com/forum/#!topic/druid-development/bPJbWO-g4aw

https://java.libhunt.com/compare-opentsdb-vs-druid







<세부 내용>

1. 원래 opentsdb의 의도는 한 대의 host, network를 모니터링을 위해 만들어졌다. kafka* 이런식의 모아보기 용은 아니다.


2. 키 구성

row key : metric_uid + timestamp + tagk1 + tagk2 + <tagkN> + <tagvN>

timestamp 

tagk1=tagv1

tagk2=tagv2



3. 키 검색 순서

time range -> metric -> tags

따라서 동일한 그룹핑 단어가 많아지면 성능은 좋지 않다.(data. agg. 으로 시작하는 단어)



4. opentsdb 2.0부터 4개의 테이블이 생성된다.

data

uid

tree

metadata 테이블 : name <--> id 매핑


5. tagk 길이 제한

rowkey에 저장해야 하기에 5개 태그 미만으로 저장해야 한다.



6. opentsdb가 api에 예민(host 정보 없으면 에러 출력), 성능이 약해서 opentsdb 앞단에 opentsdb compatible api g/w를 사용해야 잘 감싸는 서버가 필요하다. 따라서 custom, 모아보기 기능도 쉽게 진행할 수 있다. 




<opentsb를 활용한 프로젝트>


https://github.com/turn/splicer : 여러 opentsdb 클러스터와 redis를 두어 모니터링(더이상 운영 안됨)


https://github.com/facebookarchive/beringei : facebook에서는 인메모리 TSDB용으로 개발(더이상 운영 안됨)





<참고>

Opentsdb 좋은 공부 자료

http://tsunanet.net/~tsuna/hbasecon-2014-opentsdb-2.0.pdf

http://opentsdb.net/misc/opentsdb-oscon.pdf

http://Marp의 opentsdb 튜닝가이드 https://mapr.com/blog/hbase-key-design-opentsdb/

http://opentsdb.net/docs/build/html/user_guide/backends/bigtable.html







다음은 opentsdb를 분석하며 대충 그려놓은 그림이다. 자료가 없어서 이를 참조하면 좋을 것 같다. 













Posted by '김용환'
,




카프라 토픽에 저장된 데이터가 너무 많아지면 lag의 값은 점점 커진다.


lag이 커지면 처리되야 할 양은 많아지고, 결국 카프카가 저장하는 토픽 저장소의 한계도 넘어서게 된다.


따라서, 이럴 때 간단히 해결할 수 있는 방법은 파티션을 늘리는 것이다. 


topic에 할당한 파티션(partion)의 개수가 5이면 10개로 늘리면, 조금씩 lag이 줄어든다..

10개이면 20개로 늘리면 조금씩 lag이 줄어든다.



consumer group의 병렬 처리 정도는 consume하는 파티션의 수에 의해 제한된다. 따라서 일반적으로 파티션이 많아지면 당연히 처리량, throghput이 높아진다.




그렇다면 파티션을 늘리는 것이 답일까??

먼가 kafka에 영향을 주지 않을까 고민하던데 차에. 아래 문서를 보게 되었다.





http://www.confluent.io/blog/how-to-choose-the-number-of-topicspartitions-in-a-kafka-cluster/




요점 - 발 번역



1. 파티션 개수가 많아지면 오픈 파일 핸들(open file handle)을 더 필요로 한다.


각 파티션은 브로커의 파일 시스템의 디렉토리에 매핑된다. 해당 디렉토리에는 로그 세그먼트 당 두 개의 파일 (인덱스 용, 실제 데이터 용)이 존재한다. 각 브로커는 모든 로그 세그먼트의 인덱스와 데이터 파일 모두에 대한 파일 핸들을 연다. 따라서 파티션이 많을수록 기본 운영 체제에서 열린 파일 핸들 제한을 구성한다. 따라서 카프카 서버 운영자 입장에서는 open file handle(fd) 개수 더 커지지 않도록 운영할 필요가 있다.



2. 파티션 개수가 많아지면 비-가용성이 증가된다.


장애 상황에 이슈가 발생할 수 있다. 브로커가 불명확하게 종료될 비-가용성은 파티션 수에 비례한다. 브로커에 총 2000개의 파티션이 있고 각 파티션에 2 개의 복제본이 있다고 가정하면 해당 브로커는 약 1000개의 파티션을 위한 리더가 될 것이다.  해당 브로커가 불완전히 실패하면 1000개의 파티션을 모두 동시에 사용할 수 없게 된다. 단일 파티션에 대한 새로운 리더를 선출하는 데 5ms가 걸린다고 가정하면 1000개의 모든 파티션에 대한 새로운 리더를 뽑는는 데 최대 5 초가 소요된다. 일부 파티션의 경우 비-가용성은 5초+실패 감지 시간이 될 것이다. 


불행한 경우, 실패한 브로커가 컨트롤러라면 심각하다. 페일 오버는 자동으로 발생하지만 새 컨트롤러는 초기화하는 동안 ZooKeeper에서 모든 파티션의 일부 메타 데이터를 읽어야 한다. 예를 들어 카프카 클러스터에 10,000 개의 파티이 있고 ZooKeeper에서 메타 데이터를 초기화하는 데 파티션 당 2ms가 걸리면 사용 불가능한 창에 20초가 더 걸릴 수 있다.


따라서 최악의 경우를 피하려면 브로커 당 파티션 수를 2개에서 4000개 밑으로 제한하고 클러스터의 총 파티션 수를 수만 개로 제한하는 것이 좋다.



3. 파티션 개수가 많아지면 end-to-end 대기 시간이 증가된다.


카프카의 end-to-end 대기 시간은 producer가 메시지를 퍼블리싱하고 consumer가 메시지를 읽을 때까지의 시간으로 정의된다. 메시지 커밋 시간은 end-to-end 대기 시간의 상당 부분을 차지한다. default로 카프카 브로커는 두 개의 브로커 간에 복제본을 공유하는 모든 파티션에 대해 단일 스레드를 사용하여 다른 브로커의 데이터를 복제한다. 한 브로커에서 다른 브로커로 1000개의 파티션을 복제하면 약 20ms의 대기 시간이 추가될 수 있다. 즉 end-to-end 대기 시간이 최소 20ms임을 의미한다. 


end-to-end 대기 시간을 줄이려면 브로커 당 파티션 수를 100 x 카프카 클러스터의 브로커 수 x replica factor로 제한하는 것이 좋다. 



4. 파티션 개수가 많아지면 producer/consumer 클라이언트에서 많은 메모리를 필요로 한다.

0.8.2 릴리스에서 producoer는 파티션 당 메시지를 버퍼한다. 충분한 데이터가 축적되거나 충분한 시간이 경과하면 축적 된 메시지가 버퍼에서 제거되고 브로커로 전송된다.


파티션 수를 늘리면 producer의 더 많은 파티션에 메시지가 저장하게 된다. 사용된 메모리 총량이 설정된 메모리 제한을 초과할 수 있다. 이 경우 producer는 새로운 메시지를 차단하거나 삭제해야 하며 어느 쪽도 이상적이지 않다. 


따라서 producer가 생성하는 파티션 당 최소 수십 KB를 할당하도록 하고 파티션 수가 크게 늘어날 경우 총 메모리 양을 조정해야 한다.


비슷한 문제가 consumer에도 존재한다. consumer는 파티션 당 메시지 덩어리를 가져온다. 파티션이 클수록 더 많은 메모리가 필요하다.

Posted by '김용환'
,


docker build 할 때 이미지를 한 번 다운로드하면 캐싱하는 것이 정책이다.


따라서 no-cache 옵션을 변경해서 매번 docker build할 때 마다 이미지를 생성하도록 한다.


--no-cache=true


https://docs.docker.com/engine/reference/commandline/build/






Posted by '김용환'
,


self-contained system을 한글로 번역할 때 소프트웨어 공학 개념에서 사용되는 ‘여러 개의 작은 시스템’이 가장 의도에 맞는 듯 하다.


마이크로 서비스 관점에서는 '독립 시스템'도 괜찮을 것 같다.

'영어앤영문권' 카테고리의 다른 글

snowflask system(눈송이 시스템)  (0) 2019.08.16
개발자 영어 - HTTP의 조건부 GET(conditional get)  (0) 2018.08.31
authz, authn  (0) 2018.08.10
BYOD  (0) 2018.07.28
생성자(constructor)를 짧게 영어로 하면..  (0) 2018.07.27
Posted by '김용환'
,



도커 환경을 정리하는 커맨드가 여럿 있다.




· docker container prune은 중지된 모든 컨테이너를 삭제한다.


· docker image prune은 이름 없는 모든 이미지를 삭제한다.


· docker network prune은 사용되지 않는 도커 네트워크를 모두 삭제한다.


· docker volume prune은 도커 컨테이너에서 사용하지 않는 모든 도커 볼륨을 삭제한다.


· docker system prune -a는 중지된 모든 컨테이너, 사용되지 않은 모든 네트워크, 하나 이상의 컨테이너에서 사용되지 않는 모든 이미지를 삭제한다. 따라서 남아있는 컨테이너 또는 이미지는 현재 실행 중인 컨테이너에서 필요하다.



Posted by '김용환'
,



1조원을 모은 할아버지의 투자 비결은


시가 총액 1등을 샀다고 한다



http://www.thebell.co.kr/free/content/ArticleView.asp?key=201711100100018140001123&lcode=00




예전에 사실 항상 들었던 내용이 섹션(산업 분야)에서 1등 주만 사라는 얘기를 들었다.


많은 주식으로 성공하신 대가들의 내용들도 있었던 것 같다. 트렌드보다. 


오직 산업의 1등 주만 봐도 좋을 것 같다는 생각이 든다.

Posted by '김용환'
,





집킨(zipkin)은 복잡한 시스템에서

마이크로 서비스의 상호 작용에 대한 개요를 파악하는 데 도움이 될 수 있다.






스프링 클라우드 Sleuth(https://cloud.spring.io/spring-cloud-sleuth/)는

스프링 부트 애플리케이션과 집킨을 통합할 수 있는 매우 쉬운 방법을 제공한다.


문서는 다음과 같다.

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



스프링 클라우드 Sleuth는 추적 ID와 범위(span) ID가 생성되고 통신 중에 전달되도록 한다.

그리고 추적 데이터를 집킨 서버로 전송한다.



스프링 클라우드에 zipkin 관련 프로젝트(spring-cloud-starter-zipkin)가 있다.


예시는 다음을 참조한다.

https://www.baeldung.com/tracing-services-with-zipkin





또한 스프링 클라우드 Sleuth를 사용해 추적 ID만 전송할 수도 있다(http://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/1.2.5.RELEASE/single/spring-cloud-sleuth.html#_only_sleuth_log_correlation).


로그에 추적 ID를 저장하고 있다면 요청에 대한 모든 로그 정보를 로그 분석에서 사용할 수 있다.

Posted by '김용환'
,



open stack vm 인스턴스를 생성할 때 동일한 pm에 있으면, 

pm이 장애가 발생했을 때 같이 인스턴스가 종료되기 때문에 위험할 수 있다. 


같은 pm 장비에 vm 장비가 없도록 하는 것을 anti-affinity라는 정책인데. 그룹(group)에만  적용할 수 있다.

인스턴스 사용하는 방법은 다음과 같다.




$ openstack server group list


+--------------------------------------+--------------------+---------------+

| ID                                                           | Name                       |         Policies      |

+--------------------------------------+--------------------+---------------+

+--------------------------------------+--------------------+---------------+





anti-affinity 정책을 포함한 그룹을 추가한다.


$ openstack server group create stats-tsdb-read --policy anti-affinity


+----------+--------------------------------------+

| Field    | Value                                |

+----------+--------------------------------------+

| id       | 1cd7538d-23c4-493e-8684-3bf46c440864 |

| members  |                                      |

| name     | stats-tsdb-read                      |

| policies | anti-affinity                        |

+----------+--------------------------------------+





이제 stats-tsdb-read group으로 서버를 추가한다. 


$ openstack server create --flavor large --image ubuntu-16.04 --availability-zone US_WEST --hint group=1cd7538d-23c4-493e-8684-3bf46c440864 stats-tsdb-01



$ openstack server create --flavor large --image ubuntu-16.04 --availability-zone US_WEST --hint group=1cd7538d-23c4-493e-8684-3bf46c440864 stats-tsdb-02





만약 잘못만들었다면 다음과 같이 그룹을 삭제한다.


$ openstack server group delete stats-tsdb


Posted by '김용환'
,

마이크로 서비스에서는 json 로그로 저장되도록 하는 것이 중요한데..

logback에 json 출력 기능이 있다. 



logstash logback 라이브러리를 쓰는 것도 있지만.. (아래 링크 참조)


http://knight76.tistory.com/entry/log-log4j%EC%97%90%EC%84%9C-json-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EC%B6%9C%EB%A0%A5%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0



 spring boot의 logback에도 json 출력한다.



<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<include resource="org/springframework/boot/logging/logback/defaults.xml" />

<property name="LOG_FILE"

value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}" />

<include resource="org/springframework/boot/logging/logback/console-appender.xml" />


<appender name="JSON_FILE"

class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${LOG_PATH}/order-${PID}.json</file>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>order-${PID}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>

<maxHistory>7</maxHistory>

</rollingPolicy>

<encoder

class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">

<providers>

<arguments />

<stackTrace />

<timestamp>

<timeZone>UTC</timeZone>

</timestamp>

<pattern>

<pattern>

{

"severity": "%level",

"service": "order",

"pid": ${PID:-},

"thread": "%thread",

"logger": "%logger",

"message": "%message"

}

</pattern>

</pattern>

</providers>

</encoder>

</appender>

<root level="INFO">

<appender-ref ref="JSON_FILE" />

<appender-ref ref="CONSOLE" />

</root>

</configuration>

Posted by '김용환'
,