Meteor는 많은 클라이언트간에 거대한 컬렉션을 공유하는 동안 얼마나 많았습니까?
다음과 같은 경우를 상상해 발견.
1,000 명의 클라이언트가 "Somestuff"컬렉션의 내용을 표시하는 Meteor 페이지에 연결되어 있습니다.
"Somestuff"는 1,000 개의 항목을 보유한 컬렉션입니다.
누군가 "Somestuff"컬렉션에 새 항목을 삽입합니다.
무슨 일이 일어날 것 :
Meteor.Collection
클라이언트의 모든 항목 이 업데이트됩니다. 즉 삽입이 모든 클라이언트에 전달됩니다 (즉, 하나의 삽입 메시지가 1,000 개의 클라이언트에 전송됨을 의미 함).
업데이트해야 할 클라이언트를 결정하는 서버의 CPU 비용은 얼마입니까?
전체 목록이 아닌 삽입 된 값만 클라이언트에 전달되는 것입니다.
실생활에서 어떻게 작동합니까? 대규모 규모의 벤치 마크 나 실험이 있습니까?
짧은 대답은 새로운 데이터 만 유선으로 전송 것입니다. 작동 원리는 다음과 가변적입니다.
Meteor 서버에는 구독을 관리하는 세 가지 중요한 부분이 있습니다. 구독이 제공하는 데이터에 대한 논리를 정의하는 게시 기능 ; 몽고 드라이버 변경에 대한 데이터베이스를 시계; 그리고 클라이언트의 모든 활성 구독을 결합하여 네트워크를 통해 클라이언트로 병합 상자 .
게시 기능
유성가 컬렉션을 클라이언트 구독 할 때마다 서버는 게시 기능 현관을 실행 합니다 . 게시 기능의 역할은 클라이언트가 가져야하는 문서 세트를 제공하고 각 문서 속성을 병합 상자로 것입니다. 새 구독 클라이언트마다 한 실행 실행됩니다. 를 사용하는 임의의 복잡한 액세스 제어와 같이 게시 기능에 원하는 JavaScript를 넣을 수 있습니다 this.userId
. 는 함수가 호출하여 병합 상자에 데이터를 전송 게시 this.added
, this.changed
하고 this.removed
. 자세한 내용은 전체 게시 문서 를 참조하세요.
게시 대부분의 기능은 낮은 수준의 주위에 깨끗이 할 필요가 없습니다 added
, changed
그리고 removed
하지만, API. 는 함수를 리턴한다 몽고 커서를 게시하면 , 유성 서버는 자동으로 몽고 드라이버 (의 출력에 연결 insert
, update
및 removed
병합 상자의 입력 입력에 콜백을 () this.added
, this.changed
및 this.removed
). 게시에서 모든 권한 확인을 미리 수행 한 다음 사용자 코드없이 데이터베이스 드라이버를 병합 상자에 직접 기능 수있는 것이 매우 깔끔합니다. 그리고 자동 게시가 설정되면 작은 부분조차 숨겨져 있습니다. 서버는 각 컬렉션의 모든 문서에 대한 쿼리를 자동으로 설정하고 병합 상자로 푸시합니다.
반면에 데이터베이스 쿼리 게시에만 국한되지 않습니다. 예를 들어 내부의 기기에서 GPS 위치를 읽거나 Meteor.setInterval
다른 웹 서비스에서 레거시 REST API를 폴링하는 게시 함수를 사용할 수 있습니다 . 경우에, 당신은 낮은 수준을 호출하여 병합 상자의 변경을 방출 것 added
, changed
및 removed
DDP API.
몽고 드라이버
몽고 드라이버의 작업은 라이브 쿼리에 대한 변경 사항 몽고 데이터베이스를 시청하는 것입니다. 쿼리는 계속 이러한 실행 added
되며 removed
, 및 changed
콜백 을 호출하여 결과가 변경되면 업데이트를 반환합니다 .
Mongo는 실시간 데이터베이스가 아닙니다. 그래서 매일가 투표합니다. 각 활성 라이브 쿼리에 대한 마지막 쿼리 결과의 메모리 내 복사본을 유지합니다. 각 폴링주기에, 그것은 최소 세트의 이전 저장 결과를 비교 added
, removed
그리고 changed
그 차이를 설명하는 이벤트. 여러 호출자가 동일한 라이브 쿼리에 대한 복수를 등록하는 경우 드라이버는 쿼리의 복사본 하나만 감시하여 동일한 결과로 등록 된 각을 호출합니다.
서버가 컬렉션을 업데이트 할 때마다 드라이버는 해당 컬렉션에 대한 각 라이브 쿼리를 다시 계산합니다 (향후 Meteor 버전은 업데이트시 다시 계산되는 라이브 쿼리를 제한하기 위해 포함하는 API를 노출합니다.) 또한 드라이버는 10 초 타이머 에서 각 각 쿼리를 폴링하여 Meteor 서버를 우회하는 대역 외 데이터베이스 업데이트를 라이브합니다.
병합 상자
의 작업 병합 결과 상자 입니다 (결합하는 것입니다 added
, changed
그리고 removed
단일 데이터 스트림으로 클라이언트의 활성 게시의 모든 기능 통화). 많은 각 클라이언트에 대해 하나의 병합 상자가 있습니다. 클라이언트의 minimongo 캐시의 전체 사본을 보유합니다.
단일 구독이있는 예에서 병합 상자는 기본적으로 통과입니다. 그러나 더 복잡한 앱에는 여러 개의 구독이있을 수 있습니다. 두 구독이 모두 동일한 문서에 동일한 속성을 설정하면 병합 상자는 우선 순위를 장치 값을 결정하고 클라이언트에게 보냅니다. 아직 구독 우선 순위 설정을위한 API를 공개하지 않습니다. 현재 우선 순위는 클라이언트가 데이터 세트를 구독하는 순서에 따라 결정됩니다. 클라이언트가 만드는 첫 번째 구독이 가장 높은 우선 순위를 가지고 두 번째 구독이 그 다음으로 높은 방식입니다.
병합 상자는 클라이언트의 상태를 유지하기 때문에 게시 기능이 제공하는 내용에 관계없이 각 클라이언트를 최신 상태로 유지하기 위해 최소한의 데이터를 보낼 수 있습니다.
업데이트시 발생하는 사항
이제 시나리오에 대한 단계를 설정했습니다.
1,000 개의 사용 가능한 클라이언트가 있습니다. 각각은 동일한 라이브 Mongo 쿼리 ( Somestuff.find({})
)를 구독합니다 . 쿼리는 각 클라이언트에 대해 동일시 드라이버는 하나의 라이브 쿼리 만 실행합니다. 1,000 개의 활성 병합 상자가 있습니다. 그리고 각 클라이언트의 기능이 등록 게시 added
, changed
그리고 removed
그 라이브 쿼리 병합 상자 중 하나에 피드가. 정리 상자에 다른 것은 사용하지 않습니다.
먼저 몽고 드라이버. 클라이언트 중 하나가에 새 문서를 삽입 Somestuff
하면 재 계산이 트리거됩니다. 몽고 드라이버는의 모든 문서에 대한 쿼리 를 다시 실행하고 Somestuff
, 결과를 메모리의 이전 결과와 비교 하고 , 새 문서가 하나 있음을 확인하고, 1000 등록 된 개의 insert
콜백을 각각 호출합니다.
다음으로 게시 기능입니다. 여기서는 거의 일어나지입니다. 1,000 개의 대규모 insert
추가 증가 은를 호출하여 병합 상자로 데이터를 푸시합니다 added
.
마지막으로 각 병합 상자는 클라이언트 캐시의 메모리 내 사본에 대해 새 속성을 확인합니다. 신규의 경우 값이 아직 클라이언트에 기존 값을 숨기지 발견합니다. 따라서 병합 상자 DATA
는 SockJS 연결에 대한 DDP 메시지를 보강하고 측 메모리 내 복사본을 업데이트합니다.
총 CPU 비용은 하나의 Mongo 쿼리를 비교하는 비용과 클라이언트의 상태를 확인하고 새로운 DDP 메시지 페이로드를 구성하는 1,000 개의 병합 상자 비용을 더한 비용입니다. 유선을 통해 흐르는 유일한 데이터는 데이터베이스의 새 문서에 해당하는 1,000 개의 클라이언트 각각에 전송되는 단일 클라이언트 JSON 개체와 원본 삽입을 만든 에서 로 개인 번호 RPC 메시지 입니다.
최적화
우리가 확실히 계획을 세우는 것입니다.
보다 효율적인 Mongo 드라이버. 우리는 드라이버를 최적화 된 호출의 쿼리 당 하나의 관찰 유해 실행하는 0.5.1에서.
모든 DB 변경이 쿼리 재 계산을 트리거하는 것은 아닙니다. 일부 자동 개선을 수행 할 수있는 가장 좋은 방법은 개발자가 재실행이 필요한 쿼리를 수있는 API입니다. 예를 들어, 하나의 채팅방에 메시지를 삽입하는 것이 두 번째 방의 메시지에 대한 실시간 쿼리를 무효화합니다.
Mongo 드라이버, 게시 기능 및 병합은 동일한 프로세스 또는 시스템에서 필요가 없습니다. 일부 애플리케이션은 복잡한 라이브 쿼리를 실행하고 데이터베이스를보기 위해 더 많은 CPU가 필요합니다. 다른 쿼리에는 몇 가지 고유 한 쿼리 만 들어 (블로그 엔진을 상상할 수 있습니다.) 클라이언트가 많을 수 있습니다. 더 많은 CPU가 필요합니다. 개별 구성 요소를 분리하면 각 조각을 독립적으로 확장 할 수 있습니다.
많은 데이터베이스는 행이 업데이트 될 때 실행되는 트리거를 지원하고 이전 및 새 행을 제공합니다. 이 기능을 사용하면 데이터베이스 드라이버가 변경 사항을 폴링하는 대신 트리거를 등록 할 수 있습니다.
내 경험상 Meteor에서 거대한 컬렉션을 공유하는 동안 많은 클라이언트를 사용하는 것은 버전 0.7.0.1부터 본질적으로 작동하지 않습니다. 이유를 설명하려고합니다.
위의 게시물과 https://github.com/meteor/meteor/issues/1821 에서 설명한 것처럼 유성 서버는 병합 상자 에 각 클라이언트에 대해 게시 된 데이터의 복사본을 보관해야합니다 . 이것이 Meteor 마법이 일어날 수있게 해주지 만, 노드 프로세스의 메모리에 반복적으로 유지되는 대용량 공유 데이터베이스를 초래합니다. ( meteor에 컬렉션이 정적 (변경되지 않을 것)이라고 알려주는 방법이 있습니까? ) 와 같은 정적 컬렉션에 대해 가능한 최적화를 사용하더라도 Node 프로세스의 CPU 및 메모리 사용량에 큰 문제가 발생했습니다.
우리의 경우에는 완전히 정적 인 각 클라이언트에게 15k 문서 모음을 게시했습니다. 문제는 연결시 이러한 문서를 클라이언트의 병합 상자 (메모리)에 복사하면 기본적으로 노드 프로세스가 거의 1 초 동안 100 % CPU로 전환되어 추가 메모리 사용량이 증가한다는 것입니다. 연결하는 클라이언트가 서버를 무릎에 대고 (동시 연결이 서로를 차단할 것임) 메모리 사용량이 클라이언트 수에서 선형 적으로 증가하기 때문에 이것은 본질적으로 확장 할 수 없습니다. 우리의 경우 전송 된 원시 데이터가 약 5MB에 불과했지만 각 클라이언트는 추가로 ~ 60MB 의 메모리를 사용했습니다.
우리의 경우 컬렉션이 정적 이었기 때문에 모든 문서를 .json
nginx에 의해 gzip으로 압축 된 파일 로 전송 하고 익명 컬렉션에로드하여 추가 CPU없이 최대 1MB의 데이터 만 전송함으로써이 문제를 해결 했습니다. 또는 노드 프로세스의 메모리와 훨씬 더 빠른로드 시간. 이 컬렉션에 대한 모든 작업 _id
은 서버에있는 훨씬 작은 출판물의 s를 사용하여 수행되었으며 Meteor의 대부분의 이점을 유지할 수 있습니다. 이를 통해 앱을 더 많은 클라이언트로 확장 할 수있었습니다. 또한 앱이 대부분 읽기 전용이기 때문에 각 노드 인스턴스가 단일 스레드이므로로드 밸런싱 (단일 Mongo 사용)을 사용하여 nginx 뒤에서 여러 Meteor 인스턴스를 실행하여 확장 성을 더욱 향상 시켰습니다.
그러나 여러 클라이언트간에 큰 쓰기 가능한 컬렉션을 공유하는 문제는 Meteor가 해결해야하는 엔지니어링 문제입니다. 각 클라이언트에 대한 모든 사본을 보관하는 것보다 더 좋은 방법이있을 수 있지만 분산 시스템 문제로 심각한 생각이 필요합니다. 대규모 CPU 및 메모리 사용량의 현재 문제는 확장되지 않습니다.
이 질문에 답하기 위해 사용할 수있는 실험 :
- 테스트 유성 설치 :
meteor create --example todos
- Webkit 검사기 (WKI)에서 실행합니다.
- 회선을 통해 이동 하는 XHR 메시지 의 내용을 조사하십시오 .
- 전체 컬렉션이 전선을 가로 질러 이동하지 않는지 확인합니다.
WKI 사용 방법에 대한 팁은이 기사를 확인 하십시오 . 약간 구식이지만, 특히이 질문에 대해서는 여전히 유효합니다.
아직 1 년이 지났으므로 "Meteor 1.0"이전의 지식이 있다고 생각합니다. 그러면 상황이 다시 변경되었을 수 있습니다. 아직 조사 중입니다. http://meteorhacks.com/does-meteor-scale.html 은 "Meteor를 확장하는 방법?"으로 연결됩니다. 기사 http://meteorhacks.com/how-to-scale-meteor.html
'IT' 카테고리의 다른 글
여러 열을 기본 키로 사용하는 이유 (복합 기본 키) (0) | 2020.08.19 |
---|---|
사용자가 Facebook의 API를 사용하여 내 Facebook 페이지 또는 URL을 좋아하는지 확인하는 방법 (0) | 2020.08.19 |
C / C ++ NaN 상수 (리터럴)? (0) | 2020.08.19 |
왜“short thirty = 3 * 10”이 법적 과제입니까? (0) | 2020.08.19 |
ngModel 포맷터 및 파서 (0) | 2020.08.19 |