Azure Native Qumulo Maintenant disponible dans l'UE, au Royaume-Uni et au Canada - En savoir plus

Ingénierie chez Qumulo: API REST

Rédigé par:

Lorsque nous avons commencé à créer notre système de fichiers, nous savions que nous voulions pouvoir contrôler et inspecter le système de fichiers avec des outils de ligne de commande, une interface utilisateur et des tests automatisés. Utilisant un API REST exposer cette fonctionnalité était une solution naturelle, et nous avons réalisé que si nous voulions cette fonctionnalité dans Qumulo, nos clients le voudraient probablement aussi. Nous avons donc choisi de rendre notre API REST publique dès le début.

Dans cet article, nous explorerons les principes de notre API, les défis avec REST et la manière dont nous évoluons continuellement avec notre système de fichiers.

Les principes de l'API REST

Le transfert d'état représentatif (REST) ​​est un style architectural largement utilisé que nous supposons que vous connaissez déjà. Lors de la définition d'une nouvelle API à l'aide de REST, il y a de nombreux choix à faire en cours de route. Tout d'abord, vous devez décider du type de fonctionnalité à intégrer à votre API. Plan de contrôle (configuration du système et statistiques) ? Plan de données (fichiers et métadonnées stockés dans le système de fichiers) ? Nous avons choisi les deux, ainsi que des points de terminaison internes uniquement pour aider au développement des fonctionnalités. Tout ce qui peut être fait sur un cluster Qumulo peut être fait via l'API REST.

Ensuite, nous avons considéré le contenu de la réponse. Lorsque vous utilisez des protocoles de système de fichiers comme SMB ou NFS pour lire des métadonnées, vous obtenez l'interprétation de l'état du système de fichiers par ce protocole, et il peut être limité dans ce qu'il peut exprimer. Notre API REST, en revanche, renvoie une vérité terrain - les clients n'ont pas besoin d'interpréter les données renvoyées et nous renvoyons toutes les informations disponibles. Au fur et à mesure que nous étendons les capacités du système de fichiers (comme le stockage de métadonnées agrégées), nous augmentons nos points de terminaison pour exposer ces capacités.

Inspiré du livret de règles de conception de l'API REST, nous classons chacun de nos points de terminaison comme l'un de ces archétypes de ressources:

  • Documents: un enregistrement de base de données, avec les deux champs et des liens vers des ressources connexes
  • Collection: un répertoire de ressources (généralement des ressources documentaires)
  • : une fonction exécutable (comme "send-mail")
  • Agence: un référentiel de ressources géré par le client (généralement non utilisé dans notre API)

En structurant nos URI, nous voulions simplifier le script pour les développeurs ou les administrateurs par rapport à nos points de terminaison ou utiliser des outils comme cURL. Nous voulions également nous assurer que les clients ne se cassent pas accidentellement si le contrat d'un terminal change. Cela nous a conduit à mettre plus de contenu dans l'URI, comme le numéro de version, en privilégiant les contrats explicites par rapport aux contrats implicites. Par exemple, voici comment lire un répertoire:

/ v1 /des dossiers/ % 2F / entrées / ? limit = 1000

/ v1: la version du noeud final vient toujours en premier
/des dossiers/: le document, la collection ou le contrôleur auquel accéder. Dans ce cas, / files / est une collection.
% 2F: l'id d'un document dans la collection de fichiers. Dans ce cas, le répertoire racine du système de fichiers.
/ entrées /: l'action à effectuer sur le fichier / dossier spécifié.
? limit = 1000: enfin, paramètres de requête optionnels pour l'action.

Avec cette conception, le seul en-tête HTTP requis est l’autorisation des jetons de support de style OAuth2:

curl -k -X GET -H "Authorization: Bearer <token>" https://server:8000/v1/file-system</var/www/wordpress>

Il convient de noter que nous n'avons pas essayé d'imiter les API REST de système de fichiers existantes. Nous voulions que notre API soit spécifique aux capacités de notre système de fichiers et donne à l'utilisateur un contrôle maximal sur le système. Si, à un moment donné, nous souhaitons prendre en charge des clients qui parlent S3, WebDAV ou autre, nous ajouterons de nouveaux ports pour ces protocoles, en les séparant de notre API REST.

Défis avec REST

Bon nombre de nos points de terminaison de configuration ont un comportement simple: vous utilisez GET</var/www/wordpress> to retrieve a document (e.g. GET /v1/users/123</var/www/wordpress>), and you use SET</var/www/wordpress> or PATCH</var/www/wordpress> to update the document. The requests take effect immediately, so that when you receive a 200 OK</var/www/wordpress> response, you know the change has been made.

Mais REST n'est ni dynamique ni transactionnel, ce qui peut avoir un impact sur l'expérience utilisateur s'il n'est pas pris en compte correctement. Supposons qu'un administrateur modifie un partage de fichiers sur le cluster à l'aide de l'interface utilisateur intégrée. Entre le moment où l'interface utilisateur récupère les détails du partage de fichiers et lorsque l'administrateur enregistre les modifications, un autre utilisateur ou processus peut modifier ce partage de fichiers. Par défaut, dans notre API, le dernier écrivain gagne, de sorte que l’administrateur bombarderait involontairement ces modifications. Ce n'est pas l'expérience utilisateur que nous voulons, nous utilisons donc ETag</var/www/wordpress> and If-Match</var/www/wordpress> HTTP headers for all of our documents to prevent accidental overwrites. When the UI retrieves a document, it reads the ETag</var/www/wordpress> response header (entity tag, or essentially a hashcode) and stores that. Later, when updating that same document, the UI sends an If-Match</var/www/wordpress> request header, which tells the cluster to only perform the action if the document is the same as we expect. If the document changed, we’ll get back a 12 Precondition Failed</var/www/wordpress> response, which allows us to build a better experience for the user.

Les actions de longue durée nécessitent également une attention particulière. Pour que nos temps de réponse de l'API REST soient prévisibles, nous traitons les requêtes à exécution courte de manière synchrone et les requêtes à exécution longue de manière asynchrone. Nous classons chaque point de terminaison dans notre API comme court ou long, afin que les clients sachent quel type de réponse ils doivent gérer pour réduire la complexité. Tout GET, PUT</var/www/wordpress>, and PATCH</var/www/wordpress> operations on documents and collections are short-running requests, returning 200 OK</var/www/wordpress> when successfully processed. In contrast, we always POST to a controller endpoint for long-running requests, which return 202 Accepted</var/www/wordpress> with a URI to poll for completion status. For example, when joining a cluster to Active Directory, the client invokes the controller like this:

Demande POST / v1 / ad / join
Demander un corps {
"Domaine": "ad.server.com",
"Utilisateur": "utilisateur du domaine",
«Password»: «domain-password»,
...
}

Si la demande est valide, le contrôleur répond:

 

Demande POST / v1 / ad / join
202 Accepté {
"Monitor_uri": "/ v1 / ad / monitor"
}

Le client peut alors émettre plusieurs fois GET /v1/ad/monitor</var/www/wordpress> calls while waiting for the join action to succeed or fail.

Evolution de l'API REST

Pour que notre API REST reste en phase avec les capacités de notre système de fichiers, les ordinateurs d'extrémité sont générés automatiquement à partir de code. Cela signifie que le système de fichiers, l'API et la documentation de l'API sont toujours synchronisés. Notre système de construction nous empêche d’apporter des modifications accidentelles aux structures de données internes qui pourraient entraîner des modifications de l’API REST qui briseraient les clients de l’API. Et en mettant la documentation de notre API dans le code, celle-ci reste à jour.

Deux ans après le développement de notre API REST, nous avons réalisé que nous avions un problème : l'API s'était développée de manière organique à mesure que différentes équipes de développement ajoutaient des fonctionnalités, ce qui entraînait des incohérences entre les points de terminaison et une hiérarchie douteuse qui rendait difficile la découverte des fonctionnalités. Pour résoudre ce problème, nous avons fait deux choses : nous avons migré vers un nouvel espace de noms d'API sur une série de versions pour résoudre les problèmes de cohérence et de découvrabilité, et nous avons créé une feuille de route d'API à suivre pour les ingénieurs Qumulo qui permet à l'API d'évoluer et de rester cohérente. Un exemple d'amélioration de l'espace de noms d'API était la consolidation de tous les analyse en temps réel-fonctionnalité liée sous /v1/analytics. Auparavant, cette fonctionnalité était dispersée dans tout l'espace de noms, et lorsque nous avons entendu des clients qu'ils ne pouvaient pas trouver ces fonctionnalités, nous savions qu'il s'agissait d'un domaine à améliorer.

Maintenant que nous avons solidifié notre API / v1, les systèmes d'extrémité individuels peuvent changer de version si un changement est nécessaire. (La rupture des modifications inclut des éléments tels que l'ajout de nouveaux champs obligatoires aux demandes ou la modification de la sémantique des données renvoyées.) Même avec cette disposition, les modifications majeures sont un dernier recours. Nous nous efforçons de trouver des moyens d'augmenter les données de réponse ou d'introduire des champs facultatifs sans impact sur les clients API existants.

Dans cet article, nous avons exploré les principes de l’API REST de Qumulo, comment nous avons relevé certains défis avec REST et notre approche pour faire évoluer l’API avec le produit.

Articles Similaires

Remonter en haut