From 7ef67dd9dd5649ee27e814929dd80bfa1f54fb71 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Tue, 1 Nov 2022 21:01:49 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[ADD]=209=EC=9E=A5=20-=20=EC=9D=B4=EC=84=9C?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../A\354\241\260/\354\235\264\354\204\234\354\232\260.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "9\354\243\274\354\260\250/A\354\241\260/empty" => "9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" (100%) diff --git "a/9\354\243\274\354\260\250/A\354\241\260/empty" "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" similarity index 100% rename from "9\354\243\274\354\260\250/A\354\241\260/empty" rename to "9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" From 8521fcd702bd7bda276aee472e6161dbdc0170e2 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Tue, 1 Nov 2022 21:21:53 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[ADD]=209=EC=9E=A5=20-=20=EC=9D=B4=EC=84=9C?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../A\354\241\260/empty" | 0 .../\354\235\264\354\204\234\354\232\260.md" | 164 ++++++++++++++++++ 2 files changed, 164 insertions(+) delete mode 100644 "9\354\243\274\354\260\250/A\354\241\260/empty" create mode 100644 "9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" diff --git "a/9\354\243\274\354\260\250/A\354\241\260/empty" "b/9\354\243\274\354\260\250/A\354\241\260/empty" deleted file mode 100644 index e69de29..0000000 diff --git "a/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" new file mode 100644 index 0000000..627530a --- /dev/null +++ "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -0,0 +1,164 @@ +# 9장 웹 크롤러 설계 + +## 웹 크롤러 (= 로봇, 스파이더) + +- 검색 엔진에서 널리 쓰는 기술 +- 목적 : 웹에 새로 올라오거나 갱신된 콘텐츠(웹 페이지, 이미지나 비디오 등)를 찾아내는 것 +- 웹 페이지 몇 개에서 시작하여 그 링크를 따라 나가면서 새로운 콘텐츠를 수집한다 + +## 웹 크롤러 이용 + +- 검색 엔진 인덱싱 : 크롤러의 가장 보편적인 용례 +- 웹 아카이빙 : 나중에 사용할 목적으로 장기보관하기 위해 웹에서 정보를 모으는 절차. 많은 국립 도서관이 크롤러로 웹 사이트 아카이빙 중 +- 웹 마이닝 : 인터넷에서 유용한 지식을 도출 +- 웹 모니터링 : 크롤러를 사용하면 인터넷에서 저작권이나 상표권이 침해되는 사례를 모니터링 할 수 있다 + +## 1단계 문제 이해 및 설계 범위 확정 + +### 웹 크롤러의 기본 알고리즘 + +1. URL 집합이 입력으로 주어지면, 해당 URL 들이 가리키는 모든 웹 페이지를 다운로드 한다 +2. 다운받은 웹 페이지에서 URL 들을 추출한다 +3. 추출된 URL들을 다운로드 할 URL 목록에 추가하고 위의 과정을 처음부터 반복한다 + +### 좋은 웹 크롤러가 만족시켜야 할 속성 + +- 규모 확장성 : 웹은 거대하고 수십억 개의 페이지가 존재하므로 병행성을 활용하면 효과적 +- 안정성 : 웹은 함정으로 가득하다. 크롤러를 비정상적인 입력이나 환경에 잘 대응할 수 있어야한다 +- 예절 : 크롤러는 수집 대상 웹 사이트에 짧은 시간동안 너무 많은 요청을 보내서는 안 된다 +- 확장성 : 새로운 형태의 콘텐츠를 지원하기가 쉬워야 한다. + +## 2단계 개략적 설계안 제시 및 동의 구하기 + +### 시작 URL 집합 + +- 웹 크롤러가 크롤링을 시작하는 출발점 + ex) 어떤 대학 웹 사이트로부터 찾아 나갈 수 있는 모든 웹 페이지를 크롤링하는 가장 직관적인 방법은 해당 대학의 도메인 이름이 붙은 모든 페이지의 URL을 시작URL로 쓰는 것 +- 크롤러가 가능한 한 많은 링크를 탐색할 수 있도록 하는 URL을 고르는 것이 바람직 +- 전체 URL공간을 작은 부분집합으로 나누는 전략을 사용 +- 주제별로 다른 시작 URL을 사용 +- 시작 URL을 무엇을 쓸 것이냐는 질문에 정답은 없다 + +## 미수집 URL 저장소 + +- 대부분의 현대적 웹 크롤러는 크롤링 상태를 다운로드할 URL, 다운로드 된 URL의 두가지로 나눠 관리한다. +- 이 중 다운로드 할 URL을 저장 관리하는 컴포넌트를 미수집 URL 저장소라 부른다 + +### HTML 다운로더 + +- 인터넷에서 웹 페이지를 다운로드하는 컴포넌트 +- 다운로드할 페이지 URL은 미수집 URL저장소가 제공 + +### 도메인 이름 변환기 + +- URL -> IP주소 +- HTML 다운로더는 도메인 이름 변환기를 사용하여 URL에 대응되는 IP주소를 알아낸다 + +### 콘텐츠 파서 + +- 웹페이지를 다운로드하면 파싱과 검증 과정을 거쳐야한다. 이상한 웹 페이지는 문제를 일으킬 수 있다 +- 크롤링 서버 안에 구현하면 크롤링 과정이 느려지게 되므로 독립된 컴포넌트로 만들었다 + +### 중복 콘텐츠인가? + +- 같은 콘텐츠 여러번 저장하게 되는 것 방지 +- 효과적인 방법은 웹 페이지의 해시 값 비교 + +### 콘텐츠 저장소 + +- HTML 문서를 보관하는 시스템 +- 저장할 데이터의 유형, 크기, 저장소 접근 빈도, 데이터의 유효기간 등을 종합적으로 고려해야 한다 + +### URL 추출기 + +- HTML 페이지를 파싱하여 링크들을 골라냄 + +### URL 필터 + +- 특정한 콘텐츠 타입이나 파일 확장자를 갖는 URL, 접속 시 오류 발생하는 URL 등 을 크롤링 대상에서 배제하는 역할 + +### 이미 방문한 URL? + +- 이미 방문한 URL이나 미수집 URL 저장소에 보관된 URL을 추적할 수 있도록 하는 자료 구조 (볼륨필터나 해시 테이블..) 사용 +- 같은 URL을 여러번 처리하는 일 방지하여 서버 부하를 줄이고 시스템이 무한 루프에 빠지는 일 방지 + +### URL 저장소 + +- 이미 방문한 URL 을 보관하는 저장소 + +### 웹 크롤러 작업 흐름 + +1. 시작 URL 들을 미수집 URL 저장소에 저장 +2. HTML 다운로더는 미수집 URL 저장소에서 URL 목록 가져옴 +3. HTML 다운로더는 도메인 이름 변환기를 사용하여 URL의 IP 주소르 알아내고, 해당 IP 주소로 접속하여 웹 페이지를 다운 받음 +4. 콘텐츠 파서는 다운된 HTML 페이지를 파싱하여 올바른 형식을 갖춘 페이지인지 검증 +5. 콘텐츠 파싱과 검증이 끝나면 중복 콘텐츠인지 확인하는 절차 개시 +6. 중복 콘텐츠인지 확인하기 위해서, 해당 페이지가 이미 저장소에 있는지 본다 ( 이미 저장소에 있 : 처리하지 않고 버림 / 없 : 저장소에 저장한 뒤 URL 추출기로 전달) +7. URL 추출기는 해당 HTML 페이지에서 링크를 골라낸다 +8. 골라낸 링크를 URL 필터로 전달한다 +9. 필터링이 끝나고 남은 URL 만 중복 URL 판별 단계로 전달 +10. 이미 처리한 URL인지 확인하기 위해 ,URL저장소에서 보관된 URL인지 살핀다. 이미 저장소에 있는 URL은 버린다 +11. 저장소에 없는 URL은 URL 저장소에 저장할 뿐 아니라 미수집 URL 저장소에도 전달한다 + +## 3단계 상세 설계 + +### DFS VS BFS + +- 웹 크롤러는 보통 BFS 사용. 큐의 한쪽으로는 탐색할 URL을 집어넣고, 다른 한쪽으로는 꺼내기만 한다 +- 문제 1: 한 페이지에서 나오는 링크의 상당 수는 같은 서버로 되돌아간다. 크롤러는 같은 호스트에 속한 많은 링크를 다운받느라 바빠지게 되는데, 이때 링크들을 병렬로 처리하게 된다면 서버 과부하(예의 없는 크롤러) +- 문제 2 : BFS알고리즘은 우선순위를 두지 않지만 모든 웹 페이지가 같은 수준의 품질, 중요성을 갖지 않는다. + +### 미수집 URL 저장소 + +- 위의 문제 해결 방앉 +- 이 저장소를 잘 구현하여 예의를 갖춘 크롤러, URL 사이의 우선순위와 신선도를 구별하는 크롤러를 구현할 수 있다. + +#### 예의 + +- 동일 웹 사이트에 대해서는 한 번에 한 페이지만 요청 +- 웹 사이트의 호스트명과 다운로드를 수행하는 작업스레드 사이의 관계를 유지하면된다. 각 다운로드는 스레드 별로 FIFO큐를 가지고 있어서, 해당 큐에서 꺼낸 URL만 다운로드 한다 + +#### 우선순위 + +- 순위결정장치는 URL 우선순위를 결정하는 컴포넌트 + +#### 신선도 + +- 데이터의 신선함을 유지하기 위해서는 이미 다운로드한 페이지라고 해도 주기적으로 재수집할 필요가 있다. +- 이 작업을 최적화하기 위해 웹 페이지의 변경 이력을 활용하거나 우선순위를 활용하여 중요한 페이지는 좀 더 자주 재수집한다 + +### HTML 다운로더 + +- HTTP 프로토콜을 통해 웹 페이지를 내려받는다 + +#### 로봇 제외 프로토콜 + +- 웹사이트가 크롤러와 소통하는 표준적 방법 +- 크롤러가 수집해도 되는 웹 페이지 목록이 들어잇따 +- 웹 사이트를 긁어 가기 전에 크롤러는 해당 파일에 나열된 규칙을 먼저 확인해야한다 + +#### 성능 최적화 + +다음은 HTML 다운로더에 사용할 수 있는 성능 최적화 기법들이다 + +1. 분산 크롤링 +2. 도메인 이름 변환 결과 캐시 +3. 지역성 +4. 짧은 타임아웃 + +### 안정성 확보 전략 + +- 안정 해시 : 다운로더 서버들에 부하를 분산할 때 적용가능한 기술, 이 기술 이용하면 다운로더 서버를 쉽게 추가하고 삭제할 수 있다 +- 크롤링 상태 및 수집 데이터 저장: 장애가 발생한 경우에도 쉽게 복구할 수 있도록 크롤링 상태와 수집된 데이터를 지속적으로 저장장치에 기록해 두는 것이 바람직 +- 예외 처리 : 대규모 시스템에서 에러는 불가피할 뿐 아니라 흔하게 벌어지는 일이다. +- 데이터 검증 + +### 확장성 확보 전략 + +- 시스템을 설계할 때는 새로운 형태의 콘텐츠를 쉽게 지원할 수 있도록 신경써야한다. + +### 문제 있는 콘텐츠 감지 및 회피 전략 + +1. 중복 콘텐츠 : 해시나 체크섬을 사용하여 중복 콘텐츠 탐지 +2. 거미 덫 : 크롤러를 무한 루프에 빠뜨리도록 설계한 웹 페이지. URL의 최대 길이를 제한하면 회피할 수 있다. 하지만 가능한 모든 종류으 덫을 피할 수 있는 만능 해결책은 없다 +3. 데이터 노이즈: 어떤 콘텐츠는 거의 가치가 없으므로 가능하다면 제외해야한다 From 2f3f5a90d0c26ea6981cfd6879c70bc865978a96 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Wed, 2 Nov 2022 18:38:28 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[ADD]=209=EC=9E=A5=20-=20=EC=9D=B4=EC=84=9C?= =?UTF-8?q?=EC=9A=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\235\264\354\204\234\354\232\260.md" | 78 ++++++++++++++++--- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git "a/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index 627530a..38ed114 100644 --- "a/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -30,13 +30,15 @@ ## 2단계 개략적 설계안 제시 및 동의 구하기 +![](https://k.kakaocdn.net/dn/rP55W/btrP8WOAZn0/CI9aK0urjFwBn0YFjVWkxk/img.jpg) + ### 시작 URL 집합 - 웹 크롤러가 크롤링을 시작하는 출발점 ex) 어떤 대학 웹 사이트로부터 찾아 나갈 수 있는 모든 웹 페이지를 크롤링하는 가장 직관적인 방법은 해당 대학의 도메인 이름이 붙은 모든 페이지의 URL을 시작URL로 쓰는 것 - 크롤러가 가능한 한 많은 링크를 탐색할 수 있도록 하는 URL을 고르는 것이 바람직 -- 전체 URL공간을 작은 부분집합으로 나누는 전략을 사용 -- 주제별로 다른 시작 URL을 사용 +- 전체 URL공간을 작은 부분집합으로 나누는 전략을 사용(나라별로 인기 있는 웹 사이트가 다르다는 점에서 착안) +- 주제별로 다른 시작 URL을 사용 (쇼핑, 스포츠, 건강 등등의 주제별로 세분화하고 그 각각에 다른 시작 URL) - 시작 URL을 무엇을 쓸 것이냐는 질문에 정답은 없다 ## 미수집 URL 저장소 @@ -68,6 +70,7 @@ - HTML 문서를 보관하는 시스템 - 저장할 데이터의 유형, 크기, 저장소 접근 빈도, 데이터의 유효기간 등을 종합적으로 고려해야 한다 +- ex) 데이터 양이 많으므로 대부분 콘텐츠는 "디스크"에 저장, 인기 있는 콘텐츠는 "메모리"에 두어 접근 지연시간 줄임 ### URL 추출기 @@ -102,52 +105,90 @@ ## 3단계 상세 설계 -### DFS VS BFS +### < DFS VS BFS> - 웹 크롤러는 보통 BFS 사용. 큐의 한쪽으로는 탐색할 URL을 집어넣고, 다른 한쪽으로는 꺼내기만 한다 - 문제 1: 한 페이지에서 나오는 링크의 상당 수는 같은 서버로 되돌아간다. 크롤러는 같은 호스트에 속한 많은 링크를 다운받느라 바빠지게 되는데, 이때 링크들을 병렬로 처리하게 된다면 서버 과부하(예의 없는 크롤러) - 문제 2 : BFS알고리즘은 우선순위를 두지 않지만 모든 웹 페이지가 같은 수준의 품질, 중요성을 갖지 않는다. -### 미수집 URL 저장소 +### <미수집 URL 저장소> - 위의 문제 해결 방앉 - 이 저장소를 잘 구현하여 예의를 갖춘 크롤러, URL 사이의 우선순위와 신선도를 구별하는 크롤러를 구현할 수 있다. -#### 예의 +### 예의 - 동일 웹 사이트에 대해서는 한 번에 한 페이지만 요청 +- 같은 웹 사이트의 페이지를 다운받는 태스크는 시간차를 두고 실행하도록 하면 된다 - 웹 사이트의 호스트명과 다운로드를 수행하는 작업스레드 사이의 관계를 유지하면된다. 각 다운로드는 스레드 별로 FIFO큐를 가지고 있어서, 해당 큐에서 꺼낸 URL만 다운로드 한다 -#### 우선순위 +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbviJv0%2FbtrP9AdeSdU%2F02iEO9ybfOzTbT4MuODXJk%2Fimg.jpg) + +- 큐 라우터 : 같은 호스트에 속한 URL은 언제나 같은 큐로 가도록 보장 +- 매핑 테이블 : 호스트 이름과 큐 사이의 관계를 보관하는 테이블 +- FIFO 큐(b1~bn) : 같은 호스트에 속한 URL은 언제나 같은 큐에 보관 +- 큐 선택기 : 큐 선택기는 큐들을 순회하면서 큐에서 URL을 꺼내서 해당 큐에서 나온 URL을 다운로드하도록 지정된 작업 스레드에 전달하는 역할 +- 작업 스레드 : 전달된 URL을 다운로드하는 작업을 수행 + +### 우선순위 +- 페이지랭크, 트래픽 양, 갱신 빈도 등 다양한 척도를 사용하여 URL 우선순위를 나눔 - 순위결정장치는 URL 우선순위를 결정하는 컴포넌트 + ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbKe803%2FbtrQdzKSGSZ%2FYU9hIZzzz0NncK2rEulFN1%2Fimg.jpg) +- 순위결정장치 : URL을 입력으로 받아 우선순위를 계산한다 +- 큐(f1~fn) : 우선순위별로 큐가 하나씩 할당된다. 우선순위가 높으면 선택될 확률도 올라간다 +- 큐 선택기 : 임의 큐에서 처리할 URL을 꺼내는 역할을 담당, 순위가 높은 큐에서 더 자주 꺼내도록 프로그램 됨 + +

+ +아래 그림은 예의를 갖추고 URL사이의 우선순위와 신선도를 구별하는 크롤러를 구현한 예이다 +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FcT2P9I%2FbtrP981FZuu%2FQB2Ix9Ne78L8dcA6Or6uh1%2Fimg.jpg) -#### 신선도 +### 신선도 - 데이터의 신선함을 유지하기 위해서는 이미 다운로드한 페이지라고 해도 주기적으로 재수집할 필요가 있다. - 이 작업을 최적화하기 위해 웹 페이지의 변경 이력을 활용하거나 우선순위를 활용하여 중요한 페이지는 좀 더 자주 재수집한다 -### HTML 다운로더 +## HTML 다운로더 - HTTP 프로토콜을 통해 웹 페이지를 내려받는다 -#### 로봇 제외 프로토콜 +### 로봇 제외 프로토콜 - 웹사이트가 크롤러와 소통하는 표준적 방법 -- 크롤러가 수집해도 되는 웹 페이지 목록이 들어잇따 +- 크롤러가 수집해도 되는 웹 페이지 목록이 들어있다 - 웹 사이트를 긁어 가기 전에 크롤러는 해당 파일에 나열된 규칙을 먼저 확인해야한다 -#### 성능 최적화 +### 성능 최적화 다음은 HTML 다운로더에 사용할 수 있는 성능 최적화 기법들이다 1. 분산 크롤링 + +- 성능을 높이기 위해 크롤링 작업을 여러 서버에 분산 +- 각 서버는 여러 스레드를 돌려 다운로드 작업을 처리 +- URL 공간은 작은 단위로 분할하여, 각 서버는 그중 일부의 다운로드를 담당하도록 한다 + ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FJPUzp%2FbtrP9kn0iaD%2FBzKfUsNxGJKyEdCkmMYCu1%2Fimg.jpg) + 2. 도메인 이름 변환 결과 캐시 + +- 도메인 이름 변환기 : 크롤러 성능의 병목 중 하나, DNS 요청을 보내고 결과를 받는 작업의 동기적 특성 때문, DNS 요청 결과를 받기 전까지는 다음 작업을 진행 할 수 없다 +- 크롤러 스레드 가운데 어느 하나라도 이 작업을 하고 있으면 다른 스레드의 DNS 요청은 전부 블록 된다 +- DNS 조회 결과로 얻어진 도메인 이름과 IP주소 사이의 관계를 캐시에 보관해 놓고 크롭잡 등을 돌려 주기적으로 갱신해 성능 높임 + 3. 지역성 + +- 크롤링 작업을 수행하는 서버를 지역별로 분산하는 방법 +- 크롤링 서버가 크롤링 대상 서버와 지역적으로 가까우면 페이지 다운로드 시간은 줄어들 것 + 4. 짧은 타임아웃 +- 최대 얼마나 기다릴지 미리 정해두는 것 + ### 안정성 확보 전략 +최적화된 성능 뿐 아니라 안정성도 다운로더 설계시 중요하게 고려! + - 안정 해시 : 다운로더 서버들에 부하를 분산할 때 적용가능한 기술, 이 기술 이용하면 다운로더 서버를 쉽게 추가하고 삭제할 수 있다 - 크롤링 상태 및 수집 데이터 저장: 장애가 발생한 경우에도 쉽게 복구할 수 있도록 크롤링 상태와 수집된 데이터를 지속적으로 저장장치에 기록해 두는 것이 바람직 - 예외 처리 : 대규모 시스템에서 에러는 불가피할 뿐 아니라 흔하게 벌어지는 일이다. @@ -155,10 +196,23 @@ ### 확장성 확보 전략 +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2Fbpsn94%2FbtrQcNCtKIB%2FW5CoeemnDyMZH3UlifDKKK%2Fimg.jpg) + - 시스템을 설계할 때는 새로운 형태의 콘텐츠를 쉽게 지원할 수 있도록 신경써야한다. ### 문제 있는 콘텐츠 감지 및 회피 전략 1. 중복 콘텐츠 : 해시나 체크섬을 사용하여 중복 콘텐츠 탐지 -2. 거미 덫 : 크롤러를 무한 루프에 빠뜨리도록 설계한 웹 페이지. URL의 최대 길이를 제한하면 회피할 수 있다. 하지만 가능한 모든 종류으 덫을 피할 수 있는 만능 해결책은 없다 +2. 거미 덫 : 크롤러를 무한 루프에 빠뜨리도록 설계한 웹 페이지. URL의 최대 길이를 제한하면 회피할 수 있다. 하지만 가능한 모든 종류의 덫을 피할 수 있는 만능 해결책은 없다 3. 데이터 노이즈: 어떤 콘텐츠는 거의 가치가 없으므로 가능하다면 제외해야한다 + +## 4단계 마무리 + +### 추가로 논의해보면 좋을 점 + +- 서버 측 렌더링 +- 원치 않는 페이지 필터링 +- 데이터베이스 다중화 및 샤딩 +- 수평적 규모 확장성 +- 가용성, 일관성, 안정성 +- 데이터 분석 솔루션 From 4d4efc38e9ad1e5e4ca2d79b6b3bb6224d0fb185 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Tue, 8 Nov 2022 17:24:08 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[ADD]=2010=EC=9E=A5=20-=20=EC=9D=B4?= =?UTF-8?q?=EC=84=9C=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../A\354\241\260/empty" | 0 .../\354\235\264\354\204\234\354\232\260.md" | 143 ++++++++++++++++++ 2 files changed, 143 insertions(+) delete mode 100644 "10\354\243\274\354\260\250/A\354\241\260/empty" create mode 100644 "10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" diff --git "a/10\354\243\274\354\260\250/A\354\241\260/empty" "b/10\354\243\274\354\260\250/A\354\241\260/empty" deleted file mode 100644 index e69de29..0000000 diff --git "a/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" new file mode 100644 index 0000000..03e6d41 --- /dev/null +++ "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -0,0 +1,143 @@ +# < 10장 알림 시스템 설계 > + +알림시스템 (notification system)을 갖춘 애플리케이션 +
-> 최신뉴스, 제품 업데이트, 이벤트, 선물 등 고객에게 중요할 만한 정보를 비동기적으로 제공 + +# 알림 유형별 지원 방안 + +## ios 푸시 알림 + +![KakaoTalk_20221108_171617417](https://user-images.githubusercontent.com/78267146/200510953-ef73b776-7477-42cc-a25d-535b30377eb8.jpg) + +- ios에서 푸시 알림을 보내기 위해선 세 가지 컴포넌트가 필요 +- 알림 제공자(provider): 알림 요청(notification request)를 만들어 APNS로 보낸다. 알림 요청을 만들려면 (1)알림 요청을 보내는 데 필요한 고유 식별자인 "device token", (2) 알림 내용을 담은 JSON 딕셔너리인 "페이로드"가 필요 +- APNS(Apple Push Notification Service): 애플이 제공하는 원격 서비스. 푸시 알림을 iOS 장치로 보내는 역할 담당 +- ios 단말(ios device): 푸시 알림을 수신하는 사용자 단말 + +## 안드로이드 푸시 알림 + +![KakaoTalk_20221108_171617417_01](https://user-images.githubusercontent.com/78267146/200510990-69856bae-539b-4cbe-b2f1-f875a0f1db35.jpg) + +- APNS 대신 FCM(Firebase Cloud Messaging)을 사용한다는 점만 다르다 + +## SMS 메세지 + +![KakaoTalk_20221108_171617417_02](https://user-images.githubusercontent.com/78267146/200511238-192427ab-d6ba-4f80-bfd1-b4faf0b58949.jpg) + +- 보통 트윌리오, 넥스모 같은 제 3 사업자의 서비스를 많이 이용 + +## 이메일 + +![KakaoTalk_20221108_171617417_03](https://user-images.githubusercontent.com/78267146/200511250-c8010f02-81e1-4409-8294-9168c45d3aea.jpg) + +- 많은 회사가 센드그리드, 메일침프같은 상용 이메일 서비스를 이용 +- 대부분의 회사는 고유 이메일 서버를 구축할 역량은 갖추고 있지만, 상용 이메일 서버를 사용하는 것이 전송 성공률도 높고, 데이터 서비스도 제공 + +# 연락처 정보 수집 절차 + +![KakaoTalk_20221108_171617417_04](https://user-images.githubusercontent.com/78267146/200511521-141ab870-7060-4cd5-91cd-db229dc48aa2.jpg) + +- 알림 보내려면 모바일 단말 토큰, 전화번호, 이메일 주소 등의 정보가 필요 +- 앱을 설치하거나 처음으로 계정을 등록 -> API 서버는 해당 사용자의 정보를 수집하여 데이터베이스에 저장 +- 데이터베이스에 연락처 정보를 저장할 테이블 구조 ( 한 사용자가 여러 단말을 가질 수 있다) + ![KakaoTalk_20221108_171617417_05](https://user-images.githubusercontent.com/78267146/200511351-c82125a5-8822-4684-84f1-5775b88b4656.jpg) + +# 알림 전송 및 수신 절자 + +## 개략적 설계안 (초안) + +![KakaoTalk_20221108_172205888](https://user-images.githubusercontent.com/78267146/200512097-07b522b1-b362-4416-9724-12c432e8c2d9.jpg) + +- 1~N 까지의 서비스 : 이 서비스 각각은 마이크로서비스 or 크론잡 Or 분산시스템 컴포넌트일 수도 있다 ex) 사용자에게 납기일을 알리는 서비스, 배송알림 보내는 쇼핑몰 +- 알림 시스템(notification system) : 알림 전송/수신처리의 핵심. 1개 서버만 사용하는 시스템이라면 서비스 N개에 알림 전송을 위한 API 제공해야 함. 제 3자 서비스에 전달할 알림 페이로드를 만들어 낼 수 있어야 함 +- 제 3자 서비스 (third party service): 이 서비스들은 사용자에게 알림을 실제로 전달하는 역할. 고려할 점 (1)"확장성(쉽게 새로운 서비스를 통합하거나 기존 서비스를 제거할 수 있어햐)" (2) FCM이 중국에서 사용 불가능 한 것처럼 어떤 서비스는 다른 시장에서 사용 할 수 X + -ios, 안드로이드, SMS, 이메일 단말 : 사용자는 자기 단말에서 알림을 수신 + +## 이 설계의 문제점 + +- SPOF(Single-Point-Of-Failure): 알림 서버에 서버 1개 -> 그 서버에 장애 -> 전체 서비스의 장애 +- 규모 확장성: 한 대 서비스로 푸시알림 관련 다 처리 -> 컴포넌트 규모를 개별적으로 늘릴 방법이 X +- 성능병목 : 알림을 처리하고 보내는 것은 자원을 많이 필요로 하는 작업 -> 모든 것을 한 서버로 처리하면 사용자 트래픽이 많이 몰리는 시간에 시스템 과부하 상태 + +## 개략적 설계안 (개선된 버전) + +### 개선된 점 + +- 데이터베이스와 캐시를 알림 시스템 주 서버에서 분리 +- 알림 서버를 증설하고 자동으로 수평적 규모 확장 이루어질 수 있도록 +- 메세지 큐 이용 -> 시스템 컴포넌트 사이의 강한 결합 끊음 + ![KakaoTalk_20221108_171617417_07](https://user-images.githubusercontent.com/78267146/200511529-b4583977-db92-439a-bf76-036918414639.jpg) +- 1~N 서비스 : 알림 시스템 서버의 API를 통해 알림을 보낼 서비스들 +- 알림 서버 + - 알림 전송 API : 사내서비스 또는 인증된 클라이언트만 이용 가능 + - 알림 검증 : 이메일 주소, 전화번호 등에 대한 기본적 검증 수행 + - 데이터베이스 또는 캐시 질의 : 알림에 포함시킬 데이터를 가져오는 기능 + - 알림 전송 : 알림 데이터 -> 메세지 큐에 넣는다. 하나 이상의 메세지 큐 사용하면 알림 병렬처리 가능 +- 캐시 : 사용자 정보, 단말 정보, 일반 템플릿 등을 캐시한다 +- 데이터베이스 : 사용자, 알림,설정 등 다양한 정보를 저장한다 +- 메세지 큐 : 시스템 컴포넌트 간 의존성을 제거하기 위해 사용. 다량의 알림이 전송되어야 하는 경우를 대비한 버퍼 역할. 제 3자 서비스 가운데 하나에 장애 발생해도 다른 종류의 알림은 정상 동작 +- 작업 서버 : 메세지 큐 -> 제 3자 서비스로 알림 전달 + +### 알림 전송 과정 + +1. API 호출해 알림 서버로 알림 보냄 +2. 알림서버는 사용자 정보 등등을 캐시나 데이터베이스에서 가져온다 +3. 알림 서버는 전송할 알림에 맞는 이벤트 만들어 해당 이벤트를 위한 큐에 넣음. (ios 푸시 알림 이벤트는 ios 푸시알림 큐에 넣음) +4. 작업 서버 : 메세지 큐에서 알림 이벤트 꺼낸 후 제 3 자 서비스로 보냄 +5. 제 3자 서비스 : 사용자 단말로 알림 전송 + +# 안정성 + +분산 환경에서 운영될 알림 시스템 설계 -> 안정성을 확보하기 위한 사항 몇가지를 반드시 고려해야 한다 + +## 데이터 손실 방지 + +- 알림 전송 시스템의 가장 중요한 요구사항 중 하나는 알림이 절대 소실되면 안된다는 것 +- 따라서 알림 데이터를 데이터베이스에 보관하고 재시도 메커니즘을 구현해야 한다 -> 알림 로그 데이터베이스를 유지하는 것이 한가지 방법 + +## 알림 중복 전송 방지 + +- 중복 완전 방지는 불가능. 중복 빈도를 줄여야한다 +- 보내야 할 알림이 도착하면 그 이벤트ID를 검사해 이전에 본 적 있는 이벤트인지 살핀다 + +# 추가로 필요한 컴포넌트 및 고려사항 + +## 알림 템플릿 + +- 알림 메세지는 대부분 형시기이 비슷하므로 알림 템플릿은 알림 메세지의 모든 부분을 처음부터 다시 만들 수 없도록 해 준다. +- 인자나 추적링크를 조정하기만 하면 사전에 지정한 형식에 맞춰 알림을 만들어 낼 수 있다 + +## 알림 설정 + +- 많은 웹사이트와 앱에서는 사용자가 알림설정을 상세히 조정할 수 있도록 한다 +- 이 정보는 알림 설정 테이블에 보관된다 + - user id, channel(알림이 전송될 채널: 푸시, 이메일 등), opt_in(해당 채널로 알림을 받을지 여부) + +## 전송률 제한 + +- 한 사용자가 받을 수 있는 알림의 빈도를 제한 + +## 재시도 방법 + +- 제 3자 서비스가 알림 전송에 실패하면 해당 알림을 재시도 전용 큐에 넣는다 + +## 푸시 알림과 보안 + +- ios, 안드로이드 : appKey아 appSecret을 사용해 보안 유지 -> 인증되거나 승인된 클라이언트만 해당 API 이용해서 알림 보낼 수 있음 + +## 큐 모니터링 + +- 큐에 쌓인 알림의 개수가 너무 크면 작업 서버들이 이벤트를 빠르게 처리하고 있지 못하다는 뜻 -> 작업 서버를 증설하는게 바람직 + +## 이벤트 추적 + +- 데이터 분석 서비스는 보통 이벤트 추적 기능도 제공함 -> 보통 알림 시스템을 만들면 데이터 분석 서비스와도 통합해야만 한다 + +# 수정된 설계안 + +![KakaoTalk_20221108_171617417_08](https://user-images.githubusercontent.com/78267146/200511537-0e9ddeb1-2f32-4531-b454-ef0b7a1e9496.jpg) + +- 알림 서버에 인증, 전송률 제한 기능이 추가 +- 전송 실패에 대응하기 위한 재시도 기능 추가. 전송 실패한 알림을 다시 큐에 넣고 지정된 횟수만큼 재시도 +- 전송 템플릿을 사용 -> 알림 생성 과정을 단순화하고 알림 내용의 일관성 유지 +- 모니터링 & 추적 시스템 추가 -> 시스템 상태를 확인하고 추후 시스템을 개선하기 쉽도록 함 From a13ac3e80f22e936ae9e7a39c29a4cd99ed461a6 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Tue, 8 Nov 2022 17:44:35 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[FIX]=20=ED=8C=8C=EC=9D=BC=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\235\264\354\204\234\354\232\260.md" | 103 ++++++++ .../\354\235\264\354\204\234\354\232\260.md" | 137 +++++----- .../\354\235\264\354\204\234\354\232\260.md" | 157 ++++++------ .../\354\235\264\354\204\234\354\232\260.md" | 148 +++-------- .../B\354\241\260/empty" | 0 .../\354\235\264\354\204\234\354\232\260.md" | 240 +++++++++++++++--- .../A\354\241\260/empty" | 0 .../\354\235\264\354\204\234\354\232\260.md" | 0 .../\354\235\264\354\204\234\354\232\260.md" | 218 ---------------- 9 files changed, 503 insertions(+), 500 deletions(-) delete mode 100644 "6\354\243\274\354\260\250/B\354\241\260/empty" delete mode 100644 "8\354\243\274\354\260\250/A\354\241\260/empty" rename "10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" => "8\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" (100%) delete mode 100644 "9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" diff --git "a/3\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/3\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index 78d6e02..ab719fd 100644 --- "a/3\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/3\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -66,3 +66,106 @@ - 2단계: 개략적 설계안 제시 및 동의 구하기 - 10분~15분 - 3단계: 상세 설계 - 10분~25분 - 4단계: 마무리 - 3분~5분 + +

+ +# <4장 처리율 제한 장치의 설계> + +### 처리율 제한 장치 : 클라이언트 또는 서비스가 보내는 트래픽의 처리율(rate)을 제어하기 위한 장치 + +Ex) HTTP를 예로 들면, 이 장치는 특정 기간 내에 전송되는 클라이언트의 요청 횟수를 제한한다. API 요청 횟수가 제한 장치에 정의된 임계치를 넘어서면 추가 호출은 모두 처리가 중단된다 +
+ +### 처리율 제한 장치를 두면 좋은 점 + +- DoS 공격에 의한 자원 고갈 방지 +- 비용 절감 : 추가 요청에 대한 처리를 제한하면 서버를 많이 두지 않아도 되고 우선순위가 높은 API 에 더 많은 자원을 할당할 수 있다 +- 서버 과부하 방지 + +## 1단계 문제 이해 및 설계 범위 확정 + +- 처리율 제한 장치를 구현한느 데는 여러가지 알고리즘을 사용할 수 있는데, 각각은 고유한 장단점을 갖고 있다. 면접관과 소통하며 어떤 제한 장치를 구현해야 하는지 분명히 할 수 있다 + +## 2단계 개략적 설계안 제시 및 동의 구하기 + +### 처리율 제한 장치는 어디에 둘 것인가? + +- 클라이언트 측에 둔다면 : 일반적으로 클라이언트는 쉽게 위변조가 가능해 처리율 제한을 안정적으로 걸 수 있는 장소가 못 된다. +- 서버 측에 둔다면 +- 처리율 제한 미들웨어로 API 서버로 가는 요청을 통제 ex) 클라우드 마이크로서비스 API 게이트웨이 + +### 처리율 제한 알고리즘 + +처리율 제한을 실현하는 알고리즘은 여러 가지인데, 각기 다른 장단점을 갖고 있다. + +#### 토큰 버킷 알고리즘 + +- 처리율 제한에 폭넓게 이용되고 있다. 간단하고 보편적으로 사용된다. 아마존과 스트라이프가 API 요청을 통제하기 위해 이 알고리즘을 사용한다 +- 장점 : 구현이 쉽고, 메모리 사용 측면에서도 효율적이다. 짧은 시간에 집중된느 트래픽도 처리 가능하다. 버킷에 남은 토큰이 있기만 하면 요청은 시스템에 전달될 것이다 +- 단점 : 이 알고리즘은 버킷 크기와 토큰 공급률이라는 두 개 인자를 가지고 있는데, 이 값을 적절하게 튜닝하는 것은 까다로운 일이 된다 + +#### 누출 버킷 알고리즘 + +- 토큰 버킷 알고리즘과 비슷하지만 요청 처리율이 고정되어 있다는 점이 다르다. 보통 FIFO 큐로 구현한다 +- 토큰 버킷은 지정된 용량을 갖는 컨데이너로 버킷에는 사전 설정된 양의 토큰이 주기적으로 채워진다. 토큰이 꽉 찬 버킷에는 더 이상의 토큰은 추가되지 않는다. 각 요청은 처리될 때마다 하나의 토큰을 사용한다. 요청이 도착하면 버킷에 충분한 토큰이 있는지 검사하게 된다 +- 장점 : 큐의 크기가 제한되어 있어 메모리 사용량 측면에서 효율적, 고정된 처리율을 갖고 있기 때문에 안정적 출력이 필요한 경우 적합 +- 단점: 단시간에 많은 트래픽이 몰리는 경우 큐에는 오래된 요청들이 쌓이게 되고, 그 요청들을 제때 처리 못하면 최신 요청들은 버려짐 + +#### 고정 윈도 카운터 알고리즘 + +- 타임라인을 고정된 간격의 윈도로 나누고, 각 윈도마다 카운터를 붙인다 -> 요청이 접수될 때마다 이 카운터의 값은 1씩 증가한다 -> 이 카운터의 값이 사전에 설정된 임계치에 도달하면 새로운 요청은 새 윈도가 열릴 때까지 버려진다 +- 장점 : 메모리 효율 좋고, 이해하기 쉬우며 윈도가 닫히는 시점에 카운터를 초기화하는 방식은 특정한 트래픽 패턴을 처리하기에 적합하다 +- 단점: 윈도 경계 부근에서 일시적으로 많은 트래픽이 몰려드는 경우는 기대했던 시스템의 처리 한도보다 많은 양의 요청을 처리하게 된다 + +#### 이동 윈도 로깅 알고리즘 + +- 고정 윈도 카운터 알고리즘의 단점 해결 +- 요청의 타임스탬프를 추적한다. 타임스탬프 데이터는 보통 레디스의 정렬 집합 같은 캐시에 보관한다 +- 새 요청이 오면 만료된 타임스탬프는 제거한다 +- 새 요청의 타임스탬프를 로그에 추가한다 +- 로그의 크기가 허용치보다 같거나 작으면 요청을 시스템에 전달한다. 그렇지 않은 경우에 처리를 거부한다 +- 장점 : 처리율 제한 메커니즘이 아주 정교해 허용되는 요청의 개수는 시스템의 처리율 한도를 넘지 않는다 +- 단점 : 다량의 메모리를 사용 + +#### 이동 윈도 카운터 알고리즘 + +- 고정 윈도 카운터 알고리즘 + 이동 윈도 로깅 알고리즘 +- 장점 : 이전 시간대의 평균 처리율에 따라 현재 윈도의 상태를 계산하므로 짧은 시간에 몰리는 트래픽에도 잘 대응한다, 메모리 효율이 좋다 +- 단점 : 직전 시간대에 도착한 요청이 균등하게 분포되어 있다고 가정한 상태에서 추정치 계산하기 때문에 다소 느슨하다 + +### 개략적인 아키텍처 + +- 처리율 제한 알고리즘 : 얼마나 많은 요청이 접수되었는지를 추적할 수 있는 카운터를 추적 대상별로 두고이 카운터의 값이 어떤 한도를 넘어서면 한도를 넘어선 요청은 거부 +- 카운터는 캐시에 보관 +- 클라이언트가 처리율 제한 미들웨어에게 요청 -> 처리율 제한 미들웨어는 레디스의 지정 버킷에서 카운터를 가져와서 한도에 도달했는지 아닌지를 검사 + +## 3단계 상세 설계 + +처리율 제한 규칙은 어떻게 만들어지고 어디에 저장되는지, 처리가 제한된 요청들은 어떻게 처리되는지 정한다 + +### 처리율 제한 규칙 + +리프트(Lyft)는 처리율 제한에 오픈 소스를 사용하고 있다. + +### 처리율 한도 초과 트래픽의 처리 + +어떤 요청이 한도 제한에 걸리면 API는 HTTP 429응답을 클라이언트에게 보낸다 + +### 상세 설계 + +- 처리율 제한 규칙은 디스크에 보관, 작업 프로세스는 수시로 규칙을 디스크에서 읽어 캐시에 저장 +- 클라이언트가 요청을 서버로 보내면 요청은 먼저 처리율 제한 미들웨어에 도달 +- 미들웨어는 제한 규칙을 캐시에서 가져온다. 카운터 및 마지막 요청의 타임스탬프를 레디스 캐시에서 가져온다. 가져온 값들에 근거하여 해당 요청이 처리율 제한에 걸리지 않은 경우에는 API 서버로 보내고, 처리율 제한에 걸렸다면 429 에러를 클라리언트에 보낸다 + +### 분산 환경에서 처리율 제한 장치의 구현 + +단일 서버를 지원하는 처리율 제한 장치를 구현하는 것은 어렵지 않지만 여러 대의 서버와 병렬 스레드를 지원하도록 시스템을 확장하는 것은 "경쟁 조건","동기화" 문제를 풀어야한다 + +### 성능 최적화 + +- 사용자 트래픽을 가장 가까운 에지서버로 전달하여 지연시간을 줄일 수 있다 +- 제한 장치 간에 데이터를 동기화 할 때 최종 일관성 모델을 사용 + +### 모니터링 + +- 처리율 제한 장치를 설치한 이후에는 효과적으로 동작하고 있는지 보기 위해 데이터를 모을 필요가 있다. 모니터링을 통해 확인하려는 것은 채택된 처리율 제한 알고리즘이 효과적인지, 정의한 처리율 제한 규칙이 효과적인지이다. diff --git "a/4\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/4\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index 3b5dadb..bfd2576 100644 --- "a/4\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/4\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -1,100 +1,113 @@ -# <4장 처리율 제한 장치의 설계> +# < 5장 안정 해시 설계 > -### 처리율 제한 장치 : 클라이언트 또는 서비스가 보내는 트래픽의 처리율(rate)을 제어하기 위한 장치 +안정 해시 : 수평적 규모 확장성을 달성하기 위해서 요청 또는 데이터를 서버에 균등하게 나누는 것이 중요한데 이 목표를 달성하기 위해 보편적으로 사용하는 기술 -Ex) HTTP를 예로 들면, 이 장치는 특정 기간 내에 전송되는 클라이언트의 요청 횟수를 제한한다. API 요청 횟수가 제한 장치에 정의된 임계치를 넘어서면 추가 호출은 모두 처리가 중단된다 +

+ +## 해시 키 재배치(refresh) 문제 + +N개의 캐시 서버가 있을 때 이 서버들에 부하를 균등하게 나누는 보편 방법은 아래의 해시 함수를 사용하는 것 + +- serverIndex=hash(key) % N (N은 서버의 개수) + +특정한 키가 보관된 서버를 알아내기 위해, 나머지 연산을 f(key) % 4와 같이 적용하였다. 예를 들어 hash(key0) % 4 = 1 이면, 클라이언트는 캐시에 보관된 데이터를 가져오기 위해 서버 1에 접속해야한다
+-> 서버 풀(server pool)의 크기가 고정되어 있을 때, 그리고 데이터 분포가 균등할 때는 잘 동작한다 +
+-> 서버가 추가되거나 기존 서버가 삭제되면 문제가 생긴다 +
+EX) 1번 서버가 장애를 일으켜 동작을 중단했을 때, 서버 풀의 크기는 3으로 변하고 키에 대한 해시 값은 변하지 않지만 나머지 연산을 적용하여 계산한 서버 인덱스 값은 달라진다 -> 장애가 발생한 1번 서버에 보관되어 있는 키 뿐만 아니라 대부분의 키가 재분배되며 대부분 캐시 클라이언트가 데이터가 없는 엉뚱한 서버에 접속하게 된다 = 대규모 캐시미스 (cache miss) +
+

-### 처리율 제한 장치를 두면 좋은 점 +## 안정 해시 -- DoS 공격에 의한 자원 고갈 방지 -- 비용 절감 : 추가 요청에 대한 처리를 제한하면 서버를 많이 두지 않아도 되고 우선순위가 높은 API 에 더 많은 자원을 할당할 수 있다 -- 서버 과부하 방지 +해시 테이블 크기가 조정될 때 평균적으로 오직 k/n개(k는 키의 개수이고 n은 슬롯(slot)의 개수)의 키만 재배치하는 해시 기술. +
-## 1단계 문제 이해 및 설계 범위 확정 +- c.f)대부분의 전통적 해시 테이블은 슬롯의 수가 바뀌면 거의 대부분 키를 재배치한다 -- 처리율 제한 장치를 구현한느 데는 여러가지 알고리즘을 사용할 수 있는데, 각각은 고유한 장단점을 갖고 있다. 면접관과 소통하며 어떤 제한 장치를 구현해야 하는지 분명히 할 수 있다 +

-## 2단계 개략적 설계안 제시 및 동의 구하기 +### 해시 공간과 해시 링 -### 처리율 제한 장치는 어디에 둘 것인가? +- 해시 함수 f로는 SHA-1을 사용 +- SHA-1의 출력 값의 범위는 x0, x1, x2, ... xn +- SHA-1의 해시 공간 범위는 0 ~ 2의 160승 - 1 +- 해시 공간의 양쪽을 구부려 접으면 해시 링(hash ring)이 만들어진다 -- 클라이언트 측에 둔다면 : 일반적으로 클라이언트는 쉽게 위변조가 가능해 처리율 제한을 안정적으로 걸 수 있는 장소가 못 된다. -- 서버 측에 둔다면 -- 처리율 제한 미들웨어로 API 서버로 가는 요청을 통제 ex) 클라우드 마이크로서비스 API 게이트웨이 +

-### 처리율 제한 알고리즘 +### 해시 서버 -처리율 제한을 실현하는 알고리즘은 여러 가지인데, 각기 다른 장단점을 갖고 있다. +- 해시 함수 f를 사용하면 서버 IP나 이름을 이 링의 어떤 위치에 대응시킬 수 있다. -#### 토큰 버킷 알고리즘 +

-- 처리율 제한에 폭넓게 이용되고 있다. 간단하고 보편적으로 사용된다. 아마존과 스트라이프가 API 요청을 통제하기 위해 이 알고리즘을 사용한다 -- 장점 : 구현이 쉽고, 메모리 사용 측면에서도 효율적이다. 짧은 시간에 집중된느 트래픽도 처리 가능하다. 버킷에 남은 토큰이 있기만 하면 요청은 시스템에 전달될 것이다 -- 단점 : 이 알고리즘은 버킷 크기와 토큰 공급률이라는 두 개 인자를 가지고 있는데, 이 값을 적절하게 튜닝하는 것은 까다로운 일이 된다 +### 해시 키 -#### 누출 버킷 알고리즘 +- 여기 사용된 해시 함수는 해시 키 재배치 문제에 언급된 함수와는 다르며 나머지 연산은 사용하고 있지X +- 캐시할 키 key0, key1, key2, key3 또한 해시 링 위의 어느 지점에 배치할 수 있다 -- 토큰 버킷 알고리즘과 비슷하지만 요청 처리율이 고정되어 있다는 점이 다르다. 보통 FIFO 큐로 구현한다 -- 토큰 버킷은 지정된 용량을 갖는 컨데이너로 버킷에는 사전 설정된 양의 토큰이 주기적으로 채워진다. 토큰이 꽉 찬 버킷에는 더 이상의 토큰은 추가되지 않는다. 각 요청은 처리될 때마다 하나의 토큰을 사용한다. 요청이 도착하면 버킷에 충분한 토큰이 있는지 검사하게 된다 -- 장점 : 큐의 크기가 제한되어 있어 메모리 사용량 측면에서 효율적, 고정된 처리율을 갖고 있기 때문에 안정적 출력이 필요한 경우 적합 -- 단점: 단시간에 많은 트래픽이 몰리는 경우 큐에는 오래된 요청들이 쌓이게 되고, 그 요청들을 제때 처리 못하면 최신 요청들은 버려짐 +

-#### 고정 윈도 카운터 알고리즘 +### 서버 조회 -- 타임라인을 고정된 간격의 윈도로 나누고, 각 윈도마다 카운터를 붙인다 -> 요청이 접수될 때마다 이 카운터의 값은 1씩 증가한다 -> 이 카운터의 값이 사전에 설정된 임계치에 도달하면 새로운 요청은 새 윈도가 열릴 때까지 버려진다 -- 장점 : 메모리 효율 좋고, 이해하기 쉬우며 윈도가 닫히는 시점에 카운터를 초기화하는 방식은 특정한 트래픽 패턴을 처리하기에 적합하다 -- 단점: 윈도 경계 부근에서 일시적으로 많은 트래픽이 몰려드는 경우는 기대했던 시스템의 처리 한도보다 많은 양의 요청을 처리하게 된다 +- 어떤 키가 저장되는 서버는, 해당 키의 위치로부터 시계 방향으로 링을 탐색해 나가면서 만나는 첫 번째 서버다 +- key0는 서버0에 저장되고, key1은 서버 1에 저장되며, key2는 서버2, key3는 서버3에 저장된다 + ![](https://blog.kakaocdn.net/dn/KbWxa/btrNb33EoZt/dZGRHUsCDUThwuRDpK0N8k/img.jpg) -#### 이동 윈도 로깅 알고리즘 +

-- 고정 윈도 카운터 알고리즘의 단점 해결 -- 요청의 타임스탬프를 추적한다. 타임스탬프 데이터는 보통 레디스의 정렬 집합 같은 캐시에 보관한다 -- 새 요청이 오면 만료된 타임스탬프는 제거한다 -- 새 요청의 타임스탬프를 로그에 추가한다 -- 로그의 크기가 허용치보다 같거나 작으면 요청을 시스템에 전달한다. 그렇지 않은 경우에 처리를 거부한다 -- 장점 : 처리율 제한 메커니즘이 아주 정교해 허용되는 요청의 개수는 시스템의 처리율 한도를 넘지 않는다 -- 단점 : 다량의 메모리를 사용 +### 서버 추가 -#### 이동 윈도 카운터 알고리즘 +- 서버를 추가하더라도 키 가운데 일부만 재배치하면 된다 +- 서버 4가 추가된 뒤에 key0만 재배치 되고 key1,2,3는 같은 서버에 남는다. key0의 위치에서 시계 방향으로 순회했을 때 처음으로 만나게 되는 서버가 서버 4이기 때문이다. + ![](https://blog.kakaocdn.net/dn/Piq64/btrM98LqcfE/xaApcvjIjt2pOoSXQGq5aK/img.jpg) -- 고정 윈도 카운터 알고리즘 + 이동 윈도 로깅 알고리즘 -- 장점 : 이전 시간대의 평균 처리율에 따라 현재 윈도의 상태를 계산하므로 짧은 시간에 몰리는 트래픽에도 잘 대응한다, 메모리 효율이 좋다 -- 단점 : 직전 시간대에 도착한 요청이 균등하게 분포되어 있다고 가정한 상태에서 추정치 계산하기 때문에 다소 느슨하다 +

-### 개략적인 아키텍처 +### 서버 제거 -- 처리율 제한 알고리즘 : 얼마나 많은 요청이 접수되었는지를 추적할 수 있는 카운터를 추적 대상별로 두고이 카운터의 값이 어떤 한도를 넘어서면 한도를 넘어선 요청은 거부 -- 카운터는 캐시에 보관 -- 클라이언트가 처리율 제한 미들웨어에게 요청 -> 처리율 제한 미들웨어는 레디스의 지정 버킷에서 카운터를 가져와서 한도에 도달했는지 아닌지를 검사 +- 하나의 서버가 제거되면 키 가운데 일부만 재배치된다 +- 서버 1이 삭제되었을 때 key1만이 서버 2로 재배치 된다. 나머지 키에는 영향 X + ![](https://blog.kakaocdn.net/dn/bnAWH5/btrNdsg3WfK/4yjKBTOTcmehjQXdwPiMb0/img.jpg) -## 3단계 상세 설계 +### 기본 구현법의 두 가지 문제 -처리율 제한 규칙은 어떻게 만들어지고 어디에 저장되는지, 처리가 제한된 요청들은 어떻게 처리되는지 정한다 +- 안정 해시 알고리즘의 기본 절차
+ (1) 서버와 키를 균등 분포 해시 함수를 사용해 해시 링에 배치한다 +
(2) 키의 위치에서 링을 시계 방향으로 탐색하다 만나는 최초의 서버 키가 저장될 서버다 -### 처리율 제한 규칙 +- 문제점 +
(1) 서버가 추가되거나 삭제되는 상황을 감안하면 파티션(인접한 서버 사이의 해시 공간) 크기를 균등하게 유지하는 것이 불가능하다. 어떤 서버는 굉장히 작은 해시 공간을 할당받고, 어떤 서버는 굉장히 큰 해시 공간을 할당 받는 상황이 가능하다. -리프트(Lyft)는 처리율 제한에 오픈 소스를 사용하고 있다. +![](https://blog.kakaocdn.net/dn/NtMvZ/btrNbXCBv2W/H5k3CKuvWvzgGV7NgxLNB1/img.jpg) -### 처리율 한도 초과 트래픽의 처리 +(2) 키의 균등 분포를 달성하기 어렵다. 서버 1,3은 아무 데이터도 갖지 않는 반면, 대부분의 키는 서버2에 보관될 것이다 +![](https://blog.kakaocdn.net/dn/sC6tG/btrNdpR9VB1/ZYt03V4D9ZeHnv3ucRJ9L0/img.jpg) -어떤 요청이 한도 제한에 걸리면 API는 HTTP 429응답을 클라이언트에게 보낸다 +- 이 문제를 해결하기 위해 제안된 기법이 "가상노드(virtual node)" 또는 "복제(replica)" -### 상세 설계 +

-- 처리율 제한 규칙은 디스크에 보관, 작업 프로세스는 수시로 규칙을 디스크에서 읽어 캐시에 저장 -- 클라이언트가 요청을 서버로 보내면 요청은 먼저 처리율 제한 미들웨어에 도달 -- 미들웨어는 제한 규칙을 캐시에서 가져온다. 카운터 및 마지막 요청의 타임스탬프를 레디스 캐시에서 가져온다. 가져온 값들에 근거하여 해당 요청이 처리율 제한에 걸리지 않은 경우에는 API 서버로 보내고, 처리율 제한에 걸렸다면 429 에러를 클라리언트에 보낸다 +### 가상 노드 -### 분산 환경에서 처리율 제한 장치의 구현 +- 실제 노드 또는 서버를 가리키는 노드로서, 하나의 서버는 링 위에 여러 개의 가상 노드를 가질 수 있다 +- 예를 들어, 서버 0을 링에 배치하기 위해 s0 하나만 쓰는 대신 s0_0, s0_1, s0_2 세 개의 가상 노드를 사용하고, 서버 1을 링에 배치하기 위해 S1_0,S1_1,S1_2 세 개의 가상 노드를 사용했다 ( 각 서버는 여러 개의 파티션을 관리해야 한다) -> 키의 위치로부터 시계 방향으로 링을 탐색하다 만나는 최초의 가상 노드가 해당 키가 저장될 서버가 된다 +- 가상 노드의 개수를 늘려면 키의 분포는 점점 더 균등해진다. 표준 편차가 작아져서 데이터가 고르게 분포되기 때문이다. But, 가상 노드를 저장할 데이터 공간은 더 많이 필요하게 되므로 타협적 결정이 필요하다 + ![](https://blog.kakaocdn.net/dn/WL0CS/btrNdRATkbG/0VelnF5grkWWihedojnOB1/img.jpg) -단일 서버를 지원하는 처리율 제한 장치를 구현하는 것은 어렵지 않지만 여러 대의 서버와 병렬 스레드를 지원하도록 시스템을 확장하는 것은 "경쟁 조건","동기화" 문제를 풀어야한다 +### 재배치할 키 결정 -### 성능 최적화 +- 서버가 추가되거나 제거되면 데이터 일부는 재배치 해야한다 +- 서버 4가 추가되었으면 서버3 부터 서버4 사이에 있는 키들은 S0에서 S4로 재배치된다 + ![](https://blog.kakaocdn.net/dn/dPF2sh/btrM8NAJIZV/I65jUVvTdLwKrFESibotMK/img.jpg) -- 사용자 트래픽을 가장 가까운 에지서버로 전달하여 지연시간을 줄일 수 있다 -- 제한 장치 간에 데이터를 동기화 할 때 최종 일관성 모델을 사용 +## 마치며 -### 모니터링 +### 안정해시의 이점 -- 처리율 제한 장치를 설치한 이후에는 효과적으로 동작하고 있는지 보기 위해 데이터를 모을 필요가 있다. 모니터링을 통해 확인하려는 것은 채택된 처리율 제한 알고리즘이 효과적인지, 정의한 처리율 제한 규칙이 효과적인지이다. +- 서버가 추가되거나 삭제될 때 재배치되는 키의 수가 최소화된다 +- 데이터가 보다 균등하게 분포하게 되므로 수평적 규모 확장성을 달성하기 쉽다 +- 핫스팟(hotspot) 키 문제를 줄인다. 특정한 샤드에 대한 접근이 지나치게 빈번하면 서버 과부하 문제가 생길 수 있다. 안정 해시는 데이터를 좀 더 균등하게 분배하므로 이런 문제가 생길 가능성을 줄인다 diff --git "a/5\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/5\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index bfd2576..32e4499 100644 --- "a/5\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/5\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -1,113 +1,126 @@ -# < 5장 안정 해시 설계 > +# < 6장 키-값 저장소(키-값 데이터베이스) 설계> -안정 해시 : 수평적 규모 확장성을 달성하기 위해서 요청 또는 데이터를 서버에 균등하게 나누는 것이 중요한데 이 목표를 달성하기 위해 보편적으로 사용하는 기술 +- 비 관계형 데이터베이스. +- 키는 유일해야 한다 +- 키에 매달린 값은 키를 통해서만 접근할 수 있다 +- 키 : 일반 텍스트나 해시 값일 수도 있고, 짧을수록 좋다 +- 값 : 문자열 OR 리스트 OR 객체 등등, 무엇이 오든 상관하지 않는다 +- ex) 아마존 다이나모, memcached, 레디스 등 +- put(key,value) : 키-값 쌍을 저장소에 저장한다 +- get(key) : 인자로 주어진 키에 매달린 값 꺼낸다 -

+
-## 해시 키 재배치(refresh) 문제 +# 단일 서버 키-값 저장소 -N개의 캐시 서버가 있을 때 이 서버들에 부하를 균등하게 나누는 보편 방법은 아래의 해시 함수를 사용하는 것 +- 키-값 쌍 전부를 메모리에 해시 테이블로 저장 +
-> 빠른 속도 보장 But, 모든 데이터를 메모리 안에 두는 것이 불가능 할 수 있다 +
-> 개선 : 데이터 압축 / 자주 쓰이는 데이터만 메모리에 두기 +
-> 그래도 한 대 서버로는 부족하여 "분산 키-값 저장소" 필요 +

-- serverIndex=hash(key) % N (N은 서버의 개수) +# 분산 키-값 저장소 = 분산 해시 테이블 -특정한 키가 보관된 서버를 알아내기 위해, 나머지 연산을 f(key) % 4와 같이 적용하였다. 예를 들어 hash(key0) % 4 = 1 이면, 클라이언트는 캐시에 보관된 데이터를 가져오기 위해 서버 1에 접속해야한다 -
--> 서버 풀(server pool)의 크기가 고정되어 있을 때, 그리고 데이터 분포가 균등할 때는 잘 동작한다 -
--> 서버가 추가되거나 기존 서버가 삭제되면 문제가 생긴다 -
-EX) 1번 서버가 장애를 일으켜 동작을 중단했을 때, 서버 풀의 크기는 3으로 변하고 키에 대한 해시 값은 변하지 않지만 나머지 연산을 적용하여 계산한 서버 인덱스 값은 달라진다 -> 장애가 발생한 1번 서버에 보관되어 있는 키 뿐만 아니라 대부분의 키가 재분배되며 대부분 캐시 클라이언트가 데이터가 없는 엉뚱한 서버에 접속하게 된다 = 대규모 캐시미스 (cache miss) -
-

+- 키-값 쌍을 여러 서버에 분산 -## 안정 해시 +## CAP 정리 -해시 테이블 크기가 조정될 때 평균적으로 오직 k/n개(k는 키의 개수이고 n은 슬롯(slot)의 개수)의 키만 재배치하는 해시 기술. -
+데이터 일관성, 가용성, 파티션 감내라는 세 가지 요구사항을 동시에 만족하는 분산 시스템을 설계하는 것은 불가능 +
= 어떤 두 가지를 충족하려면 나머지 하나는 반드시 희생돼야 한다 -- c.f)대부분의 전통적 해시 테이블은 슬롯의 수가 바뀌면 거의 대부분 키를 재배치한다 +- 데이터 일관성 : 분산 시스템에 접속하는 모든 클라이언트는 어떤 노드에 접속했느냐에 관계없이 언제나 같은 데이터를 보게 되어야 한다 +- 가용성 : 분산 시스템에 접속하는 클라이언트는 일부 노드에 장애가 발생하더라도 항상 응답을 받을 수 있어야 한다 +- 파티션 감내 : 두 노드 사이에 통신 장애가 발생하였음에도 시스템은 계속 동작해야 한다 +
+
-

+- CP 시스템 : 일관성 & 파티션 감내를 지원, 가용성 희생 +- AP 시스템 : 가용성 & 파티션 감내를 지원, 데이터 일관성 희생 +- CA 시스템 : 존재하지 X ( <= 네트워크 장애는 피할 수 없는 일로 여겨지므로 분산 시스템은 반드시 파티션 문제를 감내할 수 있게 설계돼야 함) -### 해시 공간과 해시 링 +=> 분산 키-값 저장소를 만들 때는 그 요구사항에 맞도록 CAP 정리를 적용해야한다 -- 해시 함수 f로는 SHA-1을 사용 -- SHA-1의 출력 값의 범위는 x0, x1, x2, ... xn -- SHA-1의 해시 공간 범위는 0 ~ 2의 160승 - 1 -- 해시 공간의 양쪽을 구부려 접으면 해시 링(hash ring)이 만들어진다 +
-

+## 시스템 컴포넌트 -### 해시 서버 +### 데이터 파티션 -- 해시 함수 f를 사용하면 서버 IP나 이름을 이 링의 어떤 위치에 대응시킬 수 있다. +대규모 애플리케이션의 경우 전체 데이터를 한 대 서버에 욱여넣는 것은 불가능 +
->데이터를 작은 파티션들로 분할한 다음 여러 대 서버에 저장 +
-> 이때, 데이터를 여러 서버에 고르게 분산 가능한지, 노드가 추가되거나 삭제될 때 데이터의 이동을 최소화 할 수 있는지 따져봐야한다 +
-> 안정 해시 사용 (5장 참고)

-### 해시 키 +### 데이터 다중화 -- 여기 사용된 해시 함수는 해시 키 재배치 문제에 언급된 함수와는 다르며 나머지 연산은 사용하고 있지X -- 캐시할 키 key0, key1, key2, key3 또한 해시 링 위의 어느 지점에 배치할 수 있다 +- 높은 가용성과 안정성을 확보하기 위해서는 데이터를 N(튜닝 가능한 값)개 서버에 비동기적으로 다중화할 필요가 있다 +- N개 서버를 선정하는 방버 : 어떤 키를 해시 링 위에 배치 후, 그 지점으로부터 시계 방향으로 링을 순회하면서 만나는 첫 N개의 서버에 데이터 사본을 보관 +- 가상 노드를 사용한다면 선택한 N개의 노드가 대응될 실제 물리 서버의 개수가 N보다 작아질 수 있으므로 같은 물리서버를 중복 선택하지 않도록 해야한다

-### 서버 조회 - -- 어떤 키가 저장되는 서버는, 해당 키의 위치로부터 시계 방향으로 링을 탐색해 나가면서 만나는 첫 번째 서버다 -- key0는 서버0에 저장되고, key1은 서버 1에 저장되며, key2는 서버2, key3는 서버3에 저장된다 - ![](https://blog.kakaocdn.net/dn/KbWxa/btrNb33EoZt/dZGRHUsCDUThwuRDpK0N8k/img.jpg) - -

+### 데이터 일관성 -### 서버 추가 +- 여러 노드에 다중화된 데이터는 적절히 동기화가 되어야한다 +- 정족수 합의 프로토콜 사용 -> 읽기/쓰기 연산 모두에 일관성 보장 +- N = 사본 개수 +- W = 쓰기 연산에 대한 정족수, 쓰기 연산이 성공한 것으로 간주되려면 적어도 W개의 서버로부터 쓰기 연산이 성공했다는 응답을 받아야한다 +- R = 읽기 연산에 대한 정족수, 읽기 연산이 성공한 것으로 간주되려면 적어도 R개의 서버로부터 응답을 받아야한다 +

+ 요구되는 일관성 수준에 따라 W,R,N의 값을 정하면 된다 +- R=1, W=N : 빠른 읽기 연산에 최적화된 시스템 +- W=1, R=N : 빠른 쓰기 연산에 최적화된 시스템 +- W+R > N : 강한 일관성이 보장됨 +- W+R <= N : 강한 일관성이 보장되지 않음 -- 서버를 추가하더라도 키 가운데 일부만 재배치하면 된다 -- 서버 4가 추가된 뒤에 key0만 재배치 되고 key1,2,3는 같은 서버에 남는다. key0의 위치에서 시계 방향으로 순회했을 때 처음으로 만나게 되는 서버가 서버 4이기 때문이다. - ![](https://blog.kakaocdn.net/dn/Piq64/btrM98LqcfE/xaApcvjIjt2pOoSXQGq5aK/img.jpg) +
-

+#### 일관성 모델 -### 서버 제거 +- 강한 일관성 : 모든 읽기 연산은 가장 최근에 갱신된 결과를 반환한다. 클라이언트는 절대 낡은 데이터를 보지 못한다 => 새로운 요청의 처리가 중단되기 때문에 고가용성 시스템에는 적합하지 않다 +- 약한 일관성 : 읽기 연산은 가장 최근에 갱신된 결과를 반환하지 못 할 수 있다 +- 최종 일관성 : 약한 일관성의 한 형태로, 갱신 결과가 결국에는 모든 사본에 반영되는 모델 => 쓰기 연산이 병렬적으로 발생하면 시스템에 저장된 일관성이 깨질 수 있는데 이는 클라이언트가 해결해야 한다 -- 하나의 서버가 제거되면 키 가운데 일부만 재배치된다 -- 서버 1이 삭제되었을 때 key1만이 서버 2로 재배치 된다. 나머지 키에는 영향 X - ![](https://blog.kakaocdn.net/dn/bnAWH5/btrNdsg3WfK/4yjKBTOTcmehjQXdwPiMb0/img.jpg) +### 비 일관성 해소 기법 : 데이터 버저닝 -### 기본 구현법의 두 가지 문제 +- 데이터를 다중화하면 가용성은 높아지지만 사본 간 일관성이 깨질 가능성은 높아지므로 버저닝과 벡터 시계 사용! +- 버저닝 : 데이터를 변경할 때마다 해당 데이터의 새로운 버전을 만드는 것, 각 버전의" 데이터는 변경 불가능 -- 안정 해시 알고리즘의 기본 절차
- (1) 서버와 키를 균등 분포 해시 함수를 사용해 해시 링에 배치한다 -
(2) 키의 위치에서 링을 시계 방향으로 탐색하다 만나는 최초의 서버 키가 저장될 서버다 +### 장애 처리 -- 문제점 -
(1) 서버가 추가되거나 삭제되는 상황을 감안하면 파티션(인접한 서버 사이의 해시 공간) 크기를 균등하게 유지하는 것이 불가능하다. 어떤 서버는 굉장히 작은 해시 공간을 할당받고, 어떤 서버는 굉장히 큰 해시 공간을 할당 받는 상황이 가능하다. +#### 장애 감지 -![](https://blog.kakaocdn.net/dn/NtMvZ/btrNbXCBv2W/H5k3CKuvWvzgGV7NgxLNB1/img.jpg) +- 보통 두 대 이상의 서버가 똑같이 서버 A의 장애를 보고해야 해당 서버에 실제로 장애가 발생했다고 간주 +- 가십 프로토콜 같은 분산형 장애 감지 솔루션을 채택하는 편이 효율적 +- 가십 프로토콜 : 각 노드는 멤버십 목록을 유지하고 주기적으로 자신의 박동 카운터를 증가시킨다 -> 각 노드는 무작위로 선정된 노드들에게 주기적으로 자기 박동 카운터 목록을 보낸다 -> 박동 카운터 목록을 받은 노드는 멤버십 목록을 최신 값으로 갱신하는데 어떤 멤버의 박동 카운터 값이 지정된 시간동안 갱신되지 않으면 해당 멤버는 장애상태로 간주 -(2) 키의 균등 분포를 달성하기 어렵다. 서버 1,3은 아무 데이터도 갖지 않는 반면, 대부분의 키는 서버2에 보관될 것이다 -![](https://blog.kakaocdn.net/dn/sC6tG/btrNdpR9VB1/ZYt03V4D9ZeHnv3ucRJ9L0/img.jpg) +#### 일시적 장애 처리 -- 이 문제를 해결하기 위해 제안된 기법이 "가상노드(virtual node)" 또는 "복제(replica)" +쓰기 연산을 수행할 W개의 건강한 서버와 읽기 연산을 수행할 R개의 건강한 서버를 해시링에서 고른다 +
-> 장애 상태인 서버로 가는 요청은 다른 서버가 잠시 맡아 처리하고 해당 서버가 복구되었을 때 일괄 반영하여 데이터 일관성을 보존한다 +
-> 이를 위해 임시로 쓰기 연산ㅇ르 처리한 서버에는 그에 관한 단서를 남겨둔다 ( 단서 후 임시 위탁 기법 ) -

+#### 영구 장애 처리 -### 가상 노드 +사본 간의 일관성이 망가진 상태를 탐지하고 전송 데이터의 양을 줄이기 위해서 "머클 트리" 사용 -- 실제 노드 또는 서버를 가리키는 노드로서, 하나의 서버는 링 위에 여러 개의 가상 노드를 가질 수 있다 -- 예를 들어, 서버 0을 링에 배치하기 위해 s0 하나만 쓰는 대신 s0_0, s0_1, s0_2 세 개의 가상 노드를 사용하고, 서버 1을 링에 배치하기 위해 S1_0,S1_1,S1_2 세 개의 가상 노드를 사용했다 ( 각 서버는 여러 개의 파티션을 관리해야 한다) -> 키의 위치로부터 시계 방향으로 링을 탐색하다 만나는 최초의 가상 노드가 해당 키가 저장될 서버가 된다 -- 가상 노드의 개수를 늘려면 키의 분포는 점점 더 균등해진다. 표준 편차가 작아져서 데이터가 고르게 분포되기 때문이다. But, 가상 노드를 저장할 데이터 공간은 더 많이 필요하게 되므로 타협적 결정이 필요하다 - ![](https://blog.kakaocdn.net/dn/WL0CS/btrNdRATkbG/0VelnF5grkWWihedojnOB1/img.jpg) +머클트리 = 해시트리 -### 재배치할 키 결정 +- 각 노드에 그 자식 노드들에 보관된 값의 해시 또는 자식 노드들의 레이블로부터 계산된 해시값을 레이블로 붙여두는 트리 +- 해시 트리를 사용하면 대규모 자료 구조의 내용을 효과적이면서도 보안상 안전한 방법으로 검증할 수 있다 -- 서버가 추가되거나 제거되면 데이터 일부는 재배치 해야한다 -- 서버 4가 추가되었으면 서버3 부터 서버4 사이에 있는 키들은 S0에서 S4로 재배치된다 - ![](https://blog.kakaocdn.net/dn/dPF2sh/btrM8NAJIZV/I65jUVvTdLwKrFESibotMK/img.jpg) +### 데이터 센터 장애 처리 -## 마치며 +- 정전, 네트워크장애, 자연재해 등 다양한 이유로 발생할 수 있다 +- 데이터를 여러 데이터 센터에 다중화하는 것이 중요 -### 안정해시의 이점 +### 시스템 아키텍쳐 다이어그램 -- 서버가 추가되거나 삭제될 때 재배치되는 키의 수가 최소화된다 -- 데이터가 보다 균등하게 분포하게 되므로 수평적 규모 확장성을 달성하기 쉽다 -- 핫스팟(hotspot) 키 문제를 줄인다. 특정한 샤드에 대한 접근이 지나치게 빈번하면 서버 과부하 문제가 생길 수 있다. 안정 해시는 데이터를 좀 더 균등하게 분배하므로 이런 문제가 생길 가능성을 줄인다 +- 클라이언트는 키-값 저장소가 제공하는 두 가지 단순한 API (get, put)과 통신한다 +- 중재자는 클라이언트에게 키-값 저장소에 대한 프락시 역할을 하는 노드다 +- 노드는 안정 해시의 해시 링 위에 분포한다 +- 노드를 자동으로 추가, 삭제할 수 있도록 시스템은 완전히 분산된다 +- 데이터는 여러 노드에 다중화된다 +- 모든 노드는 클라이언트 API, 장애감지, 데이터 충돌 해소, 다중화 등등 전부를 지원해야 한다 diff --git "a/6\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/6\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index 32e4499..f51e51e 100644 --- "a/6\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/6\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -1,126 +1,62 @@ -# < 6장 키-값 저장소(키-값 데이터베이스) 설계> +# <7장 분산 시스템을 위한 유일 ID 생성기 설계> -- 비 관계형 데이터베이스. -- 키는 유일해야 한다 -- 키에 매달린 값은 키를 통해서만 접근할 수 있다 -- 키 : 일반 텍스트나 해시 값일 수도 있고, 짧을수록 좋다 -- 값 : 문자열 OR 리스트 OR 객체 등등, 무엇이 오든 상관하지 않는다 -- ex) 아마존 다이나모, memcached, 레디스 등 -- put(key,value) : 키-값 쌍을 저장소에 저장한다 -- get(key) : 인자로 주어진 키에 매달린 값 꺼낸다 +## 1. 문제 이해 및 설계 범휘 확정 -
+- ID는 유일해야 함 +- ID는 숫자로만 구성돼야 함 +- ID는 64비트로 표현될 수 있는 값이어야 함 +- ID는 발급 날짜에 따라 정렬 가능해야 함 +- 초당 10000개의 ID를 만들 수 있어야 함 -# 단일 서버 키-값 저장소 +## 2. 개략적 설계안 제시 및 동의 구하기 -- 키-값 쌍 전부를 메모리에 해시 테이블로 저장 -
-> 빠른 속도 보장 But, 모든 데이터를 메모리 안에 두는 것이 불가능 할 수 있다 -
-> 개선 : 데이터 압축 / 자주 쓰이는 데이터만 메모리에 두기 -
-> 그래도 한 대 서버로는 부족하여 "분산 키-값 저장소" 필요 -

+분산 시스템에서 유일성이 보장되는 ID를 만드는 방법은 여러가지다 -# 분산 키-값 저장소 = 분산 해시 테이블 +### 다중 마스터 복제 -- 키-값 쌍을 여러 서버에 분산 +- 데이터베이스의 auto_increament 기능 활용 +- But, 다음 ID 값을 구할 때 1만큼 증가 X, 현재 사용 중인 데이터 베이스 서버의 수만큼 증가시킨다 +- 문제점 : 여러 데이터 센터에 결처 규모 늘리기 어렵 / ID값이 시간의 흐름에 맞추어 커지도록 보장 불가 / 서버를 추가하거나 삭제할 때도 잘 동작하도록 만들기 어렵 -## CAP 정리 +### UUID -데이터 일관성, 가용성, 파티션 감내라는 세 가지 요구사항을 동시에 만족하는 분산 시스템을 설계하는 것은 불가능 -
= 어떤 두 가지를 충족하려면 나머지 하나는 반드시 희생돼야 한다 +- 컴퓨터 시스템에 저장된느 정보를 유일하게 식별하기 위햔 128bit 수 +- 충돌 가능성이 지극히 낮다 +- 장점 : 서버 사이의 조율이 필요없으므로 동기화 이슈도 없고 서버가 자기가 쓸 ID를 알아서 만드는 구조이므로 규모 확장도 쉽다 +- 단점 : ID가 128비트로 길고 시간 순으로 정렬할 수 없으며, 숫자 아닌 값이 포함될 수 있다 -- 데이터 일관성 : 분산 시스템에 접속하는 모든 클라이언트는 어떤 노드에 접속했느냐에 관계없이 언제나 같은 데이터를 보게 되어야 한다 -- 가용성 : 분산 시스템에 접속하는 클라이언트는 일부 노드에 장애가 발생하더라도 항상 응답을 받을 수 있어야 한다 -- 파티션 감내 : 두 노드 사이에 통신 장애가 발생하였음에도 시스템은 계속 동작해야 한다 -
-
+### 티켓 서버 -- CP 시스템 : 일관성 & 파티션 감내를 지원, 가용성 희생 -- AP 시스템 : 가용성 & 파티션 감내를 지원, 데이터 일관성 희생 -- CA 시스템 : 존재하지 X ( <= 네트워크 장애는 피할 수 없는 일로 여겨지므로 분산 시스템은 반드시 파티션 문제를 감내할 수 있게 설계돼야 함) +- auto_createment 기능을 갖춘 데이터베이스 서버, 즉 티켓 서버를 중앙 집중형으로 하나만 사용하는 것 +- 장점 : 유일성이 보장되고 숫자로만 구성된 ID, 구현하기 쉽다 +- 단점 : 티켓 서버가 SPOF가 돼 이 서버에 장애가 발생하면 해당 서버를 이용하는 모든 시스템이 영향 받는다 -=> 분산 키-값 저장소를 만들 때는 그 요구사항에 맞도록 CAP 정리를 적용해야한다 +### 트위터 스노플레아크 접근법 -
+- 생성해야 하는 ID의 구조를 여러 절로 분할 +- 사인 비트 : 1비트를 할당, 음수와 양수 구분 +- 타임스탬ㅁ프 : 41비트 할당, 기원 시간 이후 몇 밀리초 경과했는지 나타내는 값 +- 데이터 센터 ID : 5비트 할당, 따라서 32개 데이터센터를 지원 +- 서버 ID : 5비트를 할당, 따라서 데이터 센터당 32개 서버를 사용 +- 일련번호 : 12비트를 할당, 각 서버에서는 ID를 생성할 때마다 이 일련번호를 1만큼 증가시킨다. 이 값은 1 밀리초가 경과할 때마다 0으로 초기화된다 -## 시스템 컴포넌트 +## 3. 상세 설계 -### 데이터 파티션 +- 트위터 스노플레이크 접근법을 사용 +- 데이터 센터 ID와 서버 ID 는 시스템이 시작할 때 결정 +- 타임스탬프나 일련번호는 ID 생성기가 돌고 있는 중에 만들어지는 값 -대규모 애플리케이션의 경우 전체 데이터를 한 대 서버에 욱여넣는 것은 불가능 -
->데이터를 작은 파티션들로 분할한 다음 여러 대 서버에 저장 -
-> 이때, 데이터를 여러 서버에 고르게 분산 가능한지, 노드가 추가되거나 삭제될 때 데이터의 이동을 최소화 할 수 있는지 따져봐야한다 -
-> 안정 해시 사용 (5장 참고) +### 타임스탬프 -

+- 시간의 흐름에 따라 점점 더 큰 값 가지므로 결국 ID는 시간 순으로 정렬 가능하게 될 것이다 +- 41비트로 표현할 수 있는 타임스탬프 최대값은 2의 41승 -1 밀리 초 = 69년에 해당한다. 이 ID 생성기는 69년 동안만 정상 동작하므로 69년이 지나면 기원 시각을 바꾸거나 ID 체계를 다른 것으로 이전하여야 한다. -### 데이터 다중화 +### 일련번호 -- 높은 가용성과 안정성을 확보하기 위해서는 데이터를 N(튜닝 가능한 값)개 서버에 비동기적으로 다중화할 필요가 있다 -- N개 서버를 선정하는 방버 : 어떤 키를 해시 링 위에 배치 후, 그 지점으로부터 시계 방향으로 링을 순회하면서 만나는 첫 N개의 서버에 데이터 사본을 보관 -- 가상 노드를 사용한다면 선택한 N개의 노드가 대응될 실제 물리 서버의 개수가 N보다 작아질 수 있으므로 같은 물리서버를 중복 선택하지 않도록 해야한다 +- 일련번호는 12비트이므로 4096개의 값 가질 수 있다 +- 서버가 같은 밀리초 동안 하나 이상의 ID를 만들어낸 경우에만 0보다 큰 값 가진다 -

+## 4. 마무리 -### 데이터 일관성 - -- 여러 노드에 다중화된 데이터는 적절히 동기화가 되어야한다 -- 정족수 합의 프로토콜 사용 -> 읽기/쓰기 연산 모두에 일관성 보장 -- N = 사본 개수 -- W = 쓰기 연산에 대한 정족수, 쓰기 연산이 성공한 것으로 간주되려면 적어도 W개의 서버로부터 쓰기 연산이 성공했다는 응답을 받아야한다 -- R = 읽기 연산에 대한 정족수, 읽기 연산이 성공한 것으로 간주되려면 적어도 R개의 서버로부터 응답을 받아야한다 -

- 요구되는 일관성 수준에 따라 W,R,N의 값을 정하면 된다 -- R=1, W=N : 빠른 읽기 연산에 최적화된 시스템 -- W=1, R=N : 빠른 쓰기 연산에 최적화된 시스템 -- W+R > N : 강한 일관성이 보장됨 -- W+R <= N : 강한 일관성이 보장되지 않음 - -
- -#### 일관성 모델 - -- 강한 일관성 : 모든 읽기 연산은 가장 최근에 갱신된 결과를 반환한다. 클라이언트는 절대 낡은 데이터를 보지 못한다 => 새로운 요청의 처리가 중단되기 때문에 고가용성 시스템에는 적합하지 않다 -- 약한 일관성 : 읽기 연산은 가장 최근에 갱신된 결과를 반환하지 못 할 수 있다 -- 최종 일관성 : 약한 일관성의 한 형태로, 갱신 결과가 결국에는 모든 사본에 반영되는 모델 => 쓰기 연산이 병렬적으로 발생하면 시스템에 저장된 일관성이 깨질 수 있는데 이는 클라이언트가 해결해야 한다 - -### 비 일관성 해소 기법 : 데이터 버저닝 - -- 데이터를 다중화하면 가용성은 높아지지만 사본 간 일관성이 깨질 가능성은 높아지므로 버저닝과 벡터 시계 사용! -- 버저닝 : 데이터를 변경할 때마다 해당 데이터의 새로운 버전을 만드는 것, 각 버전의" 데이터는 변경 불가능 - -### 장애 처리 - -#### 장애 감지 - -- 보통 두 대 이상의 서버가 똑같이 서버 A의 장애를 보고해야 해당 서버에 실제로 장애가 발생했다고 간주 -- 가십 프로토콜 같은 분산형 장애 감지 솔루션을 채택하는 편이 효율적 -- 가십 프로토콜 : 각 노드는 멤버십 목록을 유지하고 주기적으로 자신의 박동 카운터를 증가시킨다 -> 각 노드는 무작위로 선정된 노드들에게 주기적으로 자기 박동 카운터 목록을 보낸다 -> 박동 카운터 목록을 받은 노드는 멤버십 목록을 최신 값으로 갱신하는데 어떤 멤버의 박동 카운터 값이 지정된 시간동안 갱신되지 않으면 해당 멤버는 장애상태로 간주 - -#### 일시적 장애 처리 - -쓰기 연산을 수행할 W개의 건강한 서버와 읽기 연산을 수행할 R개의 건강한 서버를 해시링에서 고른다 -
-> 장애 상태인 서버로 가는 요청은 다른 서버가 잠시 맡아 처리하고 해당 서버가 복구되었을 때 일괄 반영하여 데이터 일관성을 보존한다 -
-> 이를 위해 임시로 쓰기 연산ㅇ르 처리한 서버에는 그에 관한 단서를 남겨둔다 ( 단서 후 임시 위탁 기법 ) - -#### 영구 장애 처리 - -사본 간의 일관성이 망가진 상태를 탐지하고 전송 데이터의 양을 줄이기 위해서 "머클 트리" 사용 - -머클트리 = 해시트리 - -- 각 노드에 그 자식 노드들에 보관된 값의 해시 또는 자식 노드들의 레이블로부터 계산된 해시값을 레이블로 붙여두는 트리 -- 해시 트리를 사용하면 대규모 자료 구조의 내용을 효과적이면서도 보안상 안전한 방법으로 검증할 수 있다 - -### 데이터 센터 장애 처리 - -- 정전, 네트워크장애, 자연재해 등 다양한 이유로 발생할 수 있다 -- 데이터를 여러 데이터 센터에 다중화하는 것이 중요 - -### 시스템 아키텍쳐 다이어그램 - -- 클라이언트는 키-값 저장소가 제공하는 두 가지 단순한 API (get, put)과 통신한다 -- 중재자는 클라이언트에게 키-값 저장소에 대한 프락시 역할을 하는 노드다 -- 노드는 안정 해시의 해시 링 위에 분포한다 -- 노드를 자동으로 추가, 삭제할 수 있도록 시스템은 완전히 분산된다 -- 데이터는 여러 노드에 다중화된다 -- 모든 노드는 클라이언트 API, 장애감지, 데이터 충돌 해소, 다중화 등등 전부를 지원해야 한다 +- 우리는 유일성이 보장되는 ID 생성기 구현에 쓰일 수 있는 전략 4가지를 살펴 봄 +
->모든 요구 사항을 만족하면서도 분산 환경에서 규모 확장이 가능했던 스노우플레이크 방식을 선택하였다 diff --git "a/6\354\243\274\354\260\250/B\354\241\260/empty" "b/6\354\243\274\354\260\250/B\354\241\260/empty" deleted file mode 100644 index e69de29..0000000 diff --git "a/7\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/7\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index f51e51e..38ed114 100644 --- "a/7\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/7\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -1,62 +1,218 @@ -# <7장 분산 시스템을 위한 유일 ID 생성기 설계> +# 9장 웹 크롤러 설계 -## 1. 문제 이해 및 설계 범휘 확정 +## 웹 크롤러 (= 로봇, 스파이더) -- ID는 유일해야 함 -- ID는 숫자로만 구성돼야 함 -- ID는 64비트로 표현될 수 있는 값이어야 함 -- ID는 발급 날짜에 따라 정렬 가능해야 함 -- 초당 10000개의 ID를 만들 수 있어야 함 +- 검색 엔진에서 널리 쓰는 기술 +- 목적 : 웹에 새로 올라오거나 갱신된 콘텐츠(웹 페이지, 이미지나 비디오 등)를 찾아내는 것 +- 웹 페이지 몇 개에서 시작하여 그 링크를 따라 나가면서 새로운 콘텐츠를 수집한다 -## 2. 개략적 설계안 제시 및 동의 구하기 +## 웹 크롤러 이용 -분산 시스템에서 유일성이 보장되는 ID를 만드는 방법은 여러가지다 +- 검색 엔진 인덱싱 : 크롤러의 가장 보편적인 용례 +- 웹 아카이빙 : 나중에 사용할 목적으로 장기보관하기 위해 웹에서 정보를 모으는 절차. 많은 국립 도서관이 크롤러로 웹 사이트 아카이빙 중 +- 웹 마이닝 : 인터넷에서 유용한 지식을 도출 +- 웹 모니터링 : 크롤러를 사용하면 인터넷에서 저작권이나 상표권이 침해되는 사례를 모니터링 할 수 있다 -### 다중 마스터 복제 +## 1단계 문제 이해 및 설계 범위 확정 -- 데이터베이스의 auto_increament 기능 활용 -- But, 다음 ID 값을 구할 때 1만큼 증가 X, 현재 사용 중인 데이터 베이스 서버의 수만큼 증가시킨다 -- 문제점 : 여러 데이터 센터에 결처 규모 늘리기 어렵 / ID값이 시간의 흐름에 맞추어 커지도록 보장 불가 / 서버를 추가하거나 삭제할 때도 잘 동작하도록 만들기 어렵 +### 웹 크롤러의 기본 알고리즘 -### UUID +1. URL 집합이 입력으로 주어지면, 해당 URL 들이 가리키는 모든 웹 페이지를 다운로드 한다 +2. 다운받은 웹 페이지에서 URL 들을 추출한다 +3. 추출된 URL들을 다운로드 할 URL 목록에 추가하고 위의 과정을 처음부터 반복한다 -- 컴퓨터 시스템에 저장된느 정보를 유일하게 식별하기 위햔 128bit 수 -- 충돌 가능성이 지극히 낮다 -- 장점 : 서버 사이의 조율이 필요없으므로 동기화 이슈도 없고 서버가 자기가 쓸 ID를 알아서 만드는 구조이므로 규모 확장도 쉽다 -- 단점 : ID가 128비트로 길고 시간 순으로 정렬할 수 없으며, 숫자 아닌 값이 포함될 수 있다 +### 좋은 웹 크롤러가 만족시켜야 할 속성 -### 티켓 서버 +- 규모 확장성 : 웹은 거대하고 수십억 개의 페이지가 존재하므로 병행성을 활용하면 효과적 +- 안정성 : 웹은 함정으로 가득하다. 크롤러를 비정상적인 입력이나 환경에 잘 대응할 수 있어야한다 +- 예절 : 크롤러는 수집 대상 웹 사이트에 짧은 시간동안 너무 많은 요청을 보내서는 안 된다 +- 확장성 : 새로운 형태의 콘텐츠를 지원하기가 쉬워야 한다. -- auto_createment 기능을 갖춘 데이터베이스 서버, 즉 티켓 서버를 중앙 집중형으로 하나만 사용하는 것 -- 장점 : 유일성이 보장되고 숫자로만 구성된 ID, 구현하기 쉽다 -- 단점 : 티켓 서버가 SPOF가 돼 이 서버에 장애가 발생하면 해당 서버를 이용하는 모든 시스템이 영향 받는다 +## 2단계 개략적 설계안 제시 및 동의 구하기 -### 트위터 스노플레아크 접근법 +![](https://k.kakaocdn.net/dn/rP55W/btrP8WOAZn0/CI9aK0urjFwBn0YFjVWkxk/img.jpg) -- 생성해야 하는 ID의 구조를 여러 절로 분할 -- 사인 비트 : 1비트를 할당, 음수와 양수 구분 -- 타임스탬ㅁ프 : 41비트 할당, 기원 시간 이후 몇 밀리초 경과했는지 나타내는 값 -- 데이터 센터 ID : 5비트 할당, 따라서 32개 데이터센터를 지원 -- 서버 ID : 5비트를 할당, 따라서 데이터 센터당 32개 서버를 사용 -- 일련번호 : 12비트를 할당, 각 서버에서는 ID를 생성할 때마다 이 일련번호를 1만큼 증가시킨다. 이 값은 1 밀리초가 경과할 때마다 0으로 초기화된다 +### 시작 URL 집합 -## 3. 상세 설계 +- 웹 크롤러가 크롤링을 시작하는 출발점 + ex) 어떤 대학 웹 사이트로부터 찾아 나갈 수 있는 모든 웹 페이지를 크롤링하는 가장 직관적인 방법은 해당 대학의 도메인 이름이 붙은 모든 페이지의 URL을 시작URL로 쓰는 것 +- 크롤러가 가능한 한 많은 링크를 탐색할 수 있도록 하는 URL을 고르는 것이 바람직 +- 전체 URL공간을 작은 부분집합으로 나누는 전략을 사용(나라별로 인기 있는 웹 사이트가 다르다는 점에서 착안) +- 주제별로 다른 시작 URL을 사용 (쇼핑, 스포츠, 건강 등등의 주제별로 세분화하고 그 각각에 다른 시작 URL) +- 시작 URL을 무엇을 쓸 것이냐는 질문에 정답은 없다 -- 트위터 스노플레이크 접근법을 사용 -- 데이터 센터 ID와 서버 ID 는 시스템이 시작할 때 결정 -- 타임스탬프나 일련번호는 ID 생성기가 돌고 있는 중에 만들어지는 값 +## 미수집 URL 저장소 -### 타임스탬프 +- 대부분의 현대적 웹 크롤러는 크롤링 상태를 다운로드할 URL, 다운로드 된 URL의 두가지로 나눠 관리한다. +- 이 중 다운로드 할 URL을 저장 관리하는 컴포넌트를 미수집 URL 저장소라 부른다 -- 시간의 흐름에 따라 점점 더 큰 값 가지므로 결국 ID는 시간 순으로 정렬 가능하게 될 것이다 -- 41비트로 표현할 수 있는 타임스탬프 최대값은 2의 41승 -1 밀리 초 = 69년에 해당한다. 이 ID 생성기는 69년 동안만 정상 동작하므로 69년이 지나면 기원 시각을 바꾸거나 ID 체계를 다른 것으로 이전하여야 한다. +### HTML 다운로더 -### 일련번호 +- 인터넷에서 웹 페이지를 다운로드하는 컴포넌트 +- 다운로드할 페이지 URL은 미수집 URL저장소가 제공 -- 일련번호는 12비트이므로 4096개의 값 가질 수 있다 -- 서버가 같은 밀리초 동안 하나 이상의 ID를 만들어낸 경우에만 0보다 큰 값 가진다 +### 도메인 이름 변환기 -## 4. 마무리 +- URL -> IP주소 +- HTML 다운로더는 도메인 이름 변환기를 사용하여 URL에 대응되는 IP주소를 알아낸다 -- 우리는 유일성이 보장되는 ID 생성기 구현에 쓰일 수 있는 전략 4가지를 살펴 봄 -
->모든 요구 사항을 만족하면서도 분산 환경에서 규모 확장이 가능했던 스노우플레이크 방식을 선택하였다 +### 콘텐츠 파서 + +- 웹페이지를 다운로드하면 파싱과 검증 과정을 거쳐야한다. 이상한 웹 페이지는 문제를 일으킬 수 있다 +- 크롤링 서버 안에 구현하면 크롤링 과정이 느려지게 되므로 독립된 컴포넌트로 만들었다 + +### 중복 콘텐츠인가? + +- 같은 콘텐츠 여러번 저장하게 되는 것 방지 +- 효과적인 방법은 웹 페이지의 해시 값 비교 + +### 콘텐츠 저장소 + +- HTML 문서를 보관하는 시스템 +- 저장할 데이터의 유형, 크기, 저장소 접근 빈도, 데이터의 유효기간 등을 종합적으로 고려해야 한다 +- ex) 데이터 양이 많으므로 대부분 콘텐츠는 "디스크"에 저장, 인기 있는 콘텐츠는 "메모리"에 두어 접근 지연시간 줄임 + +### URL 추출기 + +- HTML 페이지를 파싱하여 링크들을 골라냄 + +### URL 필터 + +- 특정한 콘텐츠 타입이나 파일 확장자를 갖는 URL, 접속 시 오류 발생하는 URL 등 을 크롤링 대상에서 배제하는 역할 + +### 이미 방문한 URL? + +- 이미 방문한 URL이나 미수집 URL 저장소에 보관된 URL을 추적할 수 있도록 하는 자료 구조 (볼륨필터나 해시 테이블..) 사용 +- 같은 URL을 여러번 처리하는 일 방지하여 서버 부하를 줄이고 시스템이 무한 루프에 빠지는 일 방지 + +### URL 저장소 + +- 이미 방문한 URL 을 보관하는 저장소 + +### 웹 크롤러 작업 흐름 + +1. 시작 URL 들을 미수집 URL 저장소에 저장 +2. HTML 다운로더는 미수집 URL 저장소에서 URL 목록 가져옴 +3. HTML 다운로더는 도메인 이름 변환기를 사용하여 URL의 IP 주소르 알아내고, 해당 IP 주소로 접속하여 웹 페이지를 다운 받음 +4. 콘텐츠 파서는 다운된 HTML 페이지를 파싱하여 올바른 형식을 갖춘 페이지인지 검증 +5. 콘텐츠 파싱과 검증이 끝나면 중복 콘텐츠인지 확인하는 절차 개시 +6. 중복 콘텐츠인지 확인하기 위해서, 해당 페이지가 이미 저장소에 있는지 본다 ( 이미 저장소에 있 : 처리하지 않고 버림 / 없 : 저장소에 저장한 뒤 URL 추출기로 전달) +7. URL 추출기는 해당 HTML 페이지에서 링크를 골라낸다 +8. 골라낸 링크를 URL 필터로 전달한다 +9. 필터링이 끝나고 남은 URL 만 중복 URL 판별 단계로 전달 +10. 이미 처리한 URL인지 확인하기 위해 ,URL저장소에서 보관된 URL인지 살핀다. 이미 저장소에 있는 URL은 버린다 +11. 저장소에 없는 URL은 URL 저장소에 저장할 뿐 아니라 미수집 URL 저장소에도 전달한다 + +## 3단계 상세 설계 + +### < DFS VS BFS> + +- 웹 크롤러는 보통 BFS 사용. 큐의 한쪽으로는 탐색할 URL을 집어넣고, 다른 한쪽으로는 꺼내기만 한다 +- 문제 1: 한 페이지에서 나오는 링크의 상당 수는 같은 서버로 되돌아간다. 크롤러는 같은 호스트에 속한 많은 링크를 다운받느라 바빠지게 되는데, 이때 링크들을 병렬로 처리하게 된다면 서버 과부하(예의 없는 크롤러) +- 문제 2 : BFS알고리즘은 우선순위를 두지 않지만 모든 웹 페이지가 같은 수준의 품질, 중요성을 갖지 않는다. + +### <미수집 URL 저장소> + +- 위의 문제 해결 방앉 +- 이 저장소를 잘 구현하여 예의를 갖춘 크롤러, URL 사이의 우선순위와 신선도를 구별하는 크롤러를 구현할 수 있다. + +### 예의 + +- 동일 웹 사이트에 대해서는 한 번에 한 페이지만 요청 +- 같은 웹 사이트의 페이지를 다운받는 태스크는 시간차를 두고 실행하도록 하면 된다 +- 웹 사이트의 호스트명과 다운로드를 수행하는 작업스레드 사이의 관계를 유지하면된다. 각 다운로드는 스레드 별로 FIFO큐를 가지고 있어서, 해당 큐에서 꺼낸 URL만 다운로드 한다 + +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbviJv0%2FbtrP9AdeSdU%2F02iEO9ybfOzTbT4MuODXJk%2Fimg.jpg) + +- 큐 라우터 : 같은 호스트에 속한 URL은 언제나 같은 큐로 가도록 보장 +- 매핑 테이블 : 호스트 이름과 큐 사이의 관계를 보관하는 테이블 +- FIFO 큐(b1~bn) : 같은 호스트에 속한 URL은 언제나 같은 큐에 보관 +- 큐 선택기 : 큐 선택기는 큐들을 순회하면서 큐에서 URL을 꺼내서 해당 큐에서 나온 URL을 다운로드하도록 지정된 작업 스레드에 전달하는 역할 +- 작업 스레드 : 전달된 URL을 다운로드하는 작업을 수행 + +### 우선순위 + +- 페이지랭크, 트래픽 양, 갱신 빈도 등 다양한 척도를 사용하여 URL 우선순위를 나눔 +- 순위결정장치는 URL 우선순위를 결정하는 컴포넌트 + ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbKe803%2FbtrQdzKSGSZ%2FYU9hIZzzz0NncK2rEulFN1%2Fimg.jpg) +- 순위결정장치 : URL을 입력으로 받아 우선순위를 계산한다 +- 큐(f1~fn) : 우선순위별로 큐가 하나씩 할당된다. 우선순위가 높으면 선택될 확률도 올라간다 +- 큐 선택기 : 임의 큐에서 처리할 URL을 꺼내는 역할을 담당, 순위가 높은 큐에서 더 자주 꺼내도록 프로그램 됨 + +

+ +아래 그림은 예의를 갖추고 URL사이의 우선순위와 신선도를 구별하는 크롤러를 구현한 예이다 +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FcT2P9I%2FbtrP981FZuu%2FQB2Ix9Ne78L8dcA6Or6uh1%2Fimg.jpg) + +### 신선도 + +- 데이터의 신선함을 유지하기 위해서는 이미 다운로드한 페이지라고 해도 주기적으로 재수집할 필요가 있다. +- 이 작업을 최적화하기 위해 웹 페이지의 변경 이력을 활용하거나 우선순위를 활용하여 중요한 페이지는 좀 더 자주 재수집한다 + +## HTML 다운로더 + +- HTTP 프로토콜을 통해 웹 페이지를 내려받는다 + +### 로봇 제외 프로토콜 + +- 웹사이트가 크롤러와 소통하는 표준적 방법 +- 크롤러가 수집해도 되는 웹 페이지 목록이 들어있다 +- 웹 사이트를 긁어 가기 전에 크롤러는 해당 파일에 나열된 규칙을 먼저 확인해야한다 + +### 성능 최적화 + +다음은 HTML 다운로더에 사용할 수 있는 성능 최적화 기법들이다 + +1. 분산 크롤링 + +- 성능을 높이기 위해 크롤링 작업을 여러 서버에 분산 +- 각 서버는 여러 스레드를 돌려 다운로드 작업을 처리 +- URL 공간은 작은 단위로 분할하여, 각 서버는 그중 일부의 다운로드를 담당하도록 한다 + ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FJPUzp%2FbtrP9kn0iaD%2FBzKfUsNxGJKyEdCkmMYCu1%2Fimg.jpg) + +2. 도메인 이름 변환 결과 캐시 + +- 도메인 이름 변환기 : 크롤러 성능의 병목 중 하나, DNS 요청을 보내고 결과를 받는 작업의 동기적 특성 때문, DNS 요청 결과를 받기 전까지는 다음 작업을 진행 할 수 없다 +- 크롤러 스레드 가운데 어느 하나라도 이 작업을 하고 있으면 다른 스레드의 DNS 요청은 전부 블록 된다 +- DNS 조회 결과로 얻어진 도메인 이름과 IP주소 사이의 관계를 캐시에 보관해 놓고 크롭잡 등을 돌려 주기적으로 갱신해 성능 높임 + +3. 지역성 + +- 크롤링 작업을 수행하는 서버를 지역별로 분산하는 방법 +- 크롤링 서버가 크롤링 대상 서버와 지역적으로 가까우면 페이지 다운로드 시간은 줄어들 것 + +4. 짧은 타임아웃 + +- 최대 얼마나 기다릴지 미리 정해두는 것 + +### 안정성 확보 전략 + +최적화된 성능 뿐 아니라 안정성도 다운로더 설계시 중요하게 고려! + +- 안정 해시 : 다운로더 서버들에 부하를 분산할 때 적용가능한 기술, 이 기술 이용하면 다운로더 서버를 쉽게 추가하고 삭제할 수 있다 +- 크롤링 상태 및 수집 데이터 저장: 장애가 발생한 경우에도 쉽게 복구할 수 있도록 크롤링 상태와 수집된 데이터를 지속적으로 저장장치에 기록해 두는 것이 바람직 +- 예외 처리 : 대규모 시스템에서 에러는 불가피할 뿐 아니라 흔하게 벌어지는 일이다. +- 데이터 검증 + +### 확장성 확보 전략 + +![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2Fbpsn94%2FbtrQcNCtKIB%2FW5CoeemnDyMZH3UlifDKKK%2Fimg.jpg) + +- 시스템을 설계할 때는 새로운 형태의 콘텐츠를 쉽게 지원할 수 있도록 신경써야한다. + +### 문제 있는 콘텐츠 감지 및 회피 전략 + +1. 중복 콘텐츠 : 해시나 체크섬을 사용하여 중복 콘텐츠 탐지 +2. 거미 덫 : 크롤러를 무한 루프에 빠뜨리도록 설계한 웹 페이지. URL의 최대 길이를 제한하면 회피할 수 있다. 하지만 가능한 모든 종류의 덫을 피할 수 있는 만능 해결책은 없다 +3. 데이터 노이즈: 어떤 콘텐츠는 거의 가치가 없으므로 가능하다면 제외해야한다 + +## 4단계 마무리 + +### 추가로 논의해보면 좋을 점 + +- 서버 측 렌더링 +- 원치 않는 페이지 필터링 +- 데이터베이스 다중화 및 샤딩 +- 수평적 규모 확장성 +- 가용성, 일관성, 안정성 +- 데이터 분석 솔루션 diff --git "a/8\354\243\274\354\260\250/A\354\241\260/empty" "b/8\354\243\274\354\260\250/A\354\241\260/empty" deleted file mode 100644 index e69de29..0000000 diff --git "a/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/8\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" similarity index 100% rename from "10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" rename to "8\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" diff --git "a/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" deleted file mode 100644 index 38ed114..0000000 --- "a/9\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ /dev/null @@ -1,218 +0,0 @@ -# 9장 웹 크롤러 설계 - -## 웹 크롤러 (= 로봇, 스파이더) - -- 검색 엔진에서 널리 쓰는 기술 -- 목적 : 웹에 새로 올라오거나 갱신된 콘텐츠(웹 페이지, 이미지나 비디오 등)를 찾아내는 것 -- 웹 페이지 몇 개에서 시작하여 그 링크를 따라 나가면서 새로운 콘텐츠를 수집한다 - -## 웹 크롤러 이용 - -- 검색 엔진 인덱싱 : 크롤러의 가장 보편적인 용례 -- 웹 아카이빙 : 나중에 사용할 목적으로 장기보관하기 위해 웹에서 정보를 모으는 절차. 많은 국립 도서관이 크롤러로 웹 사이트 아카이빙 중 -- 웹 마이닝 : 인터넷에서 유용한 지식을 도출 -- 웹 모니터링 : 크롤러를 사용하면 인터넷에서 저작권이나 상표권이 침해되는 사례를 모니터링 할 수 있다 - -## 1단계 문제 이해 및 설계 범위 확정 - -### 웹 크롤러의 기본 알고리즘 - -1. URL 집합이 입력으로 주어지면, 해당 URL 들이 가리키는 모든 웹 페이지를 다운로드 한다 -2. 다운받은 웹 페이지에서 URL 들을 추출한다 -3. 추출된 URL들을 다운로드 할 URL 목록에 추가하고 위의 과정을 처음부터 반복한다 - -### 좋은 웹 크롤러가 만족시켜야 할 속성 - -- 규모 확장성 : 웹은 거대하고 수십억 개의 페이지가 존재하므로 병행성을 활용하면 효과적 -- 안정성 : 웹은 함정으로 가득하다. 크롤러를 비정상적인 입력이나 환경에 잘 대응할 수 있어야한다 -- 예절 : 크롤러는 수집 대상 웹 사이트에 짧은 시간동안 너무 많은 요청을 보내서는 안 된다 -- 확장성 : 새로운 형태의 콘텐츠를 지원하기가 쉬워야 한다. - -## 2단계 개략적 설계안 제시 및 동의 구하기 - -![](https://k.kakaocdn.net/dn/rP55W/btrP8WOAZn0/CI9aK0urjFwBn0YFjVWkxk/img.jpg) - -### 시작 URL 집합 - -- 웹 크롤러가 크롤링을 시작하는 출발점 - ex) 어떤 대학 웹 사이트로부터 찾아 나갈 수 있는 모든 웹 페이지를 크롤링하는 가장 직관적인 방법은 해당 대학의 도메인 이름이 붙은 모든 페이지의 URL을 시작URL로 쓰는 것 -- 크롤러가 가능한 한 많은 링크를 탐색할 수 있도록 하는 URL을 고르는 것이 바람직 -- 전체 URL공간을 작은 부분집합으로 나누는 전략을 사용(나라별로 인기 있는 웹 사이트가 다르다는 점에서 착안) -- 주제별로 다른 시작 URL을 사용 (쇼핑, 스포츠, 건강 등등의 주제별로 세분화하고 그 각각에 다른 시작 URL) -- 시작 URL을 무엇을 쓸 것이냐는 질문에 정답은 없다 - -## 미수집 URL 저장소 - -- 대부분의 현대적 웹 크롤러는 크롤링 상태를 다운로드할 URL, 다운로드 된 URL의 두가지로 나눠 관리한다. -- 이 중 다운로드 할 URL을 저장 관리하는 컴포넌트를 미수집 URL 저장소라 부른다 - -### HTML 다운로더 - -- 인터넷에서 웹 페이지를 다운로드하는 컴포넌트 -- 다운로드할 페이지 URL은 미수집 URL저장소가 제공 - -### 도메인 이름 변환기 - -- URL -> IP주소 -- HTML 다운로더는 도메인 이름 변환기를 사용하여 URL에 대응되는 IP주소를 알아낸다 - -### 콘텐츠 파서 - -- 웹페이지를 다운로드하면 파싱과 검증 과정을 거쳐야한다. 이상한 웹 페이지는 문제를 일으킬 수 있다 -- 크롤링 서버 안에 구현하면 크롤링 과정이 느려지게 되므로 독립된 컴포넌트로 만들었다 - -### 중복 콘텐츠인가? - -- 같은 콘텐츠 여러번 저장하게 되는 것 방지 -- 효과적인 방법은 웹 페이지의 해시 값 비교 - -### 콘텐츠 저장소 - -- HTML 문서를 보관하는 시스템 -- 저장할 데이터의 유형, 크기, 저장소 접근 빈도, 데이터의 유효기간 등을 종합적으로 고려해야 한다 -- ex) 데이터 양이 많으므로 대부분 콘텐츠는 "디스크"에 저장, 인기 있는 콘텐츠는 "메모리"에 두어 접근 지연시간 줄임 - -### URL 추출기 - -- HTML 페이지를 파싱하여 링크들을 골라냄 - -### URL 필터 - -- 특정한 콘텐츠 타입이나 파일 확장자를 갖는 URL, 접속 시 오류 발생하는 URL 등 을 크롤링 대상에서 배제하는 역할 - -### 이미 방문한 URL? - -- 이미 방문한 URL이나 미수집 URL 저장소에 보관된 URL을 추적할 수 있도록 하는 자료 구조 (볼륨필터나 해시 테이블..) 사용 -- 같은 URL을 여러번 처리하는 일 방지하여 서버 부하를 줄이고 시스템이 무한 루프에 빠지는 일 방지 - -### URL 저장소 - -- 이미 방문한 URL 을 보관하는 저장소 - -### 웹 크롤러 작업 흐름 - -1. 시작 URL 들을 미수집 URL 저장소에 저장 -2. HTML 다운로더는 미수집 URL 저장소에서 URL 목록 가져옴 -3. HTML 다운로더는 도메인 이름 변환기를 사용하여 URL의 IP 주소르 알아내고, 해당 IP 주소로 접속하여 웹 페이지를 다운 받음 -4. 콘텐츠 파서는 다운된 HTML 페이지를 파싱하여 올바른 형식을 갖춘 페이지인지 검증 -5. 콘텐츠 파싱과 검증이 끝나면 중복 콘텐츠인지 확인하는 절차 개시 -6. 중복 콘텐츠인지 확인하기 위해서, 해당 페이지가 이미 저장소에 있는지 본다 ( 이미 저장소에 있 : 처리하지 않고 버림 / 없 : 저장소에 저장한 뒤 URL 추출기로 전달) -7. URL 추출기는 해당 HTML 페이지에서 링크를 골라낸다 -8. 골라낸 링크를 URL 필터로 전달한다 -9. 필터링이 끝나고 남은 URL 만 중복 URL 판별 단계로 전달 -10. 이미 처리한 URL인지 확인하기 위해 ,URL저장소에서 보관된 URL인지 살핀다. 이미 저장소에 있는 URL은 버린다 -11. 저장소에 없는 URL은 URL 저장소에 저장할 뿐 아니라 미수집 URL 저장소에도 전달한다 - -## 3단계 상세 설계 - -### < DFS VS BFS> - -- 웹 크롤러는 보통 BFS 사용. 큐의 한쪽으로는 탐색할 URL을 집어넣고, 다른 한쪽으로는 꺼내기만 한다 -- 문제 1: 한 페이지에서 나오는 링크의 상당 수는 같은 서버로 되돌아간다. 크롤러는 같은 호스트에 속한 많은 링크를 다운받느라 바빠지게 되는데, 이때 링크들을 병렬로 처리하게 된다면 서버 과부하(예의 없는 크롤러) -- 문제 2 : BFS알고리즘은 우선순위를 두지 않지만 모든 웹 페이지가 같은 수준의 품질, 중요성을 갖지 않는다. - -### <미수집 URL 저장소> - -- 위의 문제 해결 방앉 -- 이 저장소를 잘 구현하여 예의를 갖춘 크롤러, URL 사이의 우선순위와 신선도를 구별하는 크롤러를 구현할 수 있다. - -### 예의 - -- 동일 웹 사이트에 대해서는 한 번에 한 페이지만 요청 -- 같은 웹 사이트의 페이지를 다운받는 태스크는 시간차를 두고 실행하도록 하면 된다 -- 웹 사이트의 호스트명과 다운로드를 수행하는 작업스레드 사이의 관계를 유지하면된다. 각 다운로드는 스레드 별로 FIFO큐를 가지고 있어서, 해당 큐에서 꺼낸 URL만 다운로드 한다 - -![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbviJv0%2FbtrP9AdeSdU%2F02iEO9ybfOzTbT4MuODXJk%2Fimg.jpg) - -- 큐 라우터 : 같은 호스트에 속한 URL은 언제나 같은 큐로 가도록 보장 -- 매핑 테이블 : 호스트 이름과 큐 사이의 관계를 보관하는 테이블 -- FIFO 큐(b1~bn) : 같은 호스트에 속한 URL은 언제나 같은 큐에 보관 -- 큐 선택기 : 큐 선택기는 큐들을 순회하면서 큐에서 URL을 꺼내서 해당 큐에서 나온 URL을 다운로드하도록 지정된 작업 스레드에 전달하는 역할 -- 작업 스레드 : 전달된 URL을 다운로드하는 작업을 수행 - -### 우선순위 - -- 페이지랭크, 트래픽 양, 갱신 빈도 등 다양한 척도를 사용하여 URL 우선순위를 나눔 -- 순위결정장치는 URL 우선순위를 결정하는 컴포넌트 - ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbKe803%2FbtrQdzKSGSZ%2FYU9hIZzzz0NncK2rEulFN1%2Fimg.jpg) -- 순위결정장치 : URL을 입력으로 받아 우선순위를 계산한다 -- 큐(f1~fn) : 우선순위별로 큐가 하나씩 할당된다. 우선순위가 높으면 선택될 확률도 올라간다 -- 큐 선택기 : 임의 큐에서 처리할 URL을 꺼내는 역할을 담당, 순위가 높은 큐에서 더 자주 꺼내도록 프로그램 됨 - -

- -아래 그림은 예의를 갖추고 URL사이의 우선순위와 신선도를 구별하는 크롤러를 구현한 예이다 -![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FcT2P9I%2FbtrP981FZuu%2FQB2Ix9Ne78L8dcA6Or6uh1%2Fimg.jpg) - -### 신선도 - -- 데이터의 신선함을 유지하기 위해서는 이미 다운로드한 페이지라고 해도 주기적으로 재수집할 필요가 있다. -- 이 작업을 최적화하기 위해 웹 페이지의 변경 이력을 활용하거나 우선순위를 활용하여 중요한 페이지는 좀 더 자주 재수집한다 - -## HTML 다운로더 - -- HTTP 프로토콜을 통해 웹 페이지를 내려받는다 - -### 로봇 제외 프로토콜 - -- 웹사이트가 크롤러와 소통하는 표준적 방법 -- 크롤러가 수집해도 되는 웹 페이지 목록이 들어있다 -- 웹 사이트를 긁어 가기 전에 크롤러는 해당 파일에 나열된 규칙을 먼저 확인해야한다 - -### 성능 최적화 - -다음은 HTML 다운로더에 사용할 수 있는 성능 최적화 기법들이다 - -1. 분산 크롤링 - -- 성능을 높이기 위해 크롤링 작업을 여러 서버에 분산 -- 각 서버는 여러 스레드를 돌려 다운로드 작업을 처리 -- URL 공간은 작은 단위로 분할하여, 각 서버는 그중 일부의 다운로드를 담당하도록 한다 - ![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FJPUzp%2FbtrP9kn0iaD%2FBzKfUsNxGJKyEdCkmMYCu1%2Fimg.jpg) - -2. 도메인 이름 변환 결과 캐시 - -- 도메인 이름 변환기 : 크롤러 성능의 병목 중 하나, DNS 요청을 보내고 결과를 받는 작업의 동기적 특성 때문, DNS 요청 결과를 받기 전까지는 다음 작업을 진행 할 수 없다 -- 크롤러 스레드 가운데 어느 하나라도 이 작업을 하고 있으면 다른 스레드의 DNS 요청은 전부 블록 된다 -- DNS 조회 결과로 얻어진 도메인 이름과 IP주소 사이의 관계를 캐시에 보관해 놓고 크롭잡 등을 돌려 주기적으로 갱신해 성능 높임 - -3. 지역성 - -- 크롤링 작업을 수행하는 서버를 지역별로 분산하는 방법 -- 크롤링 서버가 크롤링 대상 서버와 지역적으로 가까우면 페이지 다운로드 시간은 줄어들 것 - -4. 짧은 타임아웃 - -- 최대 얼마나 기다릴지 미리 정해두는 것 - -### 안정성 확보 전략 - -최적화된 성능 뿐 아니라 안정성도 다운로더 설계시 중요하게 고려! - -- 안정 해시 : 다운로더 서버들에 부하를 분산할 때 적용가능한 기술, 이 기술 이용하면 다운로더 서버를 쉽게 추가하고 삭제할 수 있다 -- 크롤링 상태 및 수집 데이터 저장: 장애가 발생한 경우에도 쉽게 복구할 수 있도록 크롤링 상태와 수집된 데이터를 지속적으로 저장장치에 기록해 두는 것이 바람직 -- 예외 처리 : 대규모 시스템에서 에러는 불가피할 뿐 아니라 흔하게 벌어지는 일이다. -- 데이터 검증 - -### 확장성 확보 전략 - -![](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2Fbpsn94%2FbtrQcNCtKIB%2FW5CoeemnDyMZH3UlifDKKK%2Fimg.jpg) - -- 시스템을 설계할 때는 새로운 형태의 콘텐츠를 쉽게 지원할 수 있도록 신경써야한다. - -### 문제 있는 콘텐츠 감지 및 회피 전략 - -1. 중복 콘텐츠 : 해시나 체크섬을 사용하여 중복 콘텐츠 탐지 -2. 거미 덫 : 크롤러를 무한 루프에 빠뜨리도록 설계한 웹 페이지. URL의 최대 길이를 제한하면 회피할 수 있다. 하지만 가능한 모든 종류의 덫을 피할 수 있는 만능 해결책은 없다 -3. 데이터 노이즈: 어떤 콘텐츠는 거의 가치가 없으므로 가능하다면 제외해야한다 - -## 4단계 마무리 - -### 추가로 논의해보면 좋을 점 - -- 서버 측 렌더링 -- 원치 않는 페이지 필터링 -- 데이터베이스 다중화 및 샤딩 -- 수평적 규모 확장성 -- 가용성, 일관성, 안정성 -- 데이터 분석 솔루션 From 43727fcb705074d7a88e43339b28916729d060b6 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Tue, 22 Nov 2022 20:57:59 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[ADD]=2010=EC=A3=BC=EC=B0=A8=20-=EC=9D=B4?= =?UTF-8?q?=EC=84=9C=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\235\264\354\204\234\354\232\260.md" | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 "10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" diff --git "a/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" new file mode 100644 index 0000000..9d38167 --- /dev/null +++ "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -0,0 +1,82 @@ +# 13장 검색어 자동 완성 시스템 + +# 1 단계 문제 이해 및 설계 범위 확정 + +## 요구사항 + +- 빠른 응답 속도 : 사용자가 검색어를 입력함에 따라 자동완성 검색어도 충분히 빨리 표시돼야 함 +- 연관성: 자동완성되어 출력되는 검색어는 사용자가 입력한 단어와 연관된 것이어야 함 +- 정렬 : 결과는 인기도 등의 순위 모델에 의해 정렬돼 있어야 한다 +- 규모 확장성 : 시스템은 많은 트래픽을 감당할 수 있도록 확장 가능해야 한다 +- 고가용성 : 시스템의 일부에 장애가 발생하거나, 느려지거나 해도 시스템은 계속 사용 가능해야 한다 + +## 계략적 규모 추정 + +- 일간 능동 사용자는 천만 명으로 가정 +- 평균적으로 한 사용자는 매일 10건의 검색 수행 +- 질의할 때마다 평균적으로 20바이트의 데이터를 입력한다고 가정 +- 검색창에 글자를 입력할 때마다 클라이언트는 검색어 자동완성 백엔드에 요청을 보낸다. 따라서 평균적으로 1회 검색당 20건의 요청이 백엔드로 전달된다 +- 대략 초당 24000건의 질의(QPS)가 발생, 최대 QPS는 48000 +- 질의 가운데 20%정도는 신규 검색어라고 가정. 매일 0.4GB의 신규데이터가 시스템에 추가 +

+ +# 2단계 계략적 설게안 제시 및 동의 구하기 + +개략적으로 보면 시스템은 두 부분으로 나뉜다 + +- 데이터 수집 서비스(data gathering service) : 사용자가 입력한 질의를 실시간으로 수집하는 시스템 +- 질의 서비스(query service): 주어진 질의에 다섯개의 인기 검색어를 정렬해 내놓는 서비스이다 + +## 데이터 수집 서비스 + +질의문과 사용빈도를 저장하는 빈도 테이블이 있다고 가정하면 처음에 이 테이블은 비어 있는데 사용자가 단어를 순서대로 검색하면 그 상태가 다음과 같이 바뀌어 나간다 + +## 질의 서비스 + +빈도 테이블이 있을때 질의문을 저장하는 필드인 query와 질의문이 사용된 빈도를 저장하는 필드인 frequency가 있다. 사용자가 tw를 검색창에 입력했을때 가장 많이 사용된 5개 검색어는 다음과 같은 SQL 질의문을 이용해 계산할 수 있다 + +``` +SELECT * FROM frequency_table +WHERE query Like `prefix%` +ORDER BY frequency DESC +LIMIT 5 +``` + +데이터 양이 적을 땐 괜찮지만 데이터가 아주 많아지면 데이터베이스가 병목이 될 수 있다 +

+ +# 3단계 상세 설계 + +## 트라이 자료구조 + +- 개략적 설계안에서는 관계형 데이터베이스를 저장소로 사용했지만 이건 효율적이지 않아 트라이를 사용해 이 문제를 해결 + +### 트라이 + +- 문자열들을 간략하게 저장할 수 있는 자료구조 +- 문자열을 꺼내는 연산에 초점을 맞추어 설계된 자료구조임을 미루어 짐작할 수 있다 +- 트라이는 트리 형태의 자료구조 +- 트리의 루트 노드는 빈 문자열을 나타냄 +- 각 노드는 글자 하나를 저장하며, 26개의 자식노드를 가질 수 있다 +- 각 트리 노드는 하나의 단어, 또는 접두어 문자열을 나타낸다 +

+ +### 질의어 'tree','try','true','toy','wish','win'이 보관된 트라이 + +- 기본 트라이 자료구조는 노드에 문자들을 저장 +- 이용빈도에 따라 정렬된 결과를 내놓기 위해서는 노드에 빈도 정보까지 저장할 필요가 있다 + +### 트라이도 검색어 자동완성은 어떻게 구현할 수 있을까? ( 가장 많이 사용된 질의어 k개 ) + +- p: 접두어의 길이, n: 트라이 안에 있는 노드 개수, c: 주어진 노드의 자식 노드 개수 +- 해당 접두어를 표현하는 노드를 찾는다 +- 해당 노드부터 시작하는 하위트리를 탐색하며 모든 유효 노드를 찾는다. 유효한 검색 문자열을 구성하는 노드가 유효 노드다 +- 유효 노드들을 정렬하여 가장 인기 있는 검색어 k개를 찾는다 + +### 최악의 경우에 전체 트리 다 검색해야하는 경우 해결 + +- 접두어 최대 길이 제한 + - 사용자가 검색창에 긴 검색어를 입력하는 일은 거의 없다. 따라서 p값은 작은 정수값이라고 가정해도 안전하다 +- 노드에 인기 검색어 캐시 + - 각 노드에 k개의 인기 검색어를 저장해 두면 전체 트라이를 검색하는 일을 방지할 수 있다. - 각 노드에 인기 질의어를 캐시하면 top5 검색어를 질의하는 시간 복잡도를 엄청나게 낮출 수 있다. + - 하지만 각 노드에 질의어를 저장할 공간이 많이 필요하게 된다는 단점도 있다 From 6e0cf72a25bb8affb4c85812d20789b07a685583 Mon Sep 17 00:00:00 2001 From: dltjdn Date: Wed, 23 Nov 2022 02:47:12 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[FEAT]=2010=EC=A3=BC=EC=B0=A8=20-=20?= =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\235\264\354\204\234\354\232\260.md" | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git "a/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" index 9d38167..ed97673 100644 --- "a/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" +++ "b/10\354\243\274\354\260\250/A\354\241\260/\354\235\264\354\204\234\354\232\260.md" @@ -47,7 +47,7 @@ LIMIT 5 # 3단계 상세 설계 -## 트라이 자료구조 +## (1) 트라이 자료구조 - 개략적 설계안에서는 관계형 데이터베이스를 저장소로 사용했지만 이건 효율적이지 않아 트라이를 사용해 이 문제를 해결 @@ -80,3 +80,68 @@ LIMIT 5 - 노드에 인기 검색어 캐시 - 각 노드에 k개의 인기 검색어를 저장해 두면 전체 트라이를 검색하는 일을 방지할 수 있다. - 각 노드에 인기 질의어를 캐시하면 top5 검색어를 질의하는 시간 복잡도를 엄청나게 낮출 수 있다. - 하지만 각 노드에 질의어를 저장할 공간이 많이 필요하게 된다는 단점도 있다 + +## (2) 데이터 수집 서비스 + +- 실시간으로 데이터를 수정하는 서비스는 그닥 실용적이지 못 하다 +- 구글 검색 같은 애플리케이션이라면 그렇게 자주 바꿔줄 필요가 없다 +- 트라이를 만드는 데 쓰이는 데이터는 보통 데이터 분석 서비스나 로깅 서비스로부터 온다 -> 용례가 달라지더라도 데이터 수집 서비스의 토대는 바뀌지 않을 것 + +### 데이터 분석 서비스 로그 + +- 데이터 분석 서비스 로그에는 검색창에 입력된 질의에 관한 원본 데이터가 보관 +- 새로운 데이터가 추가될 뿐 수정은 이루어지지 않으며 로그 데이터에는 인덱스를 걸지 않는다 + +### 로그 취합 서버 + +- 데이터 분석 서비스로부터 나오는 로그는 보통 양이 엄청나고 데이터 형식도 제각각인 경우가 많다. 따라서 이 데이터를 잘 취합하여 우리 시스템이 쉽게 소비할 수 있도록 해야한다 +- 데이터 취합 방식은 우리 서비스의 용례에 따라 달라진다 -> 트위터와 같은 실시간 애플리케이션의 경우 결과를 빨리 보여주는 것이 중요하므로 데이터 취합 주기를 보다 짧게 가져갈 필요가 있다 / 대부분은 일주일에 한 번 정도 로그를 취합해도 충분할 것이다. + +### 작업 서버 + +- 주기적으로 비동기적 작업을 실행하는 서버 집합 +- 트라이 자료구조를 만들고 트라이 데이터베이스에 저장하는 역할을 담당한다 + +### 트라이 캐시 + +- 분산 캐시 시스템으로 트라이 데이터를 메모리에 유지하여 읽기 연산 성능을 높이는 구실을 한다 +- 매주 트라이 데이터베이스의 스냅샷을 떠서 갱신한다 + +### 트라이 데이터베이스 + +- 지속성 저장소 +- (1) 문서 저장소 : 새 트라이를 매주 만들 것이므로 주기적으로 트라이를 직렬화하여 데이터베이스에 저장할 수 있다. 몽고디비 같은 문서 저장소를 활용하면 이런 데이터를 편리하게 저장할 수 있다 +- (2) 키-값 저장소 : 트라이는 아래 로직을 적용하면 해시 테이블 형태로 변환 가능하다 + - 트라이에 보관된 모든 접두어를 해시 테이블 키로 변환 + - 각 트라이 노드에 보관된 모든 데이터를 해시 테이블 값으로 변환 + +## (3) 질의 서비스 + +개략적 설계안에서 살펴본 질의 서비스의 비효율성을 개선한 설계안 + +1. 검색 질의가 로드밸런서로 전송된다 +2. 로드밸런서는 해당 질의를 API 서버로 보낸다 +3. API 서버는 트라이 캐시에서 데이터를 가져와 해당 요청에 대한 자동완성 검색어 제안 응답을 구성한다 +4. 데이터가 트라이 캐시에 없는 경우에는 데이터를 데이터베이스에서 가져와 캐시에 채운다. 그래야 다음에 같은 접두어에 대한 질의가 오면 캐시에 보관된 데이터를 사용해 처리할 수 있다.( 캐시 미스는 캐시 서버의 메모리가 부족하거나 캐시 서버에 장애가 있어도 발생할 수 있다) + +### 질의 서비스 최적화 방안 + +- AJAX 요청 : 웹 애플리케이션의 경우 브라우저는 보통 AJAX 요청을 보내어 자동완성된 검색어 목록을 가져온다. 장점은 요청을 보내고 받기 위해 페이지를 새로고침 할 필요가 없다는 것 +- 브라우저 캐싱 : 대부분 애플리케이션은 자동완성 검색어 제안 결과는 짧은 시간 안에 자주 바뀌지 않는다. 따라서 제안된 검색어들을 브라우저 캐시에 넣어두면 후속 질의의 결과는 해당 캐시에서 바로 가져갈 수 있다. +- 데이터 샘플링 : 대규모 시스템의 경우, 모든 질의 결과를 로깅하도록 해 놓으면 CPU 자원과 저장공간을 엄청나게 소진. N개 요청 가운데 1개만 로깅하도록 하는 데이터 샘플링 기법은 이럴 때 유용 + +### 트라이 연산 + +#### 트라이 생성 + +작업서버가 담당, 데이터 분석 서비스의 로그나 데이터베이스로부터 취합된 데이터를 이용한다 + +#### 트라이 갱신 + +1. 매주 한 번 갱신하는 방법 -> 새로운 트라이를 만든 다음에 기존 트라이를 대체한다 +2. 트라이의 각 노드를 개별적으로 갱신 -> 성능이 좋지 않지만 트라이가 작을 때 고려해봄직 하다 + +#### 검색어 삭제 + +- 트라이 캐시 앞에 필터 계층을 두고 부적절한 질의어가 반환되지 않도록 하는 것 +- 필터 계층을 두면 필터 규칙에 따라 검색 결과를 자유롭게 변경할 수 있다는 장점