Krótka odpowiedź jest taka, że tylko nowe dane są przesyłane przewodem. Oto jak to działa.
Istnieją trzy ważne części serwera Meteor, które zarządzają subskrypcjami: funkcja publikowania , która definiuje logikę danych, które dostarcza subskrypcja; kierowca Mongo , który ogląda bazy danych dla zmian; oraz pole scalania , które łączy wszystkie aktywne subskrypcje klienta i wysyła je przez sieć do klienta.
Funkcje publikowania
Za każdym razem, gdy klient Meteor subskrybuje kolekcję, serwer uruchamia funkcję
publikowania . Zadaniem funkcji publikowania jest ustalenie zestawu dokumentów, które powinien posiadać klient, i wysłanie każdej właściwości dokumentu do pola korespondencji seryjnej. Działa raz dla każdego nowego klienta subskrybującego. W funkcji publikowania można umieścić dowolny kod JavaScript, na przykład dowolnie złożoną kontrolę dostępu za pomocą this.userId
. Publikowania funkcja wysyła dane w polu seryjnej poprzez wywołanie this.added
, this.changed
a
this.removed
. Zobacz
pełną dokumentację publikowania, aby uzyskać więcej informacji.
Większość publikuje funkcje nie trzeba syf wokół z niskim poziomie
added
, changed
a removed
API, choć. Jeśli publikuje zwraca kursor Mongo, serwer Meteor automatycznie łączy wyjście sterownika (Mongo insert
, update
i removed
wywołania zwrotne) do wejścia w pole scalania ( this.added
, this.changed
i this.removed
). To całkiem fajne, że możesz wykonać wszystkie sprawdzenia uprawnień z góry w funkcji publikowania, a następnie bezpośrednio podłączyć sterownik bazy danych do pola scalającego bez żadnego kodu użytkownika na drodze. A kiedy autopublikowanie jest włączone, nawet ten fragment jest ukryty: serwer automatycznie tworzy zapytanie dla wszystkich dokumentów w każdej kolekcji i umieszcza je w polu scalania.
Z drugiej strony nie jesteś ograniczony do publikowania zapytań do bazy danych. Na przykład można napisać funkcję publikowania, która odczytuje pozycję GPS z urządzenia wewnątrz Meteor.setInterval
lub sonduje starsze REST API z innej usługi internetowej. W tych przypadkach, można emitować zmian w polu seryjnej poprzez wywołanie niskim poziomie added
, changed
a removed
DDP API.
Kierowca Mongo
Zadaniem kierowcy Mongo jest obserwowanie bazy danych Mongo pod kątem zmian w zapytaniach na żywo. Zapytania te działają w sposób ciągły i powrócić aktualizacje jako zmiana wyników przez wywołanie added
, removed
oraz changed
wywołania zwrotne.
Mongo nie jest bazą danych czasu rzeczywistego. Więc kierowca sonduje. Przechowuje w pamięci kopię ostatniego wyniku zapytania dla każdego aktywnego zapytania na żywo. Na każdym cyklu odpytywania, porównuje nowy wynik z poprzedniego zapisanego wyniku obliczania minimalnego zestawu added
, removed
i changed
wydarzeń, które opisują różnicę. Jeśli wielu dzwoniących rejestruje wywołania zwrotne dla tego samego zapytania na żywo, sterownik ogląda tylko jedną kopię zapytania, wywołując każde zarejestrowane wywołanie zwrotne z tym samym wynikiem.
Za każdym razem, gdy serwer aktualizuje kolekcję, sterownik ponownie oblicza każde zapytanie na żywo w tej kolekcji (przyszłe wersje Meteor udostępnią skalujący interfejs API w celu ograniczenia liczby zapytań na żywo, które są przeliczane podczas aktualizacji). łapać aktualizacje baz danych poza pasmem, które omijały serwer Meteor.
Pole scalania
Zadaniem pole scalania jest zestawić wyniki ( added
, changed
i removed
połączenia) wszystkich aktywnych funkcji danego klienta publikują w jeden strumień danych. Dla każdego podłączonego klienta istnieje jedno pole scalania. Zawiera pełną kopię pamięci podręcznej minimongo klienta.
W Twoim przykładzie z pojedynczą subskrypcją pole scalania jest zasadniczo przejściem. Ale bardziej złożona aplikacja może mieć wiele subskrypcji, które mogą się nakładać. Jeśli dwie subskrypcje ustawiają ten sam atrybut w tym samym dokumencie, pole korespondencji seryjnej decyduje, która wartość ma priorytet i wysyła ją tylko do klienta. Nie ujawniliśmy jeszcze interfejsu API do ustawiania priorytetów subskrypcji. Na razie priorytet jest określany przez kolejność subskrybowania przez klienta zestawów danych. Pierwsza subskrypcja, którą tworzy klient, ma najwyższy priorytet, druga subskrypcja jest następna i tak dalej.
Ponieważ pole korespondencji seryjnej zawiera stan klienta, może wysłać minimalną ilość danych, aby każdy klient był aktualny, niezależnie od tego, jaka funkcja publikowania go dostarcza.
Co się dzieje po aktualizacji
Więc teraz przygotowaliśmy grunt pod Twój scenariusz.
Mamy 1000 połączonych klientów. Każdy jest subskrybowany na tę samą aktywną kwerendę Mongo ( Somestuff.find({})
). Ponieważ zapytanie jest takie samo dla każdego klienta, sterownik wykonuje tylko jedno zapytanie na żywo. Istnieje 1000 aktywnych pól korespondencji seryjnej. I każdy klient publikują funkcja zarejestrował added
, changed
i
removed
na tej kwerendy na żywo, który karmi się jednym z pól korespondencji seryjnej. Nic więcej nie jest połączone ze skrzynkami scalającymi.
Najpierw kierowca Mongo. Kiedy jeden z klientów wstawia nowy dokument do Somestuff
, wyzwala to ponowne obliczenie. Sterownik Mongo ponownie uruchamia zapytanie o wszystkie dokumenty Somestuff
, porównuje wynik z poprzednim wynikiem w pamięci, znajduje jeden nowy dokument i wywołuje każdy z 1000 zarejestrowanych insert
wywołań zwrotnych.
Następnie funkcje publikowania. Niewiele się tu dzieje: każde z 1000 insert
wywołań zwrotnych wypycha dane do pola scalającego przez wywołanie added
.
Wreszcie, każde pole scalania porównuje te nowe atrybuty ze swoją znajdującą się w pamięci kopią pamięci podręcznej klienta. W każdym przypadku stwierdza, że wartości nie są jeszcze na kliencie i nie przesłaniają istniejącej wartości. Tak więc pole scalające emituje DATA
komunikat DDP na połączeniu SockJS do swojego klienta i aktualizuje swoją kopię w pamięci po stronie serwera.
Całkowity koszt procesora to koszt porównania jednego zapytania Mongo plus koszt 1000 pól scalających sprawdzających stan klientów i konstruowania nowego ładunku wiadomości DDP. Jedyne dane przesyłane przez łącze to pojedynczy obiekt JSON wysyłany do każdego z 1000 klientów, odpowiadający nowemu dokumentowi w bazie danych, oraz jeden komunikat RPC do serwera od klienta, który dokonał oryginalnej wstawki.
Optymalizacje
Oto, co zdecydowanie zaplanowaliśmy.
Bardziej wydajny sterownik Mongo. Mamy
zoptymalizowany sterownik
w 0.5.1 do uruchomienia tylko jednego obserwatora za wyraźną zapytania.
Nie każda zmiana bazy danych powinna powodować ponowne obliczenie zapytania. Możemy wprowadzić pewne automatyczne ulepszenia, ale najlepszym podejściem jest API, które pozwala deweloperowi określić, które zapytania należy ponownie uruchomić. Na przykład dla programisty jest oczywiste, że wstawienie wiadomości do jednego pokoju rozmów nie powinno unieważniać zapytania na żywo o wiadomości w drugim pokoju.
Sterownik Mongo, funkcja publikowania i pole scalające nie muszą działać w tym samym procesie ani nawet na tym samym komputerze. Niektóre aplikacje wykonują złożone zapytania na żywo i potrzebują więcej procesora do oglądania bazy danych. Inni mają tylko kilka odrębnych zapytań (wyobraź sobie silnik bloga), ale prawdopodobnie wielu połączonych klientów - potrzebują oni więcej procesora do pól scalających. Oddzielenie tych elementów pozwoli nam na niezależne skalowanie każdego elementu.
Wiele baz danych obsługuje wyzwalacze, które są uruchamiane po zaktualizowaniu wiersza i udostępniają stare i nowe wiersze. Dzięki tej funkcji sterownik bazy danych mógłby zarejestrować wyzwalacz zamiast odpytywania o zmiany.
skip
,$near
a$where
zawierające zapytań), które jest o wiele bardziej wydajny w obciążeniu procesora, przepustowości sieci i umożliwia skalowanie na aplikację serwery.Z mojego doświadczenia wynika, że korzystanie z wielu klientów podczas udostępniania ogromnej kolekcji w Meteor jest zasadniczo niewykonalne, począwszy od wersji 0.7.0.1. Spróbuję wyjaśnić dlaczego.
Jak opisano w powyższym poście, a także na https://github.com/meteor/meteor/issues/1821 , serwer meteor musi przechowywać kopię opublikowanych danych dla każdego klienta w polu scalania . To właśnie umożliwia magię Meteor, ale także powoduje, że duże współdzielone bazy danych są wielokrotnie przechowywane w pamięci procesu węzła. Nawet podczas korzystania z możliwej optymalizacji dla kolekcji statycznych, takich jak ( Czy istnieje sposób, aby stwierdzić, że kolekcja meteor jest statyczna (nigdy się nie zmieni)? ), Napotkaliśmy ogromny problem z wykorzystaniem procesora i pamięci w procesie Node.
W naszym przypadku publikowaliśmy zbiór 15 tys. Dokumentów dla każdego klienta, który był całkowicie statyczny. Problem polega na tym, że skopiowanie tych dokumentów do skrzynki scalającej klienta (w pamięci) po połączeniu w zasadzie spowodowało, że proces Node osiągnął 100% CPU na prawie sekundę, co spowodowało duże dodatkowe użycie pamięci. Jest to z natury nieskalowalne, ponieważ każdy łączący się klient rzuci serwer na kolana (a jednoczesne połączenia będą się wzajemnie blokować), a zużycie pamięci wzrośnie liniowo w liczbie klientów. W naszym przypadku każdy klient powodował dodatkowe ~ 60 MB zużycia pamięci, mimo że przesyłane surowe dane wynosiły tylko około 5 MB.
W naszym przypadku, ponieważ kolekcja była statyczna, rozwiązaliśmy ten problem, wysyłając wszystkie dokumenty jako
.json
plik, który został spakowany przez nginx i wczytywany do anonimowej kolekcji, co skutkowało transferem tylko ~ 1MB danych bez dodatkowego procesora lub pamięć w procesie węzła i znacznie szybszy czas ładowania. Wszystkie operacje na tej kolekcji zostały wykonane przy użyciu_id
s ze znacznie mniejszych publikacji na serwerze, co pozwala zachować większość zalet Meteora. Umożliwiło to skalowanie aplikacji do wielu innych klientów. Ponadto, ponieważ nasza aplikacja jest w większości tylko do odczytu, dodatkowo poprawiliśmy skalowalność, uruchamiając wiele instancji Meteor za nginx z równoważeniem obciążenia (choć z pojedynczym Mongo), ponieważ każda instancja Node jest jednowątkowa.Jednak kwestia udostępniania dużych, zapisywalnych kolekcji wielu klientom jest problemem inżynieryjnym, który musi zostać rozwiązany przez firmę Meteor. Prawdopodobnie istnieje lepszy sposób niż przechowywanie kopii wszystkiego dla każdego klienta, ale wymaga to poważnego przemyślenia jako problemu z systemami rozproszonymi. Obecne problemy związane z ogromnym zużyciem procesora i pamięci po prostu się nie skalują.
źródło
Eksperyment, którego możesz użyć, aby odpowiedzieć na to pytanie:
meteor create --example todos
Wskazówki dotyczące korzystania z WKI można znaleźć w tym artykule . Jest trochę nieaktualny, ale w większości nadal aktualny, szczególnie w przypadku tego pytania.
źródło
To wciąż ma rok i dlatego myślę, że wiedza sprzed „Meteor 1.0”, więc wszystko mogło się znowu zmienić? Nadal się tym zajmuję. http://meteorhacks.com/does-meteor-scale.html prowadzi do pytania „Jak skalować Meteor?” artykuł http://meteorhacks.com/how-to-scale-meteor.html
źródło