2017-09-18 8 views
1

Imaginez une requête qui démarre un processus en cours d'exécution dont la sortie est un grand ensemble d'enregistrements.Comment concevoir une API REST pour extraire un flux de données volumineux (éphémère)?

Nous pourrions commencer le processus avec une requête POST:

POST/api/v1/long calcul

La sortie se compose d'une grande séquence d'enregistrements numérotés, qui doivent être envoyées au client. Comme la sortie est grande, le serveur ne stocke pas tout et maintient ainsi une fenêtre d'enregistrements avec une limite supérieure sur la taille de la fenêtre. Disons qu'il stocke jusqu'à 1000 enregistrements (et met en pause le calcul chaque fois que ces nombreux enregistrements sont disponibles). Lorsque le client récupère des enregistrements, le serveur peut ensuite supprimer ces enregistrements et continuer ainsi à générer plus d'enregistrements (car plus d'emplacements dans la fenêtre 1000-longueur sont gratuits).

Disons que nous récupérons les enregistrements avec:

GET/api/v1/long calcul ack = 213

Nous pouvons prendre cela signifie que le serveur doit renvoyer les enregistrements à partir de index 214. Lorsque le serveur reçoit cette requête, il peut supposer que le client (well-behaved) reconnaît que les enregistrements jusqu'au numéro 213 sont reçus par le client et qu'il les supprime, puis renvoie les enregistrements commençant par le numéro 214 à est disponible à ce moment-là.

Suivant si le client demande:

GET/api/v1/long calcul ack = 214

le serveur supprimerait enregistrement 214 et renvoyer les enregistrements à partir de 215.

?

Cela semble être une conception raisonnable jusqu'à ce qu'il soit remarqué que les requêtes GET doivent être sûres et idempotentes (voir la section 9.1 du HTTP RFC).

Questions:

  1. Y at-il une meilleure façon de concevoir cette API?
  2. Est-il correct de le garder comme GET même s'il semble violer la norme?
  3. Serait-il raisonnable de faire une requête POST, comme:

    POST/api/v1/long calcul/tronquer-et chercher ack = 213

+1

2616 n'est plus à jour. Merci d'utiliser RFC 723x à la place. –

+0

Je suis avec @Evert et @EricStein que d'autres protocoles/styles sont probablement plus adaptés. Néanmoins, HTTP offre une solution de streaming soignée pour les données qui renvoie la même sortie pour chaque client - [requêtes GET partielles] (https://tools.ietf.org/html/rfc7233). Alors que la RFC explique plus ou moins les gammes d'octets, des gammes personnalisées comme 'Content-Range:" items 21-40/* "' sont également possibles, bien que vous ne receviez pas l'accusé de réception. Cela devrait être fait différemment. Notez que le serveur n'a pas besoin de stocker le résultat entier s'il est capable de calculer les mêmes segments de résultats pour chaque client –

+0

@RomanVottner Ranges fonctionnerait, mais l'API doit toujours gérer à la fois le cas où la plage demandée n'est pas encore remplie et comment supprimer en toute sécurité les enregistrements pour en créer davantage. –

Répondre

1

One question que je me sens toujours comme cela doit être demandé est, êtes-vous sûr que REST est la bonne approche pour ce problème? Je suis un grand fan et promoteur REST, mais essayez de ne s'appliquer qu'aux situations où c'est applicable. Cela étant dit, je ne pense pas qu'il y ait quelque chose qui ne va pas nécessairement avec l'expiration des ressources après leur utilisation, mais je pense que c'est une mauvaise conception de réutiliser la même URL encore et encore.

Au lieu de cela, quand je l'appelle la première série de résultats (peut-être avec):

GET /api/v1/long-computation 

Je pense que des ressources pour me donner un lien next avec la prochaine série de résultats.

Bien que cette conception d'URL particulière sorte de me dire il y a seulement 1 long-calcul sur l'ensemble du système qui se passe en même temps. Si ce n'est pas le cas, je m'attendrais aussi à un peu plus d'unicité dans le design de l'URL.

+0

Merci pour la réponse, je pense que c'est une solution plus pragmatique pour moi. Est-ce que l'utilisation de POST pour transmettre un enregistrement GET suivant + SUPPRIMER des enregistrements plus anciens est une mauvaise façon de concevoir cette API? – donatello

+0

@donatello Je ne pense pas que ce soit une très mauvaise façon de concevoir cette API, mais je pense qu'à ce stade, vous ne pouvez plus l'appeler REST. Je pense que c'est une conception raisonnable, et probablement comment beaucoup de gens conçoivent cela. C'est correct d'un point de vue HTTP – Evert

1

La meilleure solution ici est d'acheter un plus grand disque dur. Je suppose que vous avez repoussé et ce n'est pas dans les cartes.

Je considérerais que votre opération est "dangereuse" comme défini par RFC 7231, donc je suggère de ne pas utiliser GET. Je vous conseille fortement de ne pas supprimer les enregistrements du serveur sans que le client ne le demande explicitement. L'un des principes de REST est que le web n'est pas fiable. Sous votre conception, que se passe-t-il si une réponse ne parvient pas au client pour une raison quelconque? Si elles font une autre demande, tous les enregistrements de la réponse perdue seront détruits. Je vais appuyer la suggestion de @ Evert que vous devez absolument garder cette conception, vous choisissez plutôt une technologie qui est construite autour de la livraison fiable d'informations, comme une file d'attente de messagerie. Si vous préférez utiliser REST, vous devez autoriser les clients à vous indiquer quand supprimer les enregistrements en toute sécurité.

Par exemple, est-il possible de paginer des enregistrements? Vous pouvez faire quelque chose comme:

POST /long-running-operations?recordsPerPage=10 
202 Accepted 
Location: "/long-running-operations/12" 
{ 
    "status": "building next page", 
    "retry-after-seconds": 120 
} 

GET /long-running-operations/12 
200 OK 
{ 
    "status": "next page available", 
    "current-page": "/pages/123" 
} 
-- or -- 
GET /long-running-operations/12 
200 OK 
{ 
    "status": "building next page", 
    "retry-after-seconds": 120 
} 
-- or -- 
GET /long-running-operations/12 
200 OK 
{ 
    "status": "complete" 
} 

GET /pages/123 
{ 
    // a page of records 
} 

DELETE /pages/123 
// remove this page so new records can be made 

Vous aurez besoin de décapsuler taille de page au nombre d'enregistrements que vous soutenez. Si la demande du client est inférieure à cette limite, vous pouvez mettre en arrière-plan plusieurs enregistrements pendant qu'ils traitent la première page.

C'est juste un brouillon, mais peut-être que vous pouvez commencer là. Aucune promesse sur la qualité - c'est totalement hors de ma tête. Cette approche est un peu bavarde, mais elle vous évite de renvoyer un 404 si la nouvelle page n'est pas encore prête.

+0

Merci pour la réponse - utiliser POST comme mentionné vers la fin de ma question soit une norme sérieuse REST/violation RFC? – donatello

+0

@donatello Ça va pour le RFC. Le problème persiste si la requête passe mais que la réponse ne le fait pas, les données sont Gone and Not Recoverable à court de redémarrer tout le processus de longue durée. C'est une expérience utilisateur plutôt négative. –