94 votes

Comment concevoir une application de web ajax multi-utilisateurs simultanément sûrs

J'ai une page web qui affiche une grande quantité de données sur le serveur. La communication se fait via ajax.

Chaque fois que l'utilisateur interagit et les modifications de ces données (Disons Un utilisateur renomme quelque chose), il indique au serveur de l'action et le serveur renvoie les données modifiées.

Si l'utilisateur B accède à la page en même temps et crée un nouvel objet de données, il sera de nouveau dire au serveur via ajax et le serveur sera de retour avec le nouvel objet pour l'utilisateur.

Sur Une de la page, nous avons les données avec un objet renommé. Et sur B de la page, nous avons les données avec un nouvel objet. Sur le serveur de données est à la fois un objet renommé et un nouvel objet.

Quelles sont mes options pour garder la page en synchronisation avec le serveur lorsque plusieurs utilisateurs l'utilisent en même temps?

Des options telles que le verrouillage de l'ensemble de la page ou la vidange de l'ensemble de l'état de l'utilisateur sur tous les changements sont plutôt à éviter.

Si cela peut aider, dans cet exemple précis de la page web des appels statiques webmethod qui exécute une procédure stockée dans la base de données. La procédure stockée renvoie toutes les données qu'il a changé et rien de plus. La statique webmethod transmet ensuite le retour de la procédure stockée pour le client.

Bounty Edit:

Comment concevoir un environnement multi-utilisateur de l'application web qui utilise Ajax pour communiquer avec le serveur, mais évite les problèmes de simultanéité?

I. e. l'accès simultané à des fonctionnalités et à des données sur une base de données sans aucun risque pour les données ou la corruption de l'état

157voto

Christoph Strasen Points 1928

Vue d'ensemble:

  • Intro
  • L'architecture de serveur
  • Architecture Client
  • Mise à jour de cas
  • S'engager cas
  • Conflit
  • La Performance et l'évolutivité

Salut Raynos,

Je ne vais pas discuter de tout produit en particulier ici. Ce que d'autres ont mentionné est un bon jeu d'outils de regarder déjà (peut-être ajouter node.js à cette liste).

À partir d'un architectureal point de vue, vous semblez avoir le même problème qui peut être vu dans le logiciel de contrôle de version. Un utilisateur rentre un changement à un objet, un autre utilisateur veut modifier l'objet même d'une autre manière => conflit. Vous devez intégrer les modifications des utilisateurs aux objets, alors que dans le même temps être en mesure de livrer des mises à jour en temps opportun et efficacement, la détection et la résolution des conflits comme celui ci-dessus.

Si j'étais à votre place, je voudrais développer quelque chose comme ceci:

1. Côté Serveur:

  • Déterminer un niveau raisonnable qui vous permettrait de définir ce que j'appelle "atomique artefacts" (la page? Les objets sur la page? Des valeurs à l'intérieur des objets?). Cela dépendra de vos serveurs web, base de données & matériel de mise en cache, nombre d'utilisateurs, nombre d'objets, etc. Pas une décision facile à prendre.

  • Pour chaque atomique artefact avoir:

    • une application unique-id
    • une incrémentation de la version id
    • un mécanisme de verrouillage pour l'accès en écriture (mutex peut-être)
    • une petite histoire ou "changelog" à l'intérieur d'un ringbuffer (mémoire partagée fonctionne bien pour ceux). Une seule paire clé-valeur peut être OK aussi, bien que moins extensible. voir http://en.wikipedia.org/wiki/Circular_buffer
  • Un serveur ou d'un pseudo-composant serveur qui est en mesure de livrer pertinentes changelogs pour un utilisateur connecté efficacement. L'observateur de Modèle est votre ami pour cela.

2. Côté Client:

  • Un client javascript qui est capable d'avoir une Connexion HTTP vers ledit serveur ci-dessus, ou utilise légère interrogation.

  • Un javascript artefact composant de mise à jour qui actualise le contenu des sites lorsque connecté, le client javascript informe des changements de contrôle des artefacts de l'histoire. (encore une fois un modèle observateur pourrait être un bon choix)

  • Un javascript artefact committer composant qui peut demander à changer de atomique artefact, d'essayer d'acquérir des mutex lock. Il permet de détecter si l'état de l'objet, il avait été modifié par un autre utilisateur en quelques secondes seulement avant (latancy de javascript client et le processus de validation des facteurs) en comparant connu infoclient artefact-version-code et actuel serverside artefact-version-code.

  • Un conflit javascript-solveur permettant pour un homme qui-change-est-le-droit decission. Vous ne voulez pas de dire à l'utilisateur "Quelqu'un a été plus rapide que vous. J'ai supprimé votre changement. Aller pleurer.". De nombreuses options de plutôt technique diffs ou des solutions plus conviviales semble possible.

Alors, comment serait-il rouler ...

Cas 1: la nature-de-squence-schéma électrique pour la mise à jour:

  • Le navigateur affiche la page
  • javascript "voit" des objets qui, chacun ayant au moins une valeur de champ, unique et une version id
  • javascript client est commencé, demandant à "regarder" le trouvé des artefacts de l'histoire à partir de leurs versions trouvées (les plus anciens les changements ne sont pas intéressantes)
  • Le processus de serveur de notes de la demande et de la permanence des contrôles et/ou envoie de l'histoire
  • Les entrées de l'historique peut contenir de simples notifications "objet x a changé, client pls demande de données" permettant au client d'interroger de façon indépendante ou complète de jeux de données "objet x a changé de valeur foo"
  • javascript artefact de mise à jour fait ce qu'il peut pour aller chercher de nouvelles valeurs dès qu'ils sont connus pour avoir des mises à jour. Il exécute de nouvelles requêtes ajax ou s'il est alimenté par le client javascript.
  • Les pages de DOM-le contenu est mis à jour, l'utilisateur est éventuellement notifiées. L'histoire d'observation continue.

Cas 2: Maintenant, pour commettre:

  • artefact-committer sait que la nouvelle valeur de la saisie de l'utilisateur et envoie un changement de la demande sur le serveur
  • serverside mutex est acquis
  • Serveur reçoit "Hé, je sais artefact x de l'état à partir de la version 123, permettez-moi de valeur foo pls."
  • Si le Serverside version de l'artefact x est égal à (ne peut pas être inférieure à) 123 la nouvelle valeur est acceptée, une nouvelle id de version de 124 généré.
  • Le nouvel état-de l'information "mis à jour en version 124" et optionnel nouvelle valeur foo sont placés au début de l'artefact x ringbuffer (changelog/histoire)
  • serverside mutex est publiée
  • demander artefact committer est heureux de recevoir un engagement de confirmation ainsi que la nouvelle carte d'identité.
  • pendant ce temps serverside composant de serveur de garde d'interrogation/poussant le ringbuffers aux clients connectés. Tous les clients de regarder le tampon de l'artefact x obtiendrez les nouvelles informations d'état et de la valeur au sein de leur habitude latancy (Voir cas 1.)

Cas 3: les conflits:

  • artefact committer sait disered nouvelle valeur à partir de la saisie de l'utilisateur et envoie un changement de la demande sur le serveur
  • dans le même temps un autre utilisateur mis à jour le même objet avec succès (voir cas 2.) mais en raison de divers latancies c'est encore inconnue à notre autre utilisateur.
  • Ainsi, un serverside mutex est acquis (ou attendu jusqu'à ce que le "plus rapide" de l'utilisateur engage sa modification)
  • Serveur reçoit "Hé, je sais artefact x de l'état à partir de la version 123, permettez-moi de valeur foo."
  • Sur le Serverside la version de l'artefact x est maintenant 124 déjà. La demande du client ne peut pas savoir la valeur qu'il serait l'écrasement.
  • Évidemment, le Serveur a pour rejeter la demande de modification (pas de comptage en dieu-intervenant remplacer les priorités), libère le mutex et a la gentillesse d'envoyer la nouvelle version-id et de la nouvelle valeur directement au client.
  • confronté à un refus de commettre demande et une valeur, le changement d'utilisateur demandeur ne savait pas encore, le javascript artefact committer se réfère à la résolution de conflit qui affiche et explique le problème à l'utilisateur.
  • L'utilisateur, étant présenté avec quelques options, par le smart conflit de résolution JS, est autorisé une autre tentative de modifier la valeur.
  • Une fois que l'utilisateur a sélectionné une valeur qu'il juge droite, le processus commence à partir de cas 2 (ou 3 si quelqu'un d'autre a été plus rapide, encore une fois)

Quelques mots sur la Performance Et l'Évolutivité

HTTP Interrogation vs HTTP "poussant"

  • Interrogation crée des demandes, l'une par seconde, 5 par seconde, ce qui vous considèrent comme un temps d'attente acceptable. Cela peut être assez cruel pour votre infrastructure si vous ne configurez pas votre (Apache?) et (php?) assez bien pour être "léger" starters. Il est souhaitable d'optimiser la requête d'interrogation sur la serverside, de sorte qu'il fonctionne pour beaucoup moins de temps que la longueur de l'intervalle d'interrogation. Le fractionnement que l'exécution dans la moitié pourrait bien signifier l'abaissement de l'ensemble de votre système de charge jusqu'à 50%,
  • Poussant via HTTP (en les web workers sont trop loin pour les soutenir) vous obligera à avoir un apache/lighthttpd processus disponibles pour chaque utilisateur tout le temps. Le résident de mémoire réservée pour chacun de ces processus et vos systèmes de mémoire totale sera une très certaine mise à l'échelle de la limite que vous rencontrerez. La réduction de l'empreinte mémoire de la connexion sera nécessaire, ainsi que la limitation de la quantité continue CPU et I/O travail effectué dans chacun de ces (vous voulez que beaucoup de sommeil/arrêt)

backend de mise à l'échelle

  • Oubliez pas de base de données et de système de fichiers, vous aurez besoin d' une sorte de mémoire partagée basée backend pour la fréquence d'interrogation (si le client n'interroge pas directement chaque exécution de processus serveur)
  • si vous allez pour memcache vous pouvez mettre à l'échelle de mieux, mais sa reste cher
  • Le mutex pour s'engage a travailler globalement, même si vous voulez avoir de multiples serveurs frontaux pour loadbalance.

interface de mise à l'échelle

  • peu importe si vous êtes d'interrogation ou de la réception de "pousse", essayez d'obtenir de l'information pour tous regardé les artefacts en une seule étape.

"créatif" tweaks

  • Si les clients sont de vote et de nombreux utilisateurs ont tendance à regarder les mêmes artefacts, vous pouvez essayer de publier l'histoire de ces artefacts comme un fichier statique, ce qui permet à apache de cache, néanmoins il rafraîchissante sur la serverside lorsque les artefacts de changement. Cela prend du PHP/memcache hors-jeu à certaines demandes. Lighthttpd est très efficace à servir les fichiers statiques.
  • l'utilisation d'un réseau de diffusion de contenu comme cotendo.com pour pousser artefact de l'histoire. Le push-latancy sera plus grand, mais l'évolutivité est un rêve
  • écrire un vrai serveur (HTTP) que les utilisateurs se connectent à l'aide de java ou flash(?). Vous avez à traiter avec le service de nombreux utilisateurs dans un serveur-fil. À vélo à travers les sockets, en train de faire (ou déléguer) le travail requis. Peut via un fork de processus ou de départ de plusieurs serveurs. Mutex rester globalement unique.
  • En fonction de la charge scenarious groupe de votre frontend et backend-serveurs par artefact-id plages. Cela permettra une meilleure utilisation de la persistance de la mémoire (pas de base de données dispose de toutes les données) et rend possible à l'échelle de la mutexing. Votre javascript doit maintenir des connexions à plusieurs serveurs en même temps.

Eh bien, je espère que cela peut être un bon départ pour vos propres idées. Je suis sûr qu'il ya beaucoup plus de possibilités. Je suis plus qu'accueillant toute critique ou les améliorations apportées à ce poste, le wiki est activé.

Christoph Strasen

13voto

victorhooi Points 3083

Je sais que c'est une vieille question, mais je pensais que je viens de mentionner.

OT (transformations) semble comme un bon ajustement pour votre exigence simultanée et cohérente de l'édition multi-utilisateur. C'est une technique utilisée dans Google Docs (et a également été utilisé dans Google Wave):

Il y a un JS-fondé de la bibliothèque pour l'utilisation Opérationnelle Transforme - ShareJS (http://sharejs.org/), écrit par un membre de la "Google Wave" de l'équipe.

Et si vous voulez, il ya une pleine MVC web-framework - DerbyJS (http://derbyjs.com/) construit sur ShareJS qui fait tout pour vous.

Il utilise BrowserChannel pour la communication entre le serveur et les clients (et je crois que les WebSockets soutien devrait être dans les travaux - il était là précédemment via Socket.IO, mais a été prise par le développeur de problèmes avec la Prise.io) Débutant docs sont un peu clairsemé à l'heure actuelle, cependant.

5voto

Chris Points 20836

Je considérerais ajoutant basés sur le temps mis à jour le timbre pour chaque dataset. Donc, si vous mettez à jour les tables db, vous modifieriez la mis à jour le timestamp en conséquence. En utilisant AJAX, vous pouvez comparer le timestamp modifiés du client avec l’horodatage de la source de données - si l’utilisateur n’est jamais derrière, mettre à jour l’affichage. Semblable à la façon dont ce site vérifie une question périodiquement pour voir si quelqu'un d’autre a répondu pendant que vous tapez une réponse.

3voto

Jannes Points 969

Vous avez besoin d'utiliser push techniques (aussi connu comme la Comète ou reverse Ajax) pour propager les modifications à l'utilisateur dès qu'ils sont faits à la db. La meilleure technique disponible actuellement pour ce qui semble être de l'Ajax d'interrogation, mais il n'est pas pris en charge par tous les navigateurs, de sorte que vous besoin de base. Heureusement, il existe déjà des solutions qui gèrent cela pour vous. Parmi eux se trouvent: orbited.org et le déjà mentionné de la prise.io.

Dans le futur il y aura un moyen plus facile de faire ce qui est appelé les WebSockets, mais il n'est pas sûr pourtant, alors que cette norme sera prêt pour la premier fois qu'il y a des problèmes de sécurité sur l'état actuel de la norme.

Il ne devrait pas être les problèmes de simultanéité dans la base de données avec de nouveaux objets. Mais lorsqu'un utilisateur modifie un objet le serveur doit avoir une certaine logique qui vérifie si l'objet a été modifié ou supprimé dans l'intervalle. Si l'objet a été supprimé de la solution est, de nouveau, simple: il suffit de jeter le modifier.

Mais le problème le plus difficile semble, lorsque plusieurs utilisateurs modifient le même objet en même temps. Si l'Utilisateur 1 et 2 commencer l'édition d'un objet dans le même temps, ils vont à la fois faire leurs modifications sur les mêmes données. Disons que les modifications de l'Utilisateur 1 sont envoyées au serveur à tout Utilisateur 2 est encore l'édition des données. Vous avez alors deux options: Vous pouvez essayer de fusion de l'Utilisateur 1 les changements dans les données de l'Utilisateur 2 ou vous pourriez dire à l'Utilisateur 2 que ses données sont en dehors de la date et de l'affichage de lui un message d'erreur dès que ses données est envoyé au serveur. Le dernier n'est pas très convivial, mais la première est très difficile à mettre en œuvre.

L'un des quelques implémentations qui a vraiment eu ce droit pour la première fois, c'était EtherPad, qui a été rachetée par Google. Je crois qu'ils ont ensuite utilisé une de EtherPad technologies dans Google Docs et Google Wave, mais je ne peux pas dire que pour vous. Google aussi opensourced EtherPad, alors peut-être que vaut un coup d'oeil, en fonction de ce que vous essayez de faire.

C'est vraiment pas facile de le faire simultanément l'édition de trucs, parce que ce n'est pas possible de faire des opérations atomiques sur le web en raison de la latence. Peut-être que cet article vous permettra d'en apprendre plus sur le sujet.

2voto

mb. Points 666

Essayez d'écrire tout cela vous-même est un gros travail, et il est très difficile de l'obtenir. Une option est d'utiliser un cadre qui est construit pour garder les clients en synchronisation avec la base de données, et les uns avec les autres, en temps réel.

J'ai trouvé que le Météore cadre de ce bien (http://docs.meteor.com/#reactivity).

"Meteor embrasse le concept de réactif de programmation. Cela signifie que vous pouvez écrire votre code dans un simple impératif de style, et le résultat sera automatiquement recalculée chaque fois que des modifications de données que votre code dépend."

"Ce modèle simple (réactif de calcul + réactif de source de données) a un large champ d'application. Le programmeur est enregistré à partir de l'écrit/se désabonner réabonner appels et de s'assurer qu'ils sont appelés au bon moment, en éliminant toutes les classes de la propagation de données de code qui serait autrement boucher votre application avec le risque d'erreur de logique."

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X