Comment concevoir une application web ajax multi-utilisateurs pour être simultanément en toute sécurité

StackOverflow https://stackoverflow.com/questions/4815226

Question

J'ai une page web qui montre une grande quantité de données à partir du serveur. La communication se fait via ajax.

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

Si l'utilisateur accède à la page B en même temps et crée un nouveau 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.

La page de On A, nous avons les données avec un objet renommé. Et sur la page de B nous avons les données avec un nouvel objet. Sur le serveur les données a à 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 utilisent simultanément?

Ces options de verrouillage comme la page entière ou le dumping tout l'État à l'utilisateur sur tous les changements sont plutôt à éviter.

Si elle aide, dans cet exemple spécifique de la page Web appelle une webmethod de statique qui exécute une procédure stockée sur la base de données. La procédure stockée renvoie toutes les données qu'il a changé et pas plus. La statique WEBMETHOD transmet ensuite le retour de la procédure stockée au client.

Bounty Edit:

Comment concevoir une application web multi-utilisateur qui utilise Ajax pour communiquer avec le serveur, mais évite les problèmes avec la concurrence?

i.e.. l'accès simultané à la fonctionnalité et aux données sur une base de données sans risque de données ou la corruption de l'État

Était-ce utile?

La solution

Vue d'ensemble:

  • Intro
  • architecture serveur
  • architecture client
  • Mise à jour le cas
  • Engagez cas
  • cas de conflit
  • Performances et évolutivité

Hi Raynos,

Je ne discuterai pas un produit particulier ici. Ce que les autres mentionnés est un bon jeu d'outils pour jeter un oeil à déjà (peut-être ajouter à cette liste Node.js).

Du point de vue architectural, vous semblez avoir le même problème que l'on peut voir dans le logiciel de contrôle de version. Un utilisateur vérifie dans un changement à un objet, un autre utilisateur veut modifier le même objet d'une autre manière => conflit. Vous devez intégrer les utilisateurs des modifications à des objets tout en étant capable de mises à jour en temps opportun et de fournir efficacement, la détection et la résolution des conflits comme celui ci-dessus.

Si j'étais dans vos chaussures, je développerait quelque chose comme ceci:

1. Côté serveur:

  • Déterminer un niveau raisonnable au cours de laquelle vous définir ce que je qualifierais « artefacts atomiques » (la page? Objets sur la page? Les valeurs à l'intérieur des objets?). Cela dépendra de vos serveurs Web, le matériel de base de données et la mise en cache, # d'utilisateur, # d'objets, etc. Pas une décision facile à prendre.

  • Pour chaque artefact atomique ont:

    • un identificateur unique à l'échelle d'application
    • une incrémenter 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'une ringbuffer (mémoire partagée fonctionne bien pour ceux-ci). Une seule paire valeur clé est peut-être bien aussi mais moins extensible. voir http://en.wikipedia.org/wiki/Circular_buffer
  • Un serveur ou d'un composant pseudo-serveur qui est en mesure de fournir pertinents changelogs à un utilisateur connecté efficacement. Observer-directivité est votre ami pour cela.

2. Côté client:

  • Un client javascript qui est capable d'avoir une longue durée HTTP connexion audit serveur ci-dessus, ou utilise polling léger.

  • Un javascript composant artefact-updater qui rafraîchit les sites de contenu lorsque le client notifie javascript connectés des changements dans les artefacts histoire surveillés. (Encore une fois un modèle d'observation pourrait être un bon choix)

  • Un javascript composant artefact committers qui peut demander de changer un artefact atomique, en essayant d'acquérir mutex. Il détecte si l'état de l'artefact a été modifié par un autre utilisateur quelques secondes avant (latancy du client javascript et engager des facteurs de processus dans) en comparant connu clientside artefact-version-id et Serverside actuelle artefact version id.

  • Un javascript résolveur de conflit permettant un humain-changement est-la-bonne décision. Vous voudrez peut-être pas simplement dire à l'utilisateur « Quelqu'un a été plus rapide que vous. Je supprimé votre changement. Allez pleurer. ». De nombreuses options de diffs plutôt techniques ou des solutions plus conviviales semblent possibles.

Alors, comment serait-il rouler ...

Cas 1: type-de-séquence-diagramme pour la mise à jour:

  • navigateur rend la page
  • javascript "voit" artefacts ayant chacun au moins un champ de valeur, et une version unique--id
  • client javascript est commencé, demandant de « montre » l'histoire des artefacts trouvés à partir de leurs versions trouvées (anciennes modifications ne sont pas intéressants)
  • processus serveur prend note des demandes et en continu des contrôles et / ou envoie l'histoire
  • Histoire des entrées peut contenir des notifications simples « x artefact a changé, pls clients demandent des données » permettant au client de sondage indépendamment ou ensembles complets de données « artefact x a changé à la valeur foo »
  • javascript artefact-Updater fait ce qu'il peut pour aller chercher de nouvelles valeurs dès qu'ils deviennent connus pour avoir mis à jour. Il exécute les nouvelles demandes de ajax ou se feeded par le client javascript.
  • Les pages DOM contenu est mis à jour, l'utilisateur est informé en option. continue l'observation de l'histoire.

Cas n ° 2: Maintenant, pour avoir commis:

  • artefact committers connaît la nouvelle valeur souhaitée de l'entrée d'utilisateur et envoie un changement demande au serveur
  • Serverside mutex est acquis
  • Server reçoit "Hey, je sais que l'artefact x l'état de la version 123, me laisser prendre à la valeur pls foo".
  • Si la version de l'artefact Serverside x est égal (ne peut pas être inférieure à) 123 la nouvelle valeur est acceptée, un nouvel identifiant de version de 124 généré.
  • Le nouvel état-information « mis à jour à la version 124 » et éventuellement nouvelle valeur foo sont mis au début de ringbuffer de l'artefact x (changelog / histoire)
  • Serverside mutex est libéré
  • demande committers de artefact est heureux de recevoir un ensemble avec le nouveau id commit-confirmation.
  • Pendant ce temps, composant serveur Serverside conserve polling / pousser les ringbuffers aux clients connectés. Tous les clients qui regardent le tampon de x artefact obtenir les nouvelles informations et la valeur de leur état dans les temps d'attente habituelle (voir le cas 1).

Cas n ° 3: pour les conflits:

  • artefact committers nouvelle valeur souhaitée connaît de l'entrée d'utilisateur et envoie un changement demande au serveur
  • en attendant un autre utilisateur mis à jour le même artefact (voir cas 2.) avec succès, mais en raison de divers latences cela est encore inconnu à notre autre utilisateur.
  • Ainsi, un mutex Serverside est acquis (ou attendu jusqu'à ce que l'utilisateur « plus vite » commis son changement)
  • Server reçoit "Hey, je sais que l'état d'artefact x de la version 123, laissez-moi il mis à la valeur foo".
  • Sur la Serverside la version d'artefact x 124 est maintenant déjà. Le client demandeur ne peut pas connaître la valeur qu'il remplacerez.
  • Il est évident que le serveur doit rejeter la demande de changement (sans compter dans les priorités Ecraser Dieu intervenant), libère le mutex et est assez bon pour renvoyer le nouveau-id version et nouvelle valeur directement au client.
  • confronté à un rejet demande de validation et d'une valeur à l'utilisateur de changement demande ne savait pas encore, l'artefact javascript committers fait référence au résolveur des conflits qui affiche et explique la question à l'utilisateur.
  • L'utilisateur, présenté avec quelques options par le conflit résolveur intelligent JS, est autorisé une nouvelle tentative de changer la valeur.
  • Une fois que l'utilisateur a sélectionné une valeur qu'il juge juste, le processus commence au-dessus de cas 2 (ou 3 cas si quelqu'un d'autre était plus rapide, encore une fois)

Quelques mots sur la performance et Evolutivité

HTTP interrogation par rapport à HTTP "pousser"

  • Polling crée des demandes, un par seconde, 5 par seconde, tout ce que vous considérez comme une latence acceptable. Cela peut être assez cruel pour votre infrastructure si vous ne configurez pas votre (Apache?) Et (php?) Assez bien pour être entrées « légers ». Il est souhaitable d'optimiser la demande de vote sur la Serverside pour qu'il fonctionne pour beaucoup moins de temps que la durée de l'intervalle d'interrogation. Fractionnement que l'exécution dans la moitié pourrait bien vouloir dire abaisser l'ensemble de votre charge du système jusqu'à 50%,
  • Pushing via HTTP (en supposant webworkers sont trop loin pour les soutenir) vous obligera à avoir un processus apache / lighthttpd disponible pour chaque utilisateur tout le temps . La mémoire résidente réservée à chacun de ces processus et vos systèmes mémoire totale sera très certaine limite de mise à l'échelle que vous rencontrerez. Réduire l'empreinte mémoire de la connexion sera nécessaire, ainsi que de limiter la quantité CPU continue et le travail d'E / S fait dans chacun de ces (vous voulez beaucoup de sommeil / temps d'inactivité)

mise à l'échelle backend

  • Oubliez la base de données et système de fichiers, vous allez besoin une sorte de back-end à base de mémoire partagée pour le vote fréquent (si le client n'interroge pas directement alors chaque processus serveur en cours d'exécution sera)
  • si vous allez pour memcache vous pouvez faire évoluer mieux, mais il est encore cher
  • Le mutex doit commits pourtravailler globaly même si vous voulez avoir plusieurs serveurs frontend à loadbalance.

frontend mise à l'échelle

  • peu importe si vous relevez ou recevoir « pousse », essayez d'obtenir des informations pour tous les artefacts surveillés en une seule étape.

tweaks "créatif"

  • Si les clients sont sondages et de nombreux utilisateurs ont tendance à regarder les mêmes objets, vous pouvez essayer de publier l'histoire de ces artefacts dans un fichier statique, ce qui permet apache à mettre en cache, en rafraichissant néanmoins sur le Serverside lors du changement des artefacts. Cela prend PHP / memcache hors du jeu pour certaines demandes. Lighthttpd est verry efficent au service de fichiers statiques.
  • utiliser un réseau de distribution de contenu comme cotendo.com à l'histoire de l'artefact poussée là-bas. Le push-temps d'attente sera plus grande, mais l'évolutivité est un rêve
  • écrire un vrai serveur (ne pas utiliser HTTP) que les utilisateurs se connectent à l'aide de java ou flash (?). Vous devez faire face à de nombreux utilisateurs au service dans un seul serveur-fil. À vélo à travers les prises ouvertes, faire (ou déléguer) les travaux nécessaires. Peut échelle via des processus ou bifurquer à partir de plusieurs serveurs. Les mutex doivent rester uniques globaly bien.
  • En fonction des scénarios de charge groupe vos serveurs back-end-frontend- et par des plages-id artefact. Cela permettra une meilleure utilisation de la mémoire persistante (aucune base de données a toutes les données) et permet à l'échelle du mutexing. Votre javascript doit maintenir des connexions à plusieurs serveurs en même temps cependant.

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

Christoph Strasen

Autres conseils

Je sais que c'est une vieille question, mais je pensais que je serais carillon juste.

OT (transformations opérationnelles) semblent comme un bon ajustement pour votre exigence simultanée et cohérente l'édition multi-utilisateur. Il est un utilisé dans Google Docs (et a également été utilisé dans Google Wave):

Il y a une bibliothèque en JS pour l'utilisation de Transforms opérationnels - ShareJS ( http://sharejs.org/ ) , écrit par un membre de l'équipe Google Wave.

Et si vous voulez, il y a un framework web complet MVC - DerbyJS ( http://derbyjs.com/) construit sur ShareJS qu'il fait tout pour vous.

Il utilise BrowserChannel pour la communication entre le serveur et les clients (et je crois que le soutien de WebSockets devrait être dans les œuvres - il était là auparavant par Socket.IO, mais il a été retiré en raison des problèmes avec le développeur Socket.io) Débutant docs sont rares bits au moment, cependant.

J'envisager d'ajouter timbre modifié en fonction du temps pour chaque ensemble de données. Donc, si vous êtes tables db mise à jour, vous devez modifier l'horodatage modifié en conséquence. Utilisation d'AJAX, vous pouvez comparer l'horodatage modifié du client avec l'horodatage de la source de données - si l'utilisateur est toujours 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 que vous tapez une réponse.

Vous devez utiliser des techniques de poussée (également connu sous le nom de Comet ou inverse Ajax) pour propager les modifications à l'utilisateur dès qu'ils sont faits à la DB. La meilleure technique actuellement disponible pour ce qui semble être Ajax à long sondage, mais il est pas pris en charge par tous les navigateurs, si vous avez besoin des solutions de repli. Heureusement, il existe déjà des solutions qui gèrent pour vous. Parmi eux: orbited.org et socket.io déjà mentionné

.

Dans l'avenir, il y aura un moyen plus facile de faire ce que l'on appelle WebSockets, mais il ne sait pas encore quand cette norme sera prête pour le moment où il y a des problèmes de sécurité au sujet de l'état actuel de la norme.

Il ne devrait pas y avoir des problèmes de concurrence dans la base de données avec de nouveaux objets. Mais lorsqu'un utilisateur édite un objet 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é la solution est, encore une fois, simple. Il suffit de jeter le modifier

Mais le plus problème apparaît difficile, lorsque plusieurs utilisateurs éditent le même objet en même temps. Si l'utilisateur 1 et 2 commencer à éditer un objet en même temps, ils seront tous deux leurs modifications sur les mêmes données. Disons que les changements utilisateur 1 fait sont envoyés au premier serveur tout utilisateur 2 est toujours en édition les données. Vous avez alors deux options: Vous pouvez essayer aux changements de fusion utilisateur 1 dans les données de l'utilisateur 2 ou vous pouvez dire utilisateur 2 que ses données sont à jour et l'afficher un message d'erreur dès que ses données s'envoyer au serveur. Ce dernier n'est pas option très conviviale, mais l'ancien est très difficile à mettre en œuvre.

L'une des rares réalisations qui ont vraiment obtenu ce droit pour la première fois était EtherPad , qui a été acquise par Google. Je crois qu'ils ont ensuite utilisé certaines des technologies de Etherpad dans Google Docs et Google Wave, mais je ne peux pas dire que sûr. Google a également opensourced EtherPad, alors peut-être que ça vaut un coup d'oeil, en fonction de ce que vous essayez de faire.

Il est vraiment pas facile à faire ce genre de choses éditer simultanément, car il est impossible de faire des opérations atomiques sur le web en raison de la latence. Peut-être cet article vous aidera à en apprendre davantage sur le sujet.

Essayer d'écrire tout cela vous-même est un gros travail, et il est très difficile de faire les choses. Une option est d'utiliser un cadre qui est conçu pour garder les clients en phase avec la base de données, et l'autre, en temps réel.

J'ai trouvé que le cadre Meteor le fait bien ( http://docs.meteor.com/# réactivité ).

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

"Ce modèle simple (calcul réactif + source de données réactive) est largement applicable. Le programmeur est sauvé de l'écriture de désabonnement / appels réabonnement et faire en sorte qu'ils sont appelés au bon moment, l'élimination des classes entières de code de propagation de données qui autrement encrasser votre application avec la logique sujette à erreur. "

Je ne peux pas croire que personne n'a mentionné Meteor . Il est un nouveau cadre immature pour sûr (et ne supporte officiellement un DB), mais il faut tous le travail grunt et de penser à une application multi-utilisateurs comme l'affiche décrit. En fait, vous ne pouvez pas construire une application pas en direct la mise à jour mult-utilisateur. Voici un résumé rapide:

  • Tout est dans Node.js (JavaScript ou CoffeeScript), afin que vous puissiez partager des choses comme le client entre les validations et le serveur.
  • Il utilise websockets, mais peut se replier pour les anciens navigateurs
  • Il met l'accent sur les mises à jour immédiates pour objet local (à savoir les UI Snappy sent), avec des modifications envoyées au serveur en arrière-plan. Seuls les mises à jour atomiques sont autorisés à faire des mises à jour de mélange plus simple. Les mises à jour rejetés sur le serveur sont annulées.
  • En prime, il gère le code en direct rechargements pour vous, et il conserve l'état d'utilisateur, même lorsque l'application change radicalement.

Meteor est assez simple que je vous suggère au moins jeter un oeil à trouver des idées pour voler.

Ces pages de Wikipedia peut aider à ajouter la perspective à l'apprentissage de et concurrency informatique concurrente pour la conception d'un href="https://en.wikipedia.org/wiki/Web_application" rel="nofollow"> application web soit < a href = "https://en.wikipedia.org/wiki/Pull_technology" rel = "nofollow"> tractions ou est a poussé l'état (< a href = "https://en.wikipedia.org/wiki/Event-driven_architecture" rel = "nofollow"> EDA ) messages dans modèle messagerie . En gros, les messages sont reproduits aux abonnés de canal qui répondent aux événements de modification et les demandes de synchronisation.

Il existe de nombreuses formes de sur le Web simultané href="https://en.wikipedia.org/wiki/List_of_collaborative_software".

Il y a un certain nombre de API HTTP bibliothèques clientes pour etherpad- Lite , collaboratif éditeur en temps réel .

django-temps réel-aire de jeux outils en temps réel une application dans le chat Django avec divers réel technologies de temps comme Socket.io .

Les deux AppEngine et AppScale mettre en œuvre l'API AppEngine canal ; qui est distincte de la API Google en temps réel , ce qui est démontré par Google Drive / realtime-terrain de jeu .

Les techniques

Push côté serveur sont de la voie à suivre ici. Comet est (ou était?) Un mot à la mode.

La direction particulière que vous prenez dépend fortement de votre pile de serveur, et comment vous flexible / est. Si vous le pouvez, je jeter un oeil à socket.io , qui fournit une implémentation multi-navigateur de websockets, qui fournissent d'une manière très Streamline d'avoir une communication bidirectionnelle avec le serveur, ce qui permet au serveur de pousser les mises à jour aux clients.

En particulier, voir cette démonstration par l'auteur de la bibliothèque, ce qui démontre la situation que vous décrivez presque exactement .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top