Azure Native Qumulo ahora disponible en la UE, el Reino Unido y Canadá: Más información

Ingeniería en Qumulo: REST API

Escrito por:

Cuando comenzamos a construir nuestro sistema de archivos, sabíamos que queríamos poder controlar e inspeccionar el sistema de archivos con herramientas de línea de comandos, UI y pruebas automatizadas. Usando un REST API exponer esta funcionalidad fue un ajuste natural, y nos dimos cuenta de que si queríamos esta funcionalidad dentro de Qumulo, es probable que nuestros clientes también la deseen. Entonces, decidimos hacer pública nuestra API REST desde el principio.

En esta publicación, exploraremos los principios de nuestra API, los desafíos con REST y cómo evolucionamos continuamente la API junto con nuestro sistema de archivos.

Principios API REST

Representational State Transfer (REST) ​​es un estilo arquitectónico ampliamente utilizado con el que asumimos que ya está familiarizado. Al definir una nueva API usando REST, hay muchas opciones que tomar en el camino. Primero, debe decidir qué tipo de funcionalidad incluye su API. Plano de control (configuración del sistema y estadísticas)? Plano de datos (archivos y metadatos almacenados en el sistema de archivos)? Elegimos ambos, además de los puntos finales solo internos para ayudar al desarrollo de funciones. Todo lo que se puede hacer en un clúster de Qumulo se puede hacer a través de la API REST.

A continuación, consideramos el contenido de la respuesta. Cuando se utilizan protocolos del sistema de archivos como SMB o NFS para leer metadatos, se obtiene la interpretación de ese protocolo del estado del sistema de archivos, y puede estar limitado en lo que puede expresar. Nuestra API REST, por el contrario, devuelve la verdad fundamental: los clientes no necesitan interpretar los datos devueltos y nosotros devolvemos toda la información disponible. A medida que ampliamos las capacidades del sistema de archivos (como almacenar metadatos agregados), aumentamos nuestros puntos finales para exponer estas capacidades.

Inspirados en el libro de reglas de diseño de API REST, clasificamos cada uno de nuestros puntos finales como uno de estos arquetipos de recursos:

  • Documento: un registro de base de datos, con ambos campos y enlaces a recursos relacionados
  • Colecciones: un directorio de recursos (generalmente recursos de documentos)
  • Joystick: una función ejecutable (como "enviar correo")
  • Tienda: un repositorio de recursos administrado por el cliente (generalmente no se utiliza en nuestra API)

Al estructurar nuestros URI, quisimos simplificar la tarea de los desarrolladores o administradores de hacer una secuencia de comandos en nuestros puntos finales o utilizar herramientas como cURL. También queríamos asegurarnos de que los clientes no se rompan accidentalmente si se modifica el contrato de un punto final. Esto nos llevó a poner más contenido en el URI, como el número de versión, favoreciendo los contratos explícitos sobre los implícitos. Por ejemplo, aquí está cómo leer un directorio:

/ v1 / archivos / % 2F / entradas / ? limit = 1000

/ v1: la versión del punto final siempre viene primero
/ archivos /: el documento, la colección, o el controlador para acceder. En este caso, / files / es una colección.
% 2F: el id de un documento en la colección de archivos. En este caso, el directorio raíz del sistema de archivos.
/ entradas /: la acción a tomar en el archivo / carpeta especificada.
? limit = 1000: finalmente, los parámetros de consulta opcionales a la acción.

Con ese diseño, el único encabezado HTTP que requerimos es Autorización para tokens de portador de estilo OAuth2:

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

Vale la pena señalar que no intentamos imitar las API REST del sistema de archivos existentes. Queríamos que nuestra API fuera específica de las capacidades de nuestro sistema de archivos y le diera al usuario el máximo control sobre el sistema. Si en algún momento en el futuro queremos apoyar a los clientes que hablan S3, WebDAV o lo que sea, agregaremos nuevos puertos para esos protocolos, manteniéndolos separados de nuestra API REST principal.

Desafíos con REST

Muchos de nuestros puntos finales de configuración tienen un comportamiento sencillo: usted usa 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.

Pero REST no es ni estatal ni transaccional, lo que puede afectar la experiencia del usuario si no se considera correctamente. Digamos que un administrador está editando un recurso compartido de archivos en el clúster utilizando la interfaz de usuario integrada. Entre el momento en que la IU recupera los detalles del recurso compartido de archivos y cuando el administrador guarda sus cambios, otro usuario o proceso podría cambiar ese recurso compartido de archivos. De forma predeterminada, en nuestra API, el último escritor gana, por lo que el administrador podría rechazar estos cambios de manera involuntaria. Esa no es la experiencia de usuario que queremos, así que usamos 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.

Las acciones de larga duración también requieren una consideración especial. Para mantener predecibles los tiempos de respuesta de nuestra API REST, procesamos las solicitudes de ejecución corta de forma síncrona y las solicitudes de ejecución prolongada de forma asíncrona. Clasificamos cada punto final en nuestra API como de corto o largo plazo, para que los clientes sepan qué tipo de respuesta necesitan manejar para reducir la complejidad. Todos 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:

SOLICITUD POST / v1 / ad / join
Solicitar cuerpo {
"Dominio": "ad.server.com",
"Usuario": "dominio-usuario",
"Contraseña": "contraseña de dominio",
...
}

Si la solicitud es válida, el controlador responde:

 

SOLICITUD POST / v1 / ad / join
202 aceptado {
"Monitor_uri": "/ v1 / ad / monitor"
}

El cliente puede emitir repetidamente GET /v1/ad/monitor</var/www/wordpress> calls while waiting for the join action to succeed or fail.

Evolución API REST

Para garantizar que nuestra API REST siga el ritmo de las capacidades de nuestro sistema de archivos, los puntos finales se generan automáticamente a partir del código. Esto significa que el sistema de archivos, la API y la documentación de la API siempre están sincronizados. Nuestro sistema de compilación nos impide realizar cambios accidentalmente en las estructuras de datos internas que darían lugar a cambios en la API REST que rompen a los clientes API. Y al poner nuestra documentación API en el código, se mantiene actualizado con el código.

Dos años después del desarrollo de nuestra API REST, nos dimos cuenta de que teníamos un problema: la API había crecido orgánicamente a medida que diferentes equipos de desarrollo agregaban funcionalidad, lo que generaba inconsistencias entre los puntos finales y una jerarquía cuestionable que dificultaba el descubrimiento de la funcionalidad. Para abordar esto, hicimos dos cosas: migramos a un nuevo espacio de nombres de API a través de una serie de lanzamientos para solucionar problemas de consistencia y descubrimiento, y creamos una hoja de ruta de API para que los ingenieros de Qumulo la sigan y que permita que la API evolucione y se mantenga consistente. Un ejemplo de una mejora del espacio de nombres de la API fue la consolidación de todos analítica en tiempo real-funcionalidad relacionada en / v1 / analytics. Anteriormente, esta funcionalidad estaba dispersa por todo el espacio de nombres, y cuando escuchamos de los clientes que no podían encontrar estas características, supimos que esta era un área para mejorar.

Ahora que hemos consolidado nuestra API / v1, los puntos finales individuales pueden cambiar la versión si se necesita un cambio importante. (Los cambios de última hora incluyen cosas como agregar nuevos campos obligatorios a las solicitudes o cambiar la semántica de los datos que devolvemos). Incluso con esta disposición, los cambios de última hora son un último recurso. Nos esforzamos por encontrar formas de aumentar los datos de respuesta o introducir campos opcionales sin afectar a los clientes API existentes.

En esta publicación, exploramos los principios de la API REST de Qumulo, cómo abordamos algunos desafíos con REST y nuestro enfoque para evolucionar la API junto con el producto.

Artículos Relacionados

Ir al Inicio