Jak zaprojektować aplikację internetową ajax dla wielu użytkowników, aby była jednocześnie bezpieczna

Mam stronę, która pokazuje dużą ilość danych z serwera. Komunikacja odbywa się za pośrednictwem ajax.

Za każdym razem, gdy użytkownik wchodzi w interakcję i zmienia te dane (powiedzmy, że użytkownik A zmienia nazwę czegoś), mówi on serwerowi, aby wykonał akcję, a serwer zwraca nowe zmienione dane.

Jeśli użytkownik B uzyska dostęp do strony w tym samym czasie i utworzy nowy obiekt danych, ponownie poinformuje serwer za pomocą ajax i serwer powróci z nowym obiektem dla użytkownika.

On A ' S strona mamy dane z przemianowanym obiektem. A na stronie B mamy dane z nowym obiektem. Na serwerze dane mają zarówno przemianowany obiekt, jak i nowy obiekt.

Jakie są moje opcje synchronizacji strony z serwerem, gdy wielu użytkowników korzysta z niej jednocześnie?

Takie opcje jak blokowanie całej strony lub wysyłanie całego stanu do Użytkownika przy każdej zmianie są raczej unikane.

Jeśli to pomaga, w tym konkretnym przykładzie strona wywołuje statyczne webmethod, który uruchamia procedurę składowaną w bazie danych. Procedura składowana zwróci wszystkie dane, które zmieniła i nie będzie ich więcej. Następnie statyczny webmethod przekazuje klientowi zwrot procedury składowanej.

Bounty Edit:

Jak zaprojektować aplikację internetową dla wielu użytkowników, która używa Ajax do komunikacji z serwerem, ale unika problemów z współbieżnością?

Tj. współbieżny dostęp do funkcjonalności i danych w bazie danych bez ryzyka wystąpienia danych lub korupcja państwa

Author: Raynos, 2011-01-27

8 answers

Przegląd:

  • Intro
  • Architektura serwera
  • Architektura klienta
  • przypadek aktualizacji
  • Commit case
  • konflikt sprawy
  • wydajność i skalowalność

Cześć Raynos,

Nie będę tutaj omawiał żadnego konkretnego produktu. To, o czym inni wspominali, to dobry zestaw narzędzi, na który można już rzucić okiem (może dodać węzeł.js do tej listy).

Z architektonicznego punktu widzenia wydaje się, że masz ten sam problem, który można zobaczyć w oprogramowanie do kontroli wersji. Jeden użytkownik sprawdza zmianę obiektu, inny użytkownik chce zmienić ten sam obiekt w inny sposób => konflikt. Musisz integrować zmiany użytkowników w obiektach, a jednocześnie być w stanie dostarczać aktualizacje na czas i skutecznie, wykrywając i rozwiązując konflikty, takie jak powyższy.

Gdybym był na Twoim miejscu, rozwinąłbym coś takiego:

1. Server-Side:

  • Określ rozsądny poziom, na którym zdefiniujesz co nazwałbym " atomic artefacts "(strona? Obiekty na stronie? Wartości wewnątrz obiektów?). Będzie to zależeć od serwerów WWW, sprzętu bazodanowego i buforowania, # użytkownika, # obiektów itp. Niełatwa decyzja.

  • Dla każdego artefaktu atomowego mają:

    • unikalny identyfikator aplikacji
    • an incrementing version-id
    • mechanizm blokujący dostęp do zapisu (mutex)
    • mała historia lub" changelog " wewnątrz bufora ringbuffera (pamięć dzielona działa dobrze dla nich). Pojedyncza para klucz-wartość może być również OK, choć mniej rozszerzalna. zobacz http://en.wikipedia.org/wiki/Circular_buffer
  • Komponent serwera lub pseudo-serwera, który jest w stanie dostarczyć odpowiednie changelogi do podłączonego użytkownika efektywnie. Obserwator-wzór jest twoim przyjacielem do tego.

2. Client-Side:

  • Klient javascript, który może mieć długotrwałe połączenie HTTP z wyżej wymienionym serwerem, lub używa lekkiego sondażu.

  • Komponent JavaScript artifact-updater, który odświeża zawartość witryny, gdy podłączony klient javascript powiadomi o zmianach w obserwowanej historii artefaktów. (ponownie wzór obserwatora może być dobrym wyborem)

  • Komponent JavaScript artifact-committer, który może zażądać zmiany artefaktu atomowego, próbując zdobyć blokadę mutex. Wykryje, czy stan artefaktu został zmieniony przez innego użytkownika zaledwie kilka sekund przed (latancja klienta javascript i czynników procesu zatwierdzania w) poprzez porównanie znanego artifact-version-id klienta i bieżącego Artifact-version-id serwera.

  • Javascript conflict-solver pozwalający na człowieka, który-zmienia-jest-właściwą decyzją. Możesz nie chcieć po prostu powiedzieć użytkownikowi " ktoś był szybszy od Ciebie. Usunąłem resztę. Idź się rozpłakać.". Wiele opcji z raczej technicznych różnic lub bardziej przyjaznych dla użytkownika rozwiązań wydaje się możliwych.

Więc jak to się potoczy ...

Przypadek 1: Rodzaj-of-sequence-diagram dla aktualizacji:

  • przeglądarka renderuje stronę
  • javascript "widzi" artefakty, z których każdy ma co najmniej jedno pole wartości, unikalny-i identyfikator wersji
  • uruchamia się klient javascript, prosząc o "oglądanie" historii znalezionych artefaktów począwszy od ich znalezionych wersji (starsze zmiany nie są interesujące)
  • proces serwera notuje żądanie i stale sprawdza i / lub wysyła historię
  • wpisy historyczne mogą contain simple notifications "Artifact x has changed, Client pls request data" allowing the client to poll niezależnie lub Pełne zestawy danych"artifact x has changed to value foo"
  • JavaScript artifact-updater robi, co może, aby pobrać nowe wartości, gdy tylko staną się znane jako zaktualizowane. Wykonuje nowe żądania ajax lub jest zasilany przez Klienta javascript.
  • strony DOM-treść jest aktualizowana, użytkownik jest opcjonalnie powiadamiany. Oglądanie historii trwa.

Przypadek 2: Teraz dla:

  • Artifact-committer zna żądaną nową wartość z danych wejściowych użytkownika i wysyła żądanie zmiany do serwera
  • serwer mutex został przejęty
  • serwer otrzymuje "Hej, znam stan artifact x z wersji 123, pozwól mi ustawić go na wartość foo pls."
  • Jeśli Serverside wersja artifact x jest równa (nie może być mniejsza) niż 123 nowa wartość jest akceptowana, nowy ID wersji 124 wygenerowany.
  • nowy stan-informacje " aktualizacja do wersji 124" i opcjonalnie nowa wartość foo jest umieszczana na początku bufora ringbuffera artefaktu x (changelog/history)
  • serverside mutex is released
  • zgłaszający Artifact commit chętnie otrzyma potwierdzenie zatwierdzenia wraz z nowym identyfikatorem.
  • tymczasem komponent serverside Server wciąż przepycha bufory ringbuffers do podłączonych klientów. Wszyscy klienci Obserwujący bufor artifact x otrzymają nowe informacje o stanie i wartości w ramach zwykłego opóźnienia (patrz case 1.)

Przypadek 3: dla konfliktów:

  • Artifact committer zna żądaną nową wartość z danych wejściowych użytkownika i wysyła żądanie zmiany do serwera
  • W międzyczasie inny użytkownik pomyślnie zaktualizował ten sam artefakt (patrz przypadek 2.) ale z powodu różnych opóźnień jest to jeszcze nieznane naszemu drugiemu użytkownikowi.
  • W ten sposób mutex po stronie serwera jest pozyskiwany (lub czekany, aż "szybszy" użytkownik dokona zmiany)
  • serwer otrzymuje "Hej, znam stan artifact x z wersji 123, ustawię na wartość foo."
  • na serwerze wersja artifact x ma już 124. Klient żądający nie może znać wartości, którą nadpisałby.
  • oczywiście serwer musi odrzucić żądanie zmiany (nie licząc interweniujących przez Boga priorytetów nadpisania), zwolnić mutex i jest na tyle uprzejmy, aby odesłać nowy identyfikator wersji i nową wartość bezpośrednio do klienta.
  • skonfrontowany z odrzuconym żądaniem zatwierdzenia i wartością użytkownika żądającego zmiany jeszcze nie wiedziałem, że committer artefaktów javascript odnosi się do rozwiązującego konflikty, który wyświetla i wyjaśnia problem użytkownikowi.
  • użytkownik, przedstawiony z niektórymi opcjami przez inteligentnego rozwiązującego konflikty JS, może ponownie spróbować zmienić wartość.
  • Gdy użytkownik wybrał wartość, którą uważa za właściwą, proces rozpoczyna się od przypadku 2 (lub przypadku 3, jeśli ktoś inny był szybszy, ponownie)
Kilka słów o wydajności i skalowalności]}

Ankieta HTTP vs. HTTP "pushing"

    Ankieta tworzy żądania, jeden na sekundę, pięć na sekundę, niezależnie od tego, co uznasz za akceptowalne opóźnienie. Może to być dość okrutne dla Twojej infrastruktury, jeśli nie skonfigurujesz swojego (Apache?) i (php?) wystarczająco dobrze, aby być" lekkim " przystawką. Pożądane jest zoptymalizowanie żądania ankiety po stronie serwera, tak aby trwało ono znacznie krócej niż długość interwału ankiety. Podzielenie tego środowiska uruchomieniowego na pół może oznaczać obniżenie całego systemu obciążenie do 50%,
  • przepychanie przez HTTP (zakładając, że webworkerzy są zbyt daleko, aby je obsługiwać) będzie wymagało posiadania jednego procesu apache / lighttpd dostępnego dla każdego użytkownika przez cały czas. Pamięć rezydentna zarezerwowana dla każdego z tych procesów i całkowita pamięć Twojego systemu będzie jednym bardzo pewnym ograniczeniem skalowania, które napotkasz. Konieczne będzie zmniejszenie ilości pamięci łącza, a także ograniczenie ilości pracy procesora i wejścia/Wyjścia wykonywanej w każdym z te (chcesz dużo snu/czasu bezczynności)

Skalowanie Backend

  • zapomnij o bazie danych i systemie plików, będziesz potrzebował jakiegoś backendu opartego na pamięci współdzielonej do częstego wyszukiwania (jeśli klient nie przepytuje bezpośrednio, to każdy uruchomiony proces serwera)
  • jeśli wybierzesz memcache, możesz skalować lepiej, ale nadal jest drogi
  • mutex dla commitów musi działać globalnie, nawet jeśli chcesz mieć wiele serwerów frontend do załadowania.

Skalowanie Frontend

  • niezależnie od tego, czy przeprowadzasz ankietę, czy otrzymujesz "Wypychy", spróbuj uzyskać informacje o wszystkich obserwowanych artefaktach w jednym kroku.

" kreatywne " poprawki

  • Jeśli klienci przeprowadzają ankiety i wielu użytkowników ogląda te same artefakty, możesz spróbować opublikować historię tych artefaktów jako plik statyczny, pozwalając apache ' owi na buforowanie go, mimo to odświeżając go po stronie serwera, gdy artefakty ulegną zmianie. To usuwa PHP / memcache gry Niektóre na życzenie. Lighthttpd jest bardzo skuteczny w obsłudze plików statycznych.
  • Użyj sieci dostarczania treści, takiej jak cotendo.com żeby wypchnąć tam historię artefaktów. Opóźnienie push będzie większe, ale skalowalność to marzenie]}
  • napisz prawdziwy serwer (nie używając HTTP), do którego użytkownicy łączą się za pomocą Javy lub Flasha(?). Masz do czynienia z obsługą wielu użytkowników w jednym serwerze-wątku. Jazda na rowerze przez otwarte gniazda, wykonywanie (lub delegowanie) wymaganej pracy. Może skalować za pomocą procesów rozwidlania lub uruchamianie kolejnych serwerów. Muteksy muszą jednak pozostać unikalne na całym świecie.
  • w zależności od scenariuszy obciążenia Grupuj frontend-i backend-serwery według zakresów Artifact-id. Pozwoli to na lepsze wykorzystanie pamięci trwałej (Żadna baza danych nie zawiera wszystkich danych) i umożliwi skalowanie muteksowania. Twój javascript musi utrzymywać połączenia z wieloma serwerami jednocześnie.

Mam nadzieję, że to może być początek dla Twoich własnych pomysłów. Jestem pewien, że jest ich dużo więcej. możliwości. Jestem bardziej niż mile widziana jakakolwiek krytyka lub ulepszenia tego postu, wiki jest włączona.

Christoph Strasen

 156
Author: Christoph Strasen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-12 06:35:52

Wiem, że to stare pytanie, ale pomyślałem, że się odezwę.

OT (operational transforms) wydaje się być dobrym rozwiązaniem dla Twoich wymagań dotyczących jednoczesnej i spójnej edycji wielu użytkowników. Jest to technika używana w Google Docs (i była również używana w Google Wave):

Istnieje Biblioteka oparta na JS do korzystania z transformacji operacyjnych-ShareJS (http://sharejs.org/), napisany przez członka zespołu Google Wave.

I jeśli chcesz, jest full MVC web-framework-DerbyJS (http://derbyjs.com / ) zbudowany na ShareJS, który robi to wszystko za Ciebie.

Używa BrowserChannel do komunikacji między serwerem a klientami (i uważam, że wsparcie WebSockets powinno być w pracy - było tam wcześniej przez Socket.IO, ale został usunięty z powodu problemów dewelopera z Socket.io) dokumenty dla początkujących są w tej chwili nieco rzadkie.

 13
Author: victorhooi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-19 04:34:40

Rozważyłbym dodanie zmodyfikowanego znacznika czasu dla każdego zbioru danych. Jeśli więc aktualizujesz tabele db, zmienisz odpowiednio zmodyfikowany znacznik czasu. Korzystając z AJAX, możesz porównać zmodyfikowany znacznik czasu klienta ze znacznikiem czasu źródła danych - jeśli użytkownik jest kiedykolwiek w tyle, zaktualizuj wyświetlacz. Podobne do tego, jak ta strona okresowo sprawdza pytanie, aby sprawdzić, czy ktoś inny odpowiedział podczas pisania odpowiedzi.

 5
Author: Chris Baker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-10 13:57:20

Musisz użyć technik push (znanych również jako Comet lub reverse Ajax), aby propagować zmiany dla użytkownika, gdy tylko zostaną wprowadzone do db. Najlepszą dostępną obecnie techniką dla tego wydaje się Ajax long polling, ale nie jest obsługiwana przez każdą przeglądarkę, więc trzeba awaryjne. Na szczęście istnieją już rozwiązania, które obsługują to za Ciebie. Wśród nich są: orbited.org i już wspomniane socket.io.

W przyszłości będzie łatwiejszy sposób, który jest nazywa się WebSockets, ale nie jest jeszcze pewne, kiedy ten standard będzie gotowy do prime time, ponieważ istnieją obawy dotyczące bezpieczeństwa dotyczące bieżącego stanu standardu.

Nie powinno być problemów z współbieżnością w bazie danych z nowymi obiektami. Ale gdy użytkownik edytuje obiekt, serwer musi mieć pewną logikę, która sprawdza, czy obiekt został edytowany lub usunięty w międzyczasie. Jeśli obiekt został usunięty, rozwiązanie jest ponownie proste: po prostu Odrzuć edycję.

Ale najbardziej trudny problem pojawia się, gdy wielu użytkowników edytuje ten sam obiekt w tym samym czasie. Jeśli Użytkownik 1 i 2 zaczną edytować obiekt w tym samym czasie, obaj dokonają zmian na tych samych danych. Załóżmy, że zmiany wprowadzone przez Użytkownika 1 są najpierw wysyłane na serwer, podczas gdy użytkownik 2 nadal edytuje dane. Następnie masz dwie opcje: możesz spróbować połączyć zmiany użytkownika 1 z danymi użytkownika 2 lub powiedzieć użytkownikowi 2, że jego dane są nieaktualne i wyświetlić mu komunikat o błędzie, gdy tylko jego dane są wysyłane na serwer. Ta ostatnia nie jest zbyt przyjazna dla użytkownika, ale ta pierwsza jest bardzo trudna do wdrożenia.

Jedną z niewielu implementacji, która po raz pierwszy tak naprawdę to zrobiła, był EtherPad , który został przejęty przez Google. Wierzę, że następnie wykorzystali niektóre z technologii EtherPad w Google Docs i Google Wave, ale nie mogę powiedzieć, że na pewno. Google również opensourced EtherPad, więc może warto zajrzeć, w zależności od tego, co próbujesz zrób.

To naprawdę nie jest łatwe do zrobienia tego jednocześnie edycji rzeczy, ponieważ nie jest możliwe, aby wykonać operacje atomowe w Internecie z powodu opóźnienia. Może Ten artykuł pomoże Ci dowiedzieć się więcej na ten temat.

 3
Author: Jannes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-17 00:24:37

Próba napisania tego wszystkiego samemu to wielka praca i bardzo trudno jest to zrobić dobrze. Jedną z opcji jest użycie frameworka, który jest zbudowany, aby utrzymać klientów w synchronizacji z bazą danych, i ze sobą, w czasie rzeczywistym.

Odkryłem, że Meteor framework robi to dobrze ( http://docs.meteor.com/#reactivity).

"Meteor obejmuje koncepcję programowania reaktywnego. Oznacza to, że możesz napisać swój kod w prostym stylu imperatywnym, a rezultatem będzie automatycznie przeliczane za każdym razem, gdy dane ulegną zmianie, od której zależy Twój kod."

" ten prosty wzór (obliczenia reaktywne + reaktywne źródło danych) ma szerokie zastosowanie. Programista jest zapisywany z wypisywania / resubscribe połączeń i upewniając się, że są one wywoływane w odpowiednim czasie, eliminując całe klasy kodu propagacji danych, które w przeciwnym razie zatkają aplikację z podatną na błędy logiką."

 2
Author: mb.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 04:51:13

Nie mogę uwierzyć, że nikt nie wspomniał o Meteorze . Na pewno jest to nowy i niedojrzały framework (i oficjalnie obsługuje tylko jeden DB), ale zajmuje całą pracę gruntową i myślenie o aplikacji dla wielu użytkowników, takiej jak opisywany plakat. W rzeczywistości nie można nie zbudować aplikacji do aktualizacji na żywo dla wielu użytkowników. Oto krótkie podsumowanie:

  • Wszystko jest w node.js (JavaScript lub CoffeeScript), dzięki czemu możesz udostępniać takie rzeczy jak walidacje między Klientem a serwerem.
  • It używa websockets, ale może się wycofać dla starszych przeglądarek
  • skupia się na natychmiastowych aktualizacjach lokalnego obiektu (tzn. interfejs wydaje się zgryźliwy), ze zmianami wysyłanymi do serwera w tle. Tylko atomic updates są dozwolone, aby ułatwić mieszanie aktualizacji. Aktualizacje odrzucone na serwerze są wycofywane.
  • jako bonus obsługuje przeładowania kodu na żywo dla Ciebie i zachowa stan użytkownika, nawet gdy aplikacja zmieni się radykalnie.

Meteor jest na tyle prosty, że sugerowałbym Ci w przynajmniej spójrz na to, żeby ukraść pomysły.

 1
Author: BraveNewCurrency,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 03:09:45

Te strony wikipedii mogą pomóc dodać perspektywę do nauki o concurrency i Concurrency computing do projektowania ajax aplikacja webowa która albo ściąga albo jest wypychana Stan Zdarzenie (EDA) wiadomości w wzór wiadomości . Zasadniczo wiadomości są replikowane do subskrybentów kanałów, którzy reagują na zmiany zdarzeń i synchronizacji prośby.

Istnieje wiele form współbieżnych internetowych oprogramowanie do współpracy .

Istnieje wiele bibliotek klientów HTTP API dla etherpad-lite , współpracującego edytora czasu rzeczywistego .

Django-realtime-playground implementuje aplikację do czatu w czasie rzeczywistym w Django z różnymi technologiami czasu rzeczywistego, takimi jak Socket.io .

AppEngine i AppScale implementują AppEngine Channel API ; które różni się od Google realtime API, co jest pokazane przez googledrive/realtime-playground .
 1
Author: Wes Turner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 06:55:06

Techniki push Po stronie serwera są sposobem, aby przejść tutaj. Kometa jest (czy była?) buzz word.

Konkretny kierunek, który obrasz, zależy w dużej mierze od Twojego stosu serwerów i tego, jak elastyczny jesteś. Jeśli możesz, rzuciłbym okiem na socket.io , który zapewnia między przeglądarkową implementację websockets, które zapewniają bardzo usprawniony sposób dwukierunkowej komunikacji z serwerem, umożliwiając serwerowi wysyłanie aktualizacji do klientów.

In w szczególności, zobacz demonstrację autorstwa biblioteki, która pokazuje niemal dokładnie sytuację, którą opisujesz.

 0
Author: davin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-15 18:34:23