181 votes

Modèles pour gérer les opérations de traitement par lots dans le reste des services web ?

Ce que le design éprouvé des modèles existent pour les opérations de traitement sur les ressources à l'intérieur d'un REPOS de style web service?

J'essaye de faire établir un équilibre entre les idéaux et la réalité en termes de performance et de stabilité. Nous avons une API en ce moment où toutes les opérations de récupérer à partir d'une liste de ressources (c'est à dire: GET /utilisateur) ou sur une seule instance (METTRE /user/1, SUPPRIMER /utilisateur/22, etc).

Il y a certains cas où vous souhaitez mettre à jour un champ unique de tout un ensemble d'objets. Il semble très inutile d'envoyer l'intégralité de la représentation de chaque objet et en arrière pour mettre à jour les champs.

Dans un RPC API de style, vous pouvez disposer d'une méthode:

/mail.do?method=markAsRead&messageIds=1,2,3,4... etc.

Quel est le REPOS équivalent ici? Ou est-ce ok de compromis maintenant et puis. Est-il ruine la conception d'ajouter en quelques opérations spécifiques où il améliore vraiment la performance, etc? Le client dans tous les cas, maintenant est d'un Navigateur Web (javascript de l'application côté client).

82voto

Alex Points 1864

Un simple Réparateur de modèle pour le traitement par lots est de rendre l'utilisation d'une collection de ressources. Par exemple, pour supprimer plusieurs messages à la fois.

DELETE /mail?&id=0&id=1&id=2

C'est un peu plus compliqué pour lot de mise à jour partielle des ressources, ou les attributs des ressources. Qui est mise à jour chaque markedAsRead attribut. En gros, au lieu de traitement de l'attribut dans le cadre de chaque ressource, vous le traitez comme un seau dans lequel de mettre les ressources. Un exemple a déjà été posté. J'ai ajusté un peu.

POST /mail?markAsRead=true
POSTDATA: ids=[0,1,2]

Fondamentalement, vous êtes à la mise à jour de la liste de mail marqué comme lu.

Vous pouvez également l'utiliser pour l'attribution de plusieurs éléments de la même catégorie.

POST /mail?category=junk
POSTDATA: ids=[0,1,2]

C'est évidemment beaucoup plus compliqué de le faire dans le style d'iTunes lot de mises à jour partielles (par exemple, l'artiste+albumTitle mais pas trackTitle). Le seau analogie commence à se décomposer.

POST /mail?markAsRead=true&category=junk
POSTDATA: ids=[0,1,2]

Dans le long terme, il est beaucoup plus facile de mettre à jour un seul partielle de ressources, ou les attributs des ressources. Il suffit de l'utilisation d'un sous-ressource.

POST /mail/0/markAsRead
POSTDATA: true

Sinon, vous pouvez utiliser paramétrée ressources. C'est moins fréquent dans le RESTE des modèles, mais est autorisé dans l'URI et HTTP specs. Un point-virgule divise horizontalement paramètres liés au sein d'une ressource.

Mise à jour de plusieurs attributs, plusieurs ressources:

POST /mail/0;1;2/markAsRead;category
POSTDATA: markAsRead=true,category=junk

Mise à jour de plusieurs ressources, à seulement un attribut:

POST /mail/0;1;2/markAsRead
POSTDATA: true

Mise à jour de plusieurs attributs, une ressource:

POST /mail/0/markAsRead;category
POSTDATA: markAsRead=true,category=junk

Le repos de la créativité abonde.

25voto

Christian Nunciato Points 8461

Pas du tout, je pense, le RESTE est équivalent (ou au moins une solution est) presque exactement cela: une interface spécialisée conçu accueillir un fonctionnement requis par le client.

Je me souviens d'un motif mentionné dans la Grue et de la Pascarello du livre Ajax en Action (un excellent livre, par ailleurs, fortement recommandé) dont ils illustrent la mise en œuvre d'un CommandQueue genre d'objet dont le travail consiste à faire la queue des requêtes en lots et de les afficher sur le serveur régulièrement.

L'objet, si je me souviens bien, essentiellement simplement tenue d'un tableau de "commandes" - par exemple, pour étendre votre exemple, chacun d'un enregistrement contenant une "markAsRead de la commande", un "messageId" et peut-être une référence à une fonction de rappel/fonction de gestionnaire d' -- et puis, selon certains horaire, ou sur une action de l'utilisateur, de la commande objet est sérialisé et publiée sur le serveur, et le client serait en mesure de gérer la conséquence de post-traitement.

Je n'arrive pas à avoir les détails à portée de main, mais elle sonne comme une file d'attente de commandes de ce type serait une façon de gérer votre problème; il faudrait réduire l'ensemble des discussions de façon importante, et il avait résumé le côté serveur de l'interface dans une manière que vous pourriez trouver plus souple en bas de la route.


Mise À Jour: Aha! J'ai trouvé une capture à partir de ce livre en ligne, complète avec des exemples de code (bien que j'ai toujours penser à ramasser le livre!). Jetez un oeil ici, en commençant par la section 5.5.3:

C'est facile à coder, mais peuvent entraîner des un grand nombre de très petits morceaux de trafic à le serveur, qui est inefficace et potentiellement source de confusion. Si nous voulons le contrôle de notre trafic, nous pouvons capturer ces mises à jour et de la file d'attente localement et puis les envoyer au serveur les lots à loisir. Un simple mise à jour de la file d'attente implémenté en JavaScript est indiqué dans le listing 5.13. [...]

La file d'attente s'est doté de deux tableaux. queued est un tableau indexé numériquement, à laquelle de nouvelles mises à jour sont ajoutées. sent est un tableau associatif contenant ces mises à jour qui ont été envoyés à le serveur mais qui sont en attente d'une réponse.

Voici deux fonctions pertinentes -- un responsable de l'ajout de commandes à la file d'attente (addCommand), et un responsable de la sérialisation, puis de les envoyer au serveur (fireRequest):

CommandQueue.prototype.addCommand = function(command)
{ 
    if (this.isCommand(command))
    {
    	this.queue.append(command,true);
    }
}

CommandQueue.prototype.fireRequest = function()
{
    if (this.queued.length == 0)
    { 
    	return;	
    }

    var data="data=";

    for (var i = 0; i < this.queued.length; i++)
    { 
    	var cmd = this.queued[i]; 
    	if (this.isCommand(cmd))
    	{
    		data += cmd.toRequestString(); 
    		this.sent[cmd.id] = cmd;

            // ... and then send the contents of data in a POST request
    	}
    }
}

Qui devrait vous aller. Bonne chance!

20voto

codepadawan Points 101

Alors que je pense que @Alex est sur le bon chemin, sur le plan conceptuel, je pense que ça devrait être l'inverse de ce qui est suggéré.

L'URL est en effet "les ressources qui nous sont ciblage" d'où:

    [GET] mail/1

les moyens d'obtenir l'enregistrement à partir de mail avec l'id 1 et

    [PATCH] mail/1

moyens patch le mail d'enregistrement avec l'id 1. La chaîne de requête est un "filtre", de la filtrer les données renvoyées par l'URL.

    [GET] mail?markAsRead=true

Nous voici donc de demander à tous le courrier déjà marqué comme lu. Donc sur [PATCH] pour ce chemin est-à-dire "mettre à jour les enregistrements déjà marqué comme vrai"... ce qui n'est pas ce que l'on cherche à atteindre.

Ainsi, une méthode de traitement par lots, à la suite de cette réflexion devrait être:

    mail/?id=1,2,3 <the records we are targeting>
    [PATCH] mail[markAsRead]=true

bien sûr, je ne dis pas que c'est vrai REPOS (qui ne marche pas permis de dossiers de lots de manipulation), plutôt elle suit la logique déjà existants et en cours d'utilisation par le REPOS.

1voto

Roberto Points 373

Super article. Je cherche une solution depuis quelques jours. J'ai proposé une solution consistant à transmettre une chaîne de requête avec un identifiant de groupe séparé par des virgules, comme:

 DELETE /my/uri/to/delete?id=1,2,3,4,5
 

... puis en le transmettant à une clause WHERE IN dans mon SQL. Cela fonctionne très bien, mais je me demande ce que les autres pensent de cette approche.

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